5

Defining __setattr__ overrides all setter methods / properties I define in a class. I want to use the defined setter methods in the property, if a property exists for a field and use self.__dict__[name] = value otherwise.

Help! I found one solution that used __setitem__, but this does not work for me

Where are properties stored in a python class? How do I access them?

How do I define __setattr__ so that it uses the properties for fields with setter methods defined?

class test(object):

def _get_gx(self):
    print "get!"
    return self.__dict__['gx']

def _set_gx(self, gx):
    print "set!"
    self.__dict__['gx'] = gx
    gx = property(_get_gx, _set_gx)

def __setattr__(self, name, value):
    self.__dict__[name] = value

def __init__(self):
    pass

also,

Why is "get!" printed twice when I do,

x = test()
x.gx = 4
x.gx

prints: 

   "gets!"
   "gets!"
   4
HaltingState
  • 1,810
  • 1
  • 20
  • 22
  • It's going to be difficult to work this out with your indentation broken. The only general question that you ask though is "where do descriptors live" and the answer is in the class dict. – aaronasterling Jan 23 '11 at 03:28
  • They are not in the class dictionary. The properties that i define, do not show up in the class dictionary. – HaltingState Jan 23 '11 at 03:51
  • 1
    Then you're doing something wrong. Descriptors live in the class dictionary. Look at the python data model for details. If you fix your indentation, I'll figure it out for you. – aaronasterling Jan 23 '11 at 04:00
  • Use 'super' instead of' __dict__', might do. – ISONecroMAn Sep 05 '16 at 07:38

1 Answers1

10

You need to rewrite your __setattr__ function. As per the docs, new style classes should use baseclass.__setattr__(self, attr, value) instead of self.__dict__[attr] = value. The former will lookup any descriptors whereas the latter will assign directly to the dict.

So rewrite your method as

def __setattr__(self, name, value):
    object.__setattr__(self, name, value)

or

def __setattr__(self, name, value):
    super(Test, self).__setattr__(name, value)

and you'll be fine. The code

class Test(object):
    @property
    def gx(self):
        print "getting gx"
        return self.__dict__['gx']

    @gx.setter
    def gx(self, value):
        print "setting gx"
        self.__dict__['gx'] = value

    def __setattr__(self, attr, value):
        print "using setattr"            
        object.__setattr__(self, attr, value)

t = Test()
t.gx = 4
t.dummy = 5
print t.gx
print t.dummy

print dir(Test)

outputs

using setattr
setting gx
getting gx
using setattr
4
5
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gx']

I don't know why your version is calling the getter twice. This one doesn't. Also, to answer your question about where descriptors live, you can plainly see it as the last entry in the class dict.

It's worth noting that you don't need __setattr__ to do what you want in your example. Python will always write an assignment foo.bar = x to foo.__dict__['bar'] = x regardless of if there's an entry in foo.__dict__ or not. __setattr__ is for when you want to transform the value or log the assignment or something like that.

aaronasterling
  • 68,820
  • 20
  • 127
  • 125