使Python类在创建新属性时抛出错误

jpm*_*c26 3 python attributes class python-2.7

让我们说这是我的班级:

class A:
    def __init__(self):
        self.good_attr = None
        self.really_good_attr = None
        self.another_good_attr = None
Run Code Online (Sandbox Code Playgroud)

然后调用者可以设置这些变量的值:

a = A()
a.good_attr = 'a value'
a.really_good_attr = 'better value'
a.another_good_attr = 'a good value'
Run Code Online (Sandbox Code Playgroud)

但他们也可以添加新属性:

a.goood_value = 'evil'
Run Code Online (Sandbox Code Playgroud)

这对我的用例来说是不可取的.我的对象被用于将一些值传递给一组方法.(基本上,这个对象替换了一些方法上的一长串共享参数,以避免重复,并清楚地区分共享内容和不同内容.)如果调用者拼写错误属性名称,那么该属性将被忽略,导致意外和令人困惑,可能很难弄清楚行为.最好快速失败,通知调用者他们使用了将被忽略的属性名称.所以类似于下面的内容是我们在使用对象上尚不存在的属性名称时所希望的行为:

>>> a.goood_value = 'evil'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'goood_value'
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

我还要注意,我完全清楚调用者可以创建一个新类并做任何他们想做的事情,完全绕过它.但是,这将是不受支持的行为.制作我提供的对象只是创建一个快速失败的骨头检查,以便为那些利用我提供的对象(包括我自己)的人节省打字错误的时间,而不是让他们抓住头脑,想知道为什么事情以意想不到的方式表现.

Mar*_*ers 8

您可以使用该__setattr__方法挂钩属性设置.所有属性设置都会调用此方法,因此考虑到它也将被称为"正确"属性:

class A(object):
    good_attr = None
    really_good_attr = None
    another_good_attr = None

    def __setattr__(self, name, value):
        if not hasattr(self, name):
            raise AttributeError(
                '{} instance has no attribute {!r}'.format(
                    type(self).__name__, name))
        super(A, self).__setattr__(name, value)
Run Code Online (Sandbox Code Playgroud)

因为good_attr等等在类上定义,所以hasattr()调用True为这些属性返回,并且不会引发异常.您也可以设置相同的属性__init__,但必须在类上定义属性hasattr()才能工作.

另一种方法是创建一个可以测试的白名单.

演示:

>>> a = A()
>>> a.good_attr = 'foo'
>>> a.bad_attr = 'foo'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 10, in __setattr__
AttributeError: A instance has no attribute 'bad_attr'
Run Code Online (Sandbox Code Playgroud)

当然,确定的开发人员仍然可以通过向a.__dict__实例字典添加键来为您的实例添加属性.

另一种选择是使用副作用__slots__; 槽用于节省内存,因为字典需要更多的空间,而不仅仅是将值直接放入Python为每个实例创建的C结构中(不需要键和动态表).副作用是在这样的类实例上没有更多属性的位置:

class A(object):
    __slots__ = ('good_attr', 'really_good_attr', 'another_good_attr')

    def __init__(self):
        self.good_attr = None
        self.really_good_attr = None
        self.another_good_attr = None
Run Code Online (Sandbox Code Playgroud)

然后错误消息如下所示:

>>> a = A()
>>> a.good_attr = 'foo'
>>> a.bad_attr = 'foo'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'bad_attr'
Run Code Online (Sandbox Code Playgroud)

但请阅读文档中列出的使用注意事项__slots__.

由于__dict__使用时没有实例属性__slots__,因此该选项实际上关闭了在实例上设置任意属性的大门.