views:

542

answers:

4

Consider the following class definitions

class of2010(object):
    def __init__(self):
        self._a = 1
        self._b = 2
        self._c = 3

    def set_a(self,value):
        print('setting a...')
        self._a = value
    def set_b(self,value):
        print('setting b...')
        self._b = value
    def set_c(self,value):
        print('setting c...')
        self._c = value
    a = property(fset=self.set_a)
    b = property(fset=self.set_b)
    c = property(fset=self.set_c)

note that set_[a|b|c]() do the same thing. is there a way do define:

def set_magic(self,value):
    print('setting <???>...')
    self._??? = value

once and use it for a,b,c as follows

a = property(fset=self.set_magic)
b = property(fset=self.set_magic)
c = property(fset=self.set_magic)
A: 

May be you're looking for __setattr__(self, name, value)

Take a look here

Keeper
+6  A: 
def attrsetter(attr):
  def set_any(self, value):
    setattr(self, attr, value)
  return set_any

a = property(fset=attrsetter('_a'))
b = property(fset=attrsetter('_b'))
c = property(fset=attrsetter('_c'))
Ignacio Vazquez-Abrams
It's property(fset=...), but otherwise I would use something like that.
olt
@olt: Thanks, fixed.
Ignacio Vazquez-Abrams
A: 
class...
 def __setattr__(self, name, value):
  print 'setting', name
  self.__dict__[name] = value

That's it.

Tor Valamo
+4  A: 

I see that your setters just log a message and then simply assign the value - in fact, your accepted answer just assigns the value. Are you using this pattern because it is the Accepted Practice / Conventional Wisdom in some other language, perhaps one whose name starts with "J"? If so, then please learn that the Pythonic approach to this same design is the much simpler:

class Of2010(object):
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3

No do-nothing setters, no intermediate function calls just to assign a value. "What?!", you say? "Public exposure to member variables?!!" Well, yes actually.

Look at these classes from the standpoint of client code. To use your class, clients create an object, and then assign property "a" using:

obj = Of2010()
obj.a = 42

Remarkably, this is the exact same code for the 5-liner class I posted above.

Why does the J-language encourage the more verbose property style? To preserve the class interface in the event of future change in requirements. If at some point in time, some other value of the object must change in concert with any changes to a, then you must implement the property mechanism. Sadly, the J-language exposes the nature of the attribute access mechanism to the client code, so that to introduce a property at some point in the future is an intrusive refactoring task that will require a rebuild of all clients that make use of that class and its "a" attribute.

In Python, such is not the case. Access to the object's "a" attribute is determined at runtime in the caller. Since direct access and property access both "look" the same, your Python class preserves this interface even though the actual mechanism is different. What matters is that it is identical as far as the client code is concerned.

So in Java, one introduces this property complexity right from the inception of this class (and in fact, by Accepted Practice, of all classes), on the off-chance that it may become necessary some day in the future. With Python, one can start by implementing the Simplest Thing That Could Possibly Work, that is, direct access to simple member variables, leaving the complex approach for the time in the future that the extra stuff is actually required and of value. Since that day may never actually come, this is a huge jump forward in getting that first working version of your code out the door.

Paul McGuire
i want an action to occur whenever certain members of (an instance of) a class are set. i want the same action to occur for these members (my example: print the name of the member) without duplicating their setters. this is the reason i need setters. otherwise, as you wisely stated. i would not use them, as they are unnecessary in python
bandana