Search code examples
pythonpython-2.7classattributespython-internals

Python 2.7: What does object.__setattr__ do under the hood?


In Python 2.7, I can declare a new-style class and set an attribute using object.__setattr__:

class A (object):
    def __init__ (self):
        object.__setattr__(self, 'foo', 'bar')

A().foo # 'bar'

However, this does not work in an old-style class:

class B:
    def __init__ (self):
        object.__setattr__(self, 'foo', 'bar')

B().foo

TypeError: can't apply this __setattr__ to instance object

I know that old-style classes do not support this behaviour, but I do not know why it doesn't work.

What is going on under the hood when I call object.__setattr__ on a new-style class that cannot occur for old-style classes?

Please note that I am not using this code for anything, I am just trying to understand it.


Solution

  • The object.__setattr__ method includes a specific check to make sure that self passed in is really a (subclass) of object (or another object that reuses the same C function directly). That test doesn't allow for anything else being passed in, including old-style instances.

    The check is there to prevent object.__setattr__ being used to modify built-in types (known as the Carlo Verre hack, after its discoverer.

    Without the explicit check (or if you compiled Python to disable it), you could do this:

    >>> object.__setattr__(str, 'lower', str.upper)
    >>> "This is why we can't have nice things".lower()
    "THIS IS WHY WE CAN'T HAVE NICE THINGS"
    

    See the hackcheck() function, and the original Python-dev discussion.

    That old-style instance objects don't pass the test is just a side-effect.

    Note that the only reason you'd ever want to call object.__setattr__ is if you have a base class that implements a __setattr__ method you need to bypass. For old-style instances, you could just use self.__dict__[name] = value instead.