unine...@gmail.com wrote:
class Person:
    def __init__(self):
        for prop in props:
            setattr(self, "__" + prop[0], prop[1])
            setattr(Person, "Get" + prop[0], lambda self: getattr
(self, "__" + prop[0]))

I've had a similar problem here and here is best how I can explain it. The prop in the lambda function is a closure by 'name' to the prop in the containing name space (__init__), so when the lambda function executes, it looks for the name 'prop' in this name space and uses it's value. After the 'for prop in props' loop is complete, 'prop' is left referring to the last item in props, so each lambda function would use it (mary)

One solution is to not use lambda and avoid closures by using default arguments:

for prop in props:
   def Getter(self=self, prop=prop):
      return getattr(self, '__' + prop[0])
   setattr(self, '__' + prop[0], prop[1])
   setattr(self, 'Get' + prop[0], Getter)

I have several problems with this though:

1. I don't think this will invoke Pythons name mangling mechanism. The property will be '__name' and not '__Person_name'.

2. If you make a copy of the class, including attributes, the Getter will operate on the old class not new:

Person a
b = copy.copy(a)

setattr(a, '__name', bob)
setattr(b, '__name', sarah)

b.Getname() -> bob

In order to make it work, the class must support updating the Getter when it is copied to have a new self value.

import copy

class MethodCaller:
   def __init__(self, obj, method, name):
       self.obj = obj
       self.method = method
       self.name = name

       setattr(obj, name, self)

   def __call__(self, *args, **kwargs):
       return self.method(self.obj, *args, **kwargs)

   def copy(self, newobj):
       return MethodCaller(newobj, self.method, self.name)


props = ( ('name', 'mary'), ('age', 21), ('gender', 'female') )

class Person:
   def __init__(self):
       self._methods = []

       for prop in props:
           (name, value) = prop
def getter(self, name=name):
               return getattr(self, '_' + name)

           setattr(self, '_' + name, value)
           self._methods.append(MethodCaller(self, getter, 'Get' + name))

   def copy(self,copymethods=True):
       c = copy.copy(self)
       if copymethods:
           c._methods = []
           for i in self._methods:
               c._methods.append(i.copy(c))
       return c


# Example without copying methods
p = Person()
q = p.copy(False)

p._name = 'sarah'
q._name = 'michelle'

print p.Getname()
print p.Getage()
print p.Getgender()

print q.Getname() # Still prints 'sarah', because getter still refers to 'p' instead of 'q'
print q.Getage()
print q.Getgender()

# Example with copying methods
p = Person()
q = p.copy()

p._name = 'sarah'
q._name = 'michelle'

print p.Getname()
print p.Getage()
print p.Getgender()

print q.Getname() # Prints 'michelle'
print q.Getage()
print q.Getgender()

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to