Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
401 views
in Technique[技术] by (71.8m points)

python - Class properties and __setattr__

In Python class, when I use __setattr__ it takes precedence over properties defined in this class (or any base classes). Consider the following code:

class Test(object):
    def get_x(self):
        x = self._x
        print "getting x: %s" % x
        return x
    def set_x(self, val):
        print "setting x: %s" % val
        self._x = val
    x = property(get_x, set_x)
    def __getattr__(self, a):
        print "getting attr %s" % a
        return -1
    def __setattr__(self, a, v):
        print "setting attr %s" % a

When I create the class and try to set x, __setattr__ is called instead of set_x:

>>> test = Test()
>>> test.x = 2
setting attr x
>>> print test.x
getting attr x_
getting x: -1
-1

What I want to achieve is that the actual code in __setattr__ were called only if there is no relevant property i.e. test.x = 2 should call set_x. I know that I can achieve this easily by manually checking if a is "x" is __setattr__, however this would make a poor design. Is there a more clever way to ensure the proper behavior in __setattr__ for every property defined in the class and all the base classes?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The search order that Python uses for attributes goes like this:

  1. __getattribute__ and __setattr__
  2. Data descriptors, like property
  3. Instance variables from the object's __dict__ (when setting an attribute, the search ends here)
  4. Non-Data descriptors (like methods) and other class variables
  5. __getattr__

Since __setattr__ is first in line, if you have one you need to make it smart unless want it to handle all attribute setting for your class. It can be smart in either of two ways: Make it handle a specific set attributes only, or make it handle all but some set of attributes. For the ones you don't want it to handle, call super().__setattr__.

For your example class, handling "all attributes except 'x'" is probably easiest:

def __setattr__(self, name, value):
    if name == "x":
        super(Test, self).__setattr__(name, value)
    else:
        print "setting attr %s" % name

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...