我有一个代表我的算法参数选项的类。创建对象会将所有选项初始化为一些预定义的值。然后,我可以通过简单地为相应的属性分配一个不同的值来更改参数:
opts = AlgOptions()
opts.mutation_strength = 0.2
但是,有时在输入属性名称时会出错,例如:
opts = AlgOptions()
opts.mutation_str = 0.2
Python只会创建一个不同的属性并继续运行,从而产生难以调试的意外结果。
是否有一种优雅的方法来避免这种潜在的陷阱?与C#之类的语言将这种情况解释为错误的语言相比,总是仔细检查所有属性名称是很麻烦的。我想到的很少,但是有一些警告:
1)我可以为参数设置setter方法,但这似乎是很多样板代码。这样,如果我尝试使用错误的名称调用方法,我将获得有意义的启示。
2)我可以创建类内部所有属性的列表,并检查是否有任何名称不在列表中的类属性。但是,我不确定我将在何时何地执行检查,而且无论如何似乎都是笨拙的解决方案。
答案 0 :(得分:3)
您可以使用slots来实现这一点,这基本上可以防止创建未在__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__
存在文档中讨论的一些缺点。
答案 1 :(得分:2)
因此,通常最好避免打错字;),但这是一个(略有漏洞)解决方案。基本上是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
,然后为您的类添加注释,这可能会更容易。
答案 2 :(得分:1)
这是仅允许设置某些属性的一种可能方法:
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#。