首页 文章

无法设置对象类的属性

提问于
浏览
70

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

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

由于 AttributeError: 'object' object has no attribute 'attr' . 但是,对于从object继承的任何类,它是有效的:

class Sub(object):
    pass

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

打印 s.attr 按预期显示'hello' . 为什么会这样? Python语言规范中的内容指定您不能将属性分配给vanilla对象?

6 回答

  • 114

    这只是因为优化 .

    Dicts相对较大 .

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

    在C中定义的大多数(可能是所有)类都没有用于优化的字典 .

    如果你看source code,你会看到有很多检查,看看对象是否有一个字典 .

  • 1

    为了支持任意属性赋值,对象需要 __dict__ :与对象关联的dict,其中可以存储任意属性 . 否则,无处可放新属性 .

    object 的一个实例 not 携带 __dict__ - 如果确实如此,那么在可怕的循环依赖问题之前(因为 dict ,就像大多数其他一切一样,继承自 object ;-),这会使Python中的每个对象都带有一个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 实例可以有任意属性,但是你为这种灵活性付出了相当大的空间成本 .

    那么,如果你只想要一个额外的属性_178230_,那该怎么办?这是一种罕见的需求,但Python确实为此提供了一种特殊的机制......

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

    ......不像_1782303那么小,请注意! (甚至两个 int ,一个 self 和一个 self.foobar - 第二个可以重新分配),但肯定比 dint 好多了 .

    当类具有 __slots__ 特殊属性(字符串序列)时, class 语句(更确切地说,默认元类, type )确实 not 为该类的每个实例配备 __dict__ (因此具有任意属性的能力) ,只是一个有限的,刚性的"slots"(基本上每个都可以保存对某个对象的一个引用的地方)和给定的名称 .

    为了弥补灵活性的损失,你可以获得每个实例的大量字节(仅当你有数以万计的实例加入时才有意义,但是,有一些用例) .

  • -1

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


    在Python 3.3中,您可以(并且应该)使用types.SimpleNamespace而不是虚拟类的实例 .

  • 5

    因此,在调查我自己的问题时,我发现了有关Python语言的内容:你可以继承像int这样的东西,你会看到相同的行为:

    >>> class MyInt(int):
           pass
    
    >>> x = MyInt()
    >>> print x
    0
    >>> x.hello = 4
    >>> print x.hello
    4
    >>> x = x + 1
    >>> print x
    1
    >>> print x.hello
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
    AttributeError: 'int' object has no attribute 'hello'
    

    我假设最后的错误是因为add函数返回一个int,所以我必须覆盖像 __add__ 之类的函数,以便保留我的自定义属性 . 但是这一切现在对我来说都是有道理的(我想),当我想起"object"时"int" .

  • 0

    这是因为对象是“类型”,而不是类 . 通常,在C扩展中定义的所有类(如所有内置数据类型和numpy数组之类的东西)都不允许添加任意属性 .

  • 16

    这是(IMO)Python的基本限制之一 - 您无法重新打开类 . 我认为实际的问题是由于在C中实现的类不能在运行时修改的事实...子类可以,但不是基类 .

相关问题