views:

2233

answers:

6

Hello together and a happy new year 2009!

I am programming a simulations for single neurons. Therefore I have to handle a lot of Parameters. Now the Idea is that I have two classes, one for a SingleParameter and a Collection of parameters. I use property() to access the parameter value easy and to make the code more readable. This works perfect for a sinlge parameter but I don't know how to implement it for the collection as I want to name the property in Collection after the SingleParameter. Here an example:

class SingleParameter(object):
  def __init__(self, name, default_value=0, unit='not specified'):
    self.name = name
    self.default_value = default_value
    self.unit = unit
    self.set(default_value)
  def get(self):
    return self._v
  def set(self, value):
    self._v = value
  v = property(fget=get, fset=set, doc='value of parameter')

par1 = SingleParameter(name='par1', default_value=10, unit='mV')
par2 = SingleParameter(name='par2', default_value=20, unit='mA')

# par1 and par2 I can access perfectly via 'p1.v = ...'
# or get its value with 'p1.v'

class Collection(object):
  def __init__(self):
    self.dict = {}
  def __getitem__(self, name):
    return self.dict[name] # get the whole object
    # to get the value instead:
    # return self.dict[name].v
  def add(self, parameter):
    self.dict[parameter.name] = parameter
    # now comes the part that I don't know how to implement with property():
    # It shoule be something like
    # self.__dict__[parameter.name] = property(...) ?

col = Collection()
col.add(par1)
col.add(par2)
col['par1'] # gives the whole object

# Now here is what I would like to get:
# col.par1 -> should result like col['par1'].v
# col.par1 = 5 -> should result like col['par1'].v = 5

Any ideas?

Cheers, Philipp (der Rautenberg :-)


Other questions that I put to understand property():

+6  A: 

Look at built-in functions getattr and setattr. You'll probably be a lot happier.

S.Lott
+3  A: 

Properties are meant to dynamically evaluate attributes or to make them read-only. What you need is customizing attribute access. __getattr__ and __setattr__ do that really fine, and there's also __getattribute__ if __getattr__ is not enough.

See Python docs on customizing attribute access for details.

Abgan
when I add:-----def __setattr__(self, name, value): self.dict[name] = value----I get the error message that " 'Collection' object has no attribute 'dict' " - although it is implemented in __init__.
Philipp der Rautenberg
ok, I implemented it now with set-/getattr. see answer below!
Philipp der Rautenberg
+2  A: 

Have you looked at the traits package? It seems that you are reinventing the wheel here with your parameter classes. Traits also have additional features that might be useful for your type of application (incidently I know a person that happily uses traits in neural simulations).

nikow
Thanks alot. I came across traits once but didn't really get what they are good for. Probably now is the right time to have another look :-) !
Philipp der Rautenberg
A: 

Now I implemented a solution with set-/getattr:

class Collection(object):
...
  def __setattr__(self, name, value):
    if 'dict' in self.__dict__:
      if name in self.dict:
        self[name].v = value
    else:
      self.__dict__[name] = value
  def __getattr__(self, name):
    return self[name].v

There is one thing I quite don't like that much: The attributes are not in the __dict__. And if I have them there as well I would have a copy of the value - which can be dangerous...

Philipp der Rautenberg
well, then just don't put a copy of them there. It would be hard to do anyway since you've overrided setattr.
nosklo
It's not recommended to use built-in type names for variables. Consider changing the 'dict' attribute name to something more self explanatory.
Abgan
A: 

Finally I succeded to implement the classes with property(). Thanks a lot for the advice. It took me quite a bit to work it out - but I can promise you that this exercise helps you to understand better pythons OOP.

I implemented it also with __getattr__ and __setattr__ but still don't know the advantages and disadvantages to the property-solution. But this seems to be worth another question. The property-solutions seems to be quit clean.

So here is the code:

class SingleParameter(object):
  def __init__(self, name, default_value=0, unit='not specified'):
    self.name = name
    self.default_value = default_value
    self.unit = unit
    self.set(default_value)
  def get(*args):
    self = args[0]
    print "get(): "
    print args
    return self._v
  def set(*args):
    print "set(): "
    print args
    self = args[0]
    value = args[-1]
    self._v = value
  v = property(fget=get, fset=set, doc='value of parameter')

class Collection(dict):
  # inheriting from dict saves the methods: __getitem__ and __init__
  def add(self, par):
    self[par.name] = par
    # Now here comes the tricky part.
    # (Note: this property call the get() and set() methods with one
    # more argument than the property of SingleParameter)
    setattr(Collection, par.name,
      property(fget=par.get, fset=par.set))

# Applying the classes:
par1 = SingleParameter(name='par1', default_value=10, unit='mV')
par2 = SingleParameter(name='par2', default_value=20, unit='mA')
col = Collection()
col.add(par1)
col.add(par2)
# Setting parameter values:
par1.v = 13
col.par1 = 14
# Getting parameter values:
par1.v
col.par1
# checking identity:
par1.v is col.par1
# to access the whole object:
col['par1']

As I am new I am not sure how to move on: how to treat follow up questions (like this itself):

  • get() is seems to be called twice - why?
  • oop-design: property vs. "__getattr__ & __setattr__" - when should I use what?
  • is it rude to check the own answer to the own question as accepted?
  • is it recommended to rename the title in order to put correlated questions or questions elaborated with the same example into the same context?

Other questions that I put to understand property():

Philipp der Rautenberg
+5  A: 

Using the same get/set functions for both classes forces you into an ugly hack with the argument list. Very sketchy, this is how I would do it:

In class SingleParameter, define get and set as usual:

def get(self):
  return self._s
def set(self, value):
  self._s = value

In class Collection, you cannot know the information until you create the property, so you define the metaset/metaget function and particularize them only later with a lambda function:

def metaget(self, par):
  return par.s
def metaset(self, value, par):
  par.s = value
def add(self, par):
  self[par.name] = par
  setattr(Collection, par.name,
    property(
      fget=lambda x : Collection.metaget(x, par),
      fset=lambda x, y : Collection.metaset(x,y, par))
meteore