问题 可以设置Python对象的任何属性


例如,这段代码是Python:

a = object()
a.b = 3

AttributeError: 'object' object has no attribute 'b'

但是,这段代码:

class c(object): pass
a = c()
a.b = 3

很好。当类x没有该属性时,为什么我可以分配属性b?如何让我的类只定义属性?


1373
2018-05-02 20:34


起源

How can I make my classes have only properties defined?  - 应该指出的是,这只是很多额外的工作,没有充分的理由,也许打破了一些有用的元编程,所以应该避免它,除非你有充分的理由去做,并确切知道你在做什么。


答案:


object type是一个用C编写的内置类,不允许向其中添加属性。它已被明确编码以防止它。

在您自己的类中获得相同行为的最简单方法是使用 __slots__ 属性,用于定义要支持的确切属性的列表。 Python将为这些属性保留空间,不允许任何其他属性。

class c(object):
    __slots__ = "foo", "bar", "baz"

a = c()

a.foo = 3  # works
a.b   = 3  # AttributeError

当然,这种方法有一些警告:你不能腌制这样的对象,以及期望每个对象都有的代码 __dict__ 属性会破裂。 “更多Pythonic”的方式是使用自定义 __setattr__() 如另一张海报所示。当然,有很多方法可以解决这个问题 __slots__ (除了子类化并将属性添加到子类)。

一般来说,这不是你应该在Python中真正想做的事情。如果你的类的用户想要在类的实例上存储一些额外的属性,那么没有理由不让它们,实际上你可能想要的原因很多。


12
2018-05-02 20:49



这很棒,我不知道 __slots__ - Mu Mind
有趣的方法!我不知道 __slots__。然而,我正在审视这种方法,并偶然发现 stackoverflow.com/questions/472000/python-slots。该线程中提到了一些好的警告。特别是缺乏了 __dict__ 我们使用的方法 __slots__! - Praveen Gollakota
是的,这些都是好警告。更多的Pythonic方式可以说是自己写的 __setattr__() 禁止未经批准的属性。当然,使用 __slots__ 将节省内存并加快速度。 - kindall
编辑调出陷阱。 - kindall


你可以覆盖的行为 __setattr__ 像这样的魔法。

class C(object):
    def __setattr__(self, name, value):
        allowed_attrs = ('a', 'b', 'c')
        if name not in allowed_attrs:
            # raise exception
            # or do something else
            pass
        self.__dict__[name] = value

当然,这只会阻止您设置a.b(点形式)等属性。您仍然可以使用设置属性 a.__dict__[b] =值。在这种情况下,你应该覆盖 __dict__ 方法也是。


3
2018-05-02 20:47





Python通常允许您在任何对象上设置任何属性。这是一个特例 object 阶级行为不同。在C中也实现了一些类似的模块。

如果你  你的对象表现得像这样,你可以定义一个 __setattr__(self, name, value) 显式执行a的方法 raise AttributeError() 如果您尝试设置不在“已批准列表”上的成员(请参阅 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/389916


2
2018-05-02 20:45





创建一个 object 实例没有任何功能。因此,在基础的实例上设置属性 object 类型明确禁用。您必须将其子类化才能创建属性。

提示:如果您想要一个简单的对象用作存储属性的东西,您可以通过创建一个匿名函数来实现 lambda。作为对象的函数也能够存储属性,因此这是完全合法的:

>>> a = lambda: None
>>> a.b = 3
>>> a.b 
3

0
2018-05-02 20:48





这是因为当你说的时候 a.b = 3,它在表示b的变量中创建一个变量。例如,

class a: pass
print a.b

回报 AttributeError: class a has no attribute b

不过这段代码,

class a: pass
a.b = 3
print a.b

返回3,因为它将a的b值设置为3。


-1
2018-05-02 20:40



为什么这会被贬低? - Matt Habel
这并没有回答他为什么能这样做 a.b=3 在第二种情况下,但不在第一种情况下。 - Bryan Oakley
我没有,我也不愿意,但我必须说它并没有完全回答这个问题。