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