Python中的安全属性分配

时间:2018-08-07 10:26:28

标签: python class attributes

我有一个代表我的算法参数选项的类。创建对象会将所有选项初始化为一些预定义的值。然后,我可以通过简单地为相应的属性分配一个不同的值来更改参数:

opts = AlgOptions()
opts.mutation_strength = 0.2

但是,有时在输入属性名称时会出错,例如:

opts = AlgOptions()
opts.mutation_str = 0.2

Python只会创建一个不同的属性并继续运行,从而产生难以调试的意外结果。

是否有一种优雅的方法来避免这种潜在的陷阱?与C#之类的语言将这种情况解释为错误的语言相比,总是仔细检查所有属性名称是很麻烦的。我想到的很少,但是有一些警告:

1)我可以为参数设置setter方法,但这似乎是很多样板代码。这样,如果我尝试使用错误的名称调用方法,我将获得有意义的启示。

2)我可以创建类内部所有属性的列表,并检查是否有任何名称不在列表中的类属性。但是,我不确定我将在何时何地执行检查,而且无论如何似乎都是笨拙的解决方案。

3 个答案:

答案 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#。