Can't set attributes on instance of “object” class

后端 未结 7 1215
囚心锁ツ
囚心锁ツ 2020-11-22 10:10

So, I was playing around with Python while answering this question, and I discovered that this is not valid:

o = object()
o.attr = \'hello\'
<
相关标签:
7条回答
  • 2020-11-22 10:27

    This is (IMO) one of the fundamental limitations with Python - you can't re-open classes. I believe the actual problem, though, is caused by the fact that classes implemented in C can't be modified at runtime... subclasses can, but not the base classes.

    0 讨论(0)
  • 2020-11-22 10:32

    https://docs.python.org/3/library/functions.html#object :

    Note: object does not have a __dict__, so you can’t assign arbitrary attributes to an instance of the object class.

    0 讨论(0)
  • 2020-11-22 10:33

    So, investigating my own question, I discovered this about the Python language: you can inherit from things like int, and you see the same behaviour:

    >>> 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'
    

    I assume the error at the end is because the add function returns an int, so I'd have to override functions like __add__ and such in order to retain my custom attributes. But this all now makes sense to me (I think), when I think of "object" like "int".

    0 讨论(0)
  • 2020-11-22 10:39

    To support arbitrary attribute assignment, an object needs a __dict__: a dict associated with the object, where arbitrary attributes can be stored. Otherwise, there's nowhere to put new attributes.

    An instance of object does not carry around a __dict__ -- if it did, before the horrible circular dependence problem (since dict, like most everything else, inherits from object;-), this would saddle every object in Python with a dict, which would mean an overhead of many bytes per object that currently doesn't have or need a dict (essentially, all objects that don't have arbitrarily assignable attributes don't have or need a dict).

    For example, using the excellent pympler project (you can get it via svn from here), we can do some measurements...:

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

    You wouldn't want every int to take up 144 bytes instead of just 16, right?-)

    Now, when you make a class (inheriting from whatever), things change...:

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

    ...the __dict__ is now added (plus, a little more overhead) -- so a dint instance can have arbitrary attributes, but you pay quite a space cost for that flexibility.

    So what if you wanted ints with just one extra attribute foobar...? It's a rare need, but Python does offer a special mechanism for the purpose...

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

    ...not quite as tiny as an int, mind you! (or even the two ints, one the self and one the self.foobar -- the second one can be reassigned), but surely much better than a dint.

    When the class has the __slots__ special attribute (a sequence of strings), then the class statement (more precisely, the default metaclass, type) does not equip every instance of that class with a __dict__ (and therefore the ability to have arbitrary attributes), just a finite, rigid set of "slots" (basically places which can each hold one reference to some object) with the given names.

    In exchange for the lost flexibility, you gain a lot of bytes per instance (probably meaningful only if you have zillions of instances gallivanting around, but, there are use cases for that).

    0 讨论(0)
  • 2020-11-22 10:39

    It's because object is a "type", not a class. In general, all classes that are defined in C extensions (like all the built in datatypes, and stuff like numpy arrays) do not allow addition of arbitrary attributes.

    0 讨论(0)
  • 2020-11-22 10:41

    It is simply due to optimization.

    Dicts are relatively large.

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

    Most (maybe all) classes that are defined in C do not have a dict for optimization.

    If you look at the source code you will see that there are many checks to see if the object has a dict or not.

    0 讨论(0)
提交回复
热议问题