problem with defining setters and getters

Alex Martelli aleax at aleax.it
Wed Mar 12 12:34:00 EST 2003


John Hunter wrote:

> 
> 
> I have a class that is initialized with another object that has
> certain attributes I want to expose to the class with getters and
> setters.  I am doing this
> 
> class Entry:
>     def __init__(self):
>         self.pid = 1
>         self.somevar = 2
> 
>     def get_field_names(self):
>         return ['pid', 'somevar']
>     
> class MyClass:
>     def __init__(self, entry):
>         self.entry = entry
> 
>         for name in entry.get_field_names():
>             def get_func(): return getattr(self.entry,  name)
>             def set_func(val): return setattr(self.entry, name, val)

Note that these get_func and set_func functions you are defining do no 
re-binding at all of 'name' at the time of definition -- rather, they 
all rely on whatever value 'name' is bound to in their lexically
surrounding scope *at the time they execute*.  So, clearly they MUST
be all using the same value for 'name', yes?

>             setattr(self, 'get_' + name, get_func)
>             setattr(self, 'set_' + name, set_func)
> 
>         
> 
> entry = Entry()
> c = MyClass(entry)
> 
> c.set_somevar('John')
> print c.get_pid()
> 
> 
> entry has a function get_field_names that returns a list of attributes
> that I want to expose.  pid is one of those attributes, and I am
> calling set_pid and get_pid as a test case.
> 
> The problem is that all of the getters and setters take on the last
> attribute returned by field names.  That is, get_pid returns 'John' in
> the example above, and I want it to return 1.  It appears that there
> is only one get_func in the __init__ loop, and all of the getters are
> using this func.  Likewise for set_func.

No, there are many get_func and set_func objects created, but all of
them choose to use the same binding for 'name' (in their lexically
enclosing scope).  For your purposes you seem to want to re-bind that
at function-definition time -- very well, then DO so, e.g. by:

         def get_func(name=name): return getattr(self.entry,  name)
         def set_func(val, name=name): return setattr(self.entry, name, val)

and you're all set -- this is just one of several equivalent approaches,
using the fact that default values are evaluated at definition-time, but
if you're uncomfortable with that you may choose different approaches,
such for example as the following:

  def __init__(self, entry):

      def make_get_set(name):
          def get_func(): return getattr(entry,  name)
          def set_func(val): return setattr(entry, name, val)
          return get_func, set_func
      
      for name in entry.get_field_names():
          get_func, set_func = make_get_set(name)
          setattr(self, 'get_' + name, get_func)
          setattr(self, 'set_' + name, set_func)

here, in make_get_set, I'm exploiting the lookup of entry in
the lexically surrounding scope (thus not binding self.entry --
it's not necessary in this example) but re-binding 'name' by
simple argument-passing.


Alex





More information about the Python-list mailing list