无法设置对象类的属性

2024-05-13 22:20:39 发布

您现在位置:Python中文网/ 问答频道 /正文

所以,我在回答this question时玩Python,发现这是无效的:

o = object()
o.attr = 'hello'

因为AttributeError: 'object' object has no attribute 'attr'。但是,对于从对象继承的任何类,它都是有效的:

class Sub(object):
    pass

s = Sub()
s.attr = 'hello'

打印s.attr按预期显示“hello”。为什么是这个案子?Python语言规范中有什么规定不能将属性赋给vanilla对象?


Tags: 对象no语言helloobjectattributepassthis
3条回答

为了支持任意属性分配,一个对象需要一个__dict__:一个与该对象相关联的dict,其中可以存储任意属性。否则,就没有地方可以放入新的属性。

一个object的实例不会携带一个__dict__——如果它携带了,在可怕的循环依赖问题之前(因为dict,和大多数其他东西一样,继承自object;-),这将为Python中的每个对象都添加一个dict,这意味着每个当前没有或需要dict的对象都有字节的开销(本质上,没有任意可分配属性的所有对象都没有或需要dict)。

例如,使用优秀的pympler项目(您可以从here通过svn获得它),我们可以进行一些测量…:

>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16

你不会希望每个int占用144字节而不是16字节,对吧?-)

现在,当你创建一个类(继承自任何类)时,事情会发生变化…:

>>> class dint(int): pass
... 
>>> asizeof.asizeof(dint(23))
184

…现在添加了__dict__(加上一点开销),因此dint实例可以具有任意属性,但是您为这种灵活性付出了相当大的空间代价。

那么,如果您想要ints只使用一个one额外属性foobar。。。?这是一种罕见的需求,但Python确实为此提供了一种特殊的机制。。。

>>> class fint(int):
...   __slots__ = 'foobar',
...   def __init__(self, x): self.foobar=x+100
... 
>>> asizeof.asizeof(fint(23))
80

……不是像int那么小,小心!(甚至是两个int,一个是self,另一个是self.foobar——第二个可以重新分配),但肯定比一个dint要好得多。

当类具有__slots__特殊属性(字符串序列)时,那么class语句(更准确地说,默认元类type)并没有为该类的每个实例配备__dict__(因此也就具备了拥有任意属性的能力),只是一组有限的、严格的“槽”(基本上是放置每一个都可以包含一个对某个对象的引用)和给定的名称。

作为失去灵活性的交换,每个实例获得了大量字节(可能只有当您有无数个实例到处闲逛时才有意义,但是的用例)。

这仅仅是由于优化。

口述比较大。

>>> import sys
>>> sys.getsizeof((lambda:1).__dict__)
140

在C中定义的大多数(可能所有)类都没有用于优化的指令。

如果您查看source code,您将看到有许多检查来查看对象是否有dict。

正如其他回答者所说,一个object没有一个__dict__object是所有类型的基类,包括intstr。因此,由object提供的任何东西也将成为他们的负担。即使是像可选的__dict__这样简单的东西,对于每个值都需要一个额外的指针;这将为系统中的每个对象浪费额外的4-8字节的内存,用于非常有限的实用程序。


在Python 3.3+中,您可以(而且应该)使用^{}来代替执行伪类的实例。

相关问题 更多 >