我有一个表示我的算法的参数选项的类。创建对象会将所有选项初始化为某些预定义值。然后,我可以通过简单地为相应的属性分配不同的值来更改参数:
opts = AlgOptions()
opts.mutation_strength = 0.2
但是,我有时会在键入属性名称时出错,例如:
opts = AlgOptions()
opts.mutation_str = 0.2
Python 只是创建一个不同的属性并继续运行,产生难以调试的意外结果。
有没有一种优雅的方法来避免这种潜在的陷阱?与 C# 等语言相比,总是仔细检查所有属性名称感觉很乏味,C# 等语言会将这种情况解释为错误。我想到的事情很少,但有一些警告:
1(我可以为参数制作setter方法,但这似乎是很多样板代码。这样,如果我尝试调用名称错误的方法,我将得到一个有意义的 excepion。
2(我可以创建类内所有属性的列表,并检查是否有任何类属性的名称不在列表中。但是,我不确定何时何地执行检查,无论如何这似乎是一个笨拙的解决方案。
你可以通过使用插槽来实现这一点,这基本上可以防止创建未在__init__
中声明的属性:
>>> class Foo:
... __slots__ = ["bar", "baz"]
... def __init__(self, bar, baz):
... self.bar = bar
... self.baz = baz
...
>>> foo = Foo(1, 2)
>>> foo.asdf = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'asdf'
>>> foo.bar
1
>>> foo.bar += 5
>>> foo.bar
6
请注意,使用 __slots__
有一些文档中讨论的缺点。
所以一般来说,最好避免打错字;)但这里有一个(有点黑客(解决方案。基本上是子类NoSetAttr
类,并使用 set_new_attr
方法在类中设置新属性。
class NoSetAttr:
def __setattr__(self, attr, value):
getattr(self, attr) # will raise error if not found
super().__setattr__(attr, value)
def set_new_attr(self, attr, value):
super().__setattr__(attr, value)
class ExampleClass(NoSetAttr):
def __init__(self, x):
self.set_new_attr('x', x)
inst = ExampleClass(12)
inst.x # 12
inst.x = 23
inst.x # 23
inst.y = 2 # raises attribute error
您可以将基类转换为类装饰器@no_set_attr
然后对类进行注释,这可能更容易。
以下是仅允许设置某些属性的可能方法:
class AlgOptions():
def __str__(self):
return str(self.__dict__)
def __setattr__(self, k, v):
allowed = ('mutation_strength',)
if k in allowed:
self.__dict__[k] = v
else:
raise AttributeError(f"attribute {k} not allowed")
opts = AlgOptions()
opts.mutation_strength = 0.2
try:
opts.mutation_str = 0.2
except Exception as e:
print('Sorry about this:', e)
print(opts)
就个人而言,我永远不会在我的代码中使用这样的东西。我不喜欢这些试图"对抗"语言的方法......请记住,Python 不是 C#。