Skip to content Skip to sidebar Skip to footer

Accessing An Object's Attribute Inside __setattr__

Python bit me today. I'm trying to access an object's attribute inside its __setattr__ implementation - I can't figure out how. This is what I've tried so far: class Test1(object):

Solution 1:

Because inside the __init__ it is trying to set blub which calls __setattr__; and it does not set anything but tries to access (and print) blub, finds nothing and raises the error. Check this:

>>> classTest2(object):
  def__init__(self):
    print"__init__ called"
    self.blub = 'hi2'print"blub was set"def__setattr__(self, name, value):
    print"__setattr__ called"print self.blub


>>> Test2()
__init__ called
__setattr__ called

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    Test2()
  File "<pyshell#9>", line 4, in __init__
    self.blub = 'hi2'
  File "<pyshell#9>", line 9, in __setattr__
    print self.blub
AttributeError: 'Test2'object has no attribute 'blub'>>> 

Solution 2:

OP, you haven't told us the whole story. You did not just run code like this:

TestX().bip = 'bap'

You ran code like this:

try:
    TestX().bip = 'bap'
except Exceptionas ex:
    print ex

There's a big difference. Why, you ask? Well, your output seems on first glance to indicate that Test6 works, and several comments and answers assumed that it did. Why does it appear to work? Reading the code, there's no way it should work. A closer inspection of the source code reveals that if it had worked, it should have printed hi6, not 'blub'.

I put a breakpoint at the print ex line in pdb to examine the exception:

(Pdb) ex
KeyError('blub',)
(Pdb) print ex
'blub'

For some reason print ex does not print KeyError: blub like you'd expect, but just 'blub', which was why Test6 appeared to work.

So we've cleared that up. In the future, please do not leave out code like this because it might be important.

All the other answers correctly point out that you have not set the attribute you're attempting to print, and that this is your problem. The answer you had accepted previously, before you accepted this answer istead, prescribed the following solution:

def__setattr__(self, name, value):
    self.__dict__[name] = value
    print self.__dict__[name]

While this solution does indeed work, it is not good design. This is because you might want to change the base class at some point, and that base class might might have important side effects when setting and/or getting attributes, or it might not store the attributes in self.__dict__ at all! It is better design to avoid messing around with __dict__.

The correct solution is to invoke the parent __setattr__ method, and this was suggested by at least one other answer, though with the wrong syntax for python 2.x. Here's how I'd do it:

def__setattr__(self, name, value):
    super(Test6, self).__setattr__(name, value)
    printgetattr(self, name)

As you see I'm using getattr instead of __dict__ to look up attributes dynamically. Unlike using __dict__ directly, this will call self.__getattr__ or self.__getattribute__, as appropriate.

Solution 3:

__setattr__ works on the class, so when you're over-riding - you need to make it actually set the attribute... otherwise, it'll never exist... eg:

object.__setattr__(self, name, value)

So, in your __init__, when you do self.blub = 'hi' that's in effect a no-op.

Solution 4:

You never actually set the attribute in your __setattr__ so of course the object doesn't have it.

def__setattr__(self, name, value):
    self.name = value
        # this doesn't work, since it calls itself (the __setattr__)def__setattr__(self, name, value):
    super().__setattr__(name, value)
        # call the default implementation directly in Py 3.xdef__setattr__(self, name, value):
    super(TestX, self).__setattr__(name, value) # for Python 2.x

Of course doing this alone is good for nothing, you usually want to add some functionality oround this like some condition, debug printing, caching or whatever you need.

Solution 5:

printself.__dict__['blub']

Prints out blub which is correct. You have to set the new value first, because python won't do that for you, like that:

def__setattr__(self, name, value):
    self.__dict__[name] = value
    print self.__dict__[name]

Then test6.blub = 'test' should print out test

Edit:

As suggested by @Lauritz you can also use

def__setattr__(self, name, value):
    super(Test6, self).__setattr__(name, value)
    print self.__dict__[name]

Which invokes the parent function, so if you superclass has already a __setattr__ function it won't get overridden.

Post a Comment for "Accessing An Object's Attribute Inside __setattr__"