【问题标题】:How to implement property() with dynamic name (in python)如何使用动态名称实现属性()(在python中)
【发布时间】:2010-09-29 14:27:38
【问题描述】:

我正在为单个神经元编写模拟程序。因此我必须处理很多参数。现在的想法是我有两个类,一个用于 SingleParameter 和一个参数集合。我使用 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(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() 提出的其他问题:

【问题讨论】:

    标签: python oop parameters properties


    【解决方案1】:

    我有一个做类似事情的类,但我在集合对象中做了以下操作:

    setattr(self, par.name, par.v)

    【讨论】:

      【解决方案2】:

      查看内置函数getattrsetattr。你可能会更快乐。

      【讨论】:

        【解决方案3】:

        对两个类使用相同的 get/set 函数会迫使您对参数列表进行丑陋的破解。非常粗略,这就是我的做法:

        在 SingleParameter 类中,像往常一样定义 get 和 set:

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

        在类 Collection 中,在创建属性之前您无法知道信息,因此您定义了 metaset/metaget 函数并稍后使用 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))
        

        【讨论】:

          【解决方案4】:

          最后我成功地用 property() 实现了这些类。非常感谢您的建议。我花了很多时间来解决这个问题 - 但我可以向你保证,这个练习可以帮助你更好地理解 python OOP。

          我也用 __getattr__ 和 __setattr__ 实现了它,但仍然不知道属性解决方案的优缺点。但这似乎值得另一个问题。属性解决方案似乎很干净。

          代码如下:

          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() 似乎被调用了两次 - 为什么?
          • oop-design:属性与“__getattr__ & __setattr__” - 我应该什么时候使用什么?
          • 检查自己对自己问题的回答是否为已接受是不礼貌的吗?
          • 是否建议重命名标题,以便将相关问题或用相同示例阐述的问题放在相同的上下文中?

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

          【讨论】:

            【解决方案5】:

            现在我用 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。
            • 不建议对变量使用内置类型名称。考虑将“dict”属性名称更改为更易于解释的名称。
            【解决方案6】:

            你看过traits package吗?您似乎正在使用参数类重新发明轮子。 Traits 还具有可能对您的应用程序类型有用的附加功能(顺便说一下,我认识一个人乐于在神经模拟中使用 Traits)。

            【讨论】:

            • 非常感谢。我曾经遇到过特质,但并没有真正了解它们的好处。可能现在是再看看的合适时机:-)!
            【解决方案7】:

            属性旨在动态评估属性或将它们设为只读。您需要的是自定义属性访问。 __getattr____setattr__ 做得很好,如果 __getattr__ 还不够,还有 __getattribute__

            详情请见Python docs on customizing attribute access

            【讨论】:

            • 当我添加: ----- def setattr__(self, name, value): self.dict[name] = value ---- 我收到错误消息“' Collection' 对象没有属性 'dict'" - 尽管它是在 __init 中实现的。
            • 好的,我现在用 set-/getattr 实现了它。请看下面的答案!
            猜你喜欢
            • 2018-04-16
            • 2013-01-29
            • 1970-01-01
            • 1970-01-01
            • 2018-05-22
            • 1970-01-01
            • 1970-01-01
            • 2019-09-05
            • 2017-08-01
            相关资源
            最近更新 更多