如何在Python中使用动态名称实现property()函数

9

我正在编写单个神经元的模拟程序,因此必须处理大量参数。现在的想法是,我有两个类,一个用于单个参数,一个用于参数集合。我使用property()轻松访问参数值并使代码更易读。这对于单个参数非常完美,但我不知道如何为集合实现它,因为我希望在Collection中根据SingleParameter命名属性。下面是一个例子:

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

我想了解有关property()的其他问题:

7个回答

10

看看内置函数getattrsetattr。你会更加愉快的。


7

对于两个类使用相同的get/set函数会迫使你使用一个带有参数列表的丑陋技巧。非常不可靠,以下是我会这样做:

在SingleParameter类中,像通常一样定义get和set函数:

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

在集合类中,只有在创建属性后才能知道信息,因此您需要定义元数据/元获取函数,并稍后使用lambda函数进行特定化:

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))

5

属性旨在动态评估属性或使其只读。您需要的是自定义属性访问。 __getattr____setattr__非常适合这样做,如果__getattr__不够用,还有__getattribute__

有关详细信息,请参见 Python文档中有关自定义属性访问的内容


当我添加以下代码时:

def setattr(self, name, value): self.dict[name] = value

我会收到错误消息:“'Collection'对象没有属性'dict'”,尽管它在__init__中实现了。
- Philipp der Rautenberg
好的,我现在用set-/getattr实现了它。请看下面的答案! - Philipp der Rautenberg

3

你是否看过traits包?似乎你在使用参数类时重新发明了轮子。Traits还具有其他功能,可能对你的应用程序类型有用(顺便说一句,我认识一个愉快地在神经模拟中使用traits的人)。


1
非常感谢。我曾经接触过traits,但并没有真正理解它们的用途。现在可能是再次了解它们的正确时机 :-)! - Philipp der Rautenberg

2
现在我使用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

有一件事情我不是很喜欢:属性不在__dict__中。如果我也将它们放在那里,我会得到一个值的副本——这可能是危险的......


那么,就不要把它们的副本放在那里。这样做也很难,因为您已经覆盖了setattr。 - nosklo
不建议使用内置类型名称作为变量名。考虑将“dict”属性名称更改为更具自说明性的名称。 - Abgan

0
我有一个类似的类,但我在集合对象中执行了以下操作:
setattr(self, par.name, par.v)

0

最终我成功地使用property()实现了类。非常感谢您的建议。我花了相当长的时间来解决它 - 但我可以向您保证,这个练习有助于更好地理解Python的面向对象编程。

我还使用__getattr__和__setattr__实现了它,但仍然不知道与property解决方案的优缺点。但这似乎值得再问一下。property解决方案似乎很干净。

所以这是代码:

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']

由于我是新手,不确定如何继续:

  • get() 被调用两次 - 为什么?
  • 面向对象设计:属性 vs. "__getattr__ & __setattr__" - 什么时候使用哪个?
  • 检查自己的答案是否被接受为 已采纳,这是否粗鲁?
  • 建议重命名标题以将相关问题或使用相同示例详细说明的问题放入相同的上下文中吗?

我提出的其他问题是为了理解 property():


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接