python中的安全属性分配



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

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

最新更新