【问题标题】:python propery: list-like objectpython属性:类似列表的对象
【发布时间】:2012-11-19 13:36:12
【问题描述】:

我正在尝试创建一个具有类似于列表的属性的对象。这就是我的意思。

class Unit:

    def __init__(self):
        self.val = 0

class Bundle:

    def __init__(self, N=3):
        self.units = [ Unit() for i in range(N) ]

    def getvals(self):
        return [ unit.val for unit in self.units ]

    def setvals(self, vals):
        for i, val in enumerate(vals):
            self.units[i].val = val

    vals = property(getvals, setvals)

现在,这个对象的行为不像预期的那样。

>>> b = Bundle()
>>> b.setvals([1,2,3])
>>> print b.vals, b.getvals()
[1, 2, 3] [1, 2, 3]
>>> b.vals = [4,5,6]
>>> print b.vals, b.getvals()
[4, 5, 6] [1, 2, 3]

所以语句“b.vals = x”和“b.setvals(x)”是不等价的。你能告诉我为什么,以及如何让它正常运行吗?

【问题讨论】:

    标签: python properties


    【解决方案1】:

    只是为了完成 Martijn Pieters answer,这可能是你想要的答案,我忍不住展示了一些你可以在 Python 中做的非常奇特的事情,当我发现它们时让我感到非常惊讶。我想你可能会觉得它们很有用,或者给你一些未来的想法。

    1。 Python list 是一个对象,因此您可以从 list 扩展您的类

    这将使您的“Bundle”对象继承内置list对象的所有方法,并允许您向其中添加新方法。

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    class Unit(object):
      def __init__(self):
        self.val = 0
      def __repr__(self):
        return "%s %s val=%s" % (type(self), hex(id(self)), self.val)
    
    class Bundle(list):
      def __init__(self, N=3):
        super(Bundle, self).__init__()
        self.extend([ Unit() for i in range(N) ])
      @property
      def vals(self):
        return [ unit.val for unit in self]
      @vals.setter
      def vals(self, vals):
        vals = vals[0:min(len(self), len(vals))] # if len(vals) > len(self), self[i] would break
        for i, val in enumerate(vals):
          self[i].val = val
    
    if __name__ == "__main__":
      bundle = Bundle()  
      print "Bundle: %s" % bundle
      newUnit = Unit()
      bundle.append(newUnit)
      print "Bundle: %s" % bundle
      bundle.vals = [1, 2, 3, 4, 5, 6]
      print "Bundle (reassigned): %s" % bundle
    

    请注意,我更改了一些属性定义,使它们成为装饰器,但基本思想保持不变。

    2。您可以覆盖某些内置方法并让一个不是list 的对象表现为list

    请注意,此代码仅用于示例目的。它有一个糟糕的设计,糟糕的 OOP 用法,它的行为会让每个人都非常困惑(即使是 Guido van Rossum ......好吧,也许对他来说不是,但我相信如果他在真正的程序)这是一段......糟糕的代码,但我认为它可以帮助理解覆盖内置方法可以做什么。此外,它缺少很多要覆盖的方法 Bundle 类表现得像一个真正的 list 对象,但我有点累 :-) 检查Emulating container types 和下一点,@ Python 文档的 987654323@ 以获取完整参考。

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    class Unit(object):
      def __init__(self):
        self.val = 0
      def __repr__(self):
        return "%s %s val=%s" % (type(self), hex(id(self)), self.val)
    
    class OtherUnit(object):
      def __init__(self):
        self.whatever = "hello"
      def __repr__(self):
        return "%s %s whatever=%s" % (type(self), hex(id(self)), self.whatever)    
    
    class Bundle(object):
      def __init__(self, N=3):
        self.units = [ Unit() for i in range(N) ]
        self.otherUnits = [ OtherUnit() for i in range(N) ]
      def __repr__(self):
        return "%s" % (self.units + self.otherUnits)
      def __len__(self):
        return len(self.units) + len(self.otherUnits)
      def __iter__(self):
        for item in (self.units + self.otherUnits):
          yield item
      def __contains__(self, value):
        if isinstance(value, Unit):
          return value in self.units
        elif isinstance(value, OtherUnit):
          return value in self.otherUnits
        elif isinstance(value, int):
          return value in [unit.val for unit in self.units]
        elif isinstance(value, str):
          return value in [otherUnit.whatever for otherUnit in self.otherUnits]
        else:
          return False
      def __getitem__(self, index):
        assert index >= 0, "Can't accept negative indexes (%s)" % indexes
        if index < len(self.units):
          return self.units[index]
        else:
          return self.otherUnits[index - len(self.units)] #Will raise index error if too big
    
      def append(self, thing):
        if isinstance(thing, Unit):
          self.units.append(thing)
        elif isinstance(thing, OtherUnit):
          self.otherUnits.append(thing)
        else:
          raise TypeError("Can't accept %s" % type(thing))
    
      @property
      def vals(self):
        return [ unit.val for unit in self.units] + [ otherUnit.whatever for otherUnit in self.otherUnits ]
      @vals.setter
      def vals(self, vals):
        insertionPointUnits = 0
        insertionPointOtherUnits = 0
        for i, val in enumerate(vals):
          if isinstance(val, int):  
        self.units[insertionPointUnits].val = val
        insertionPointUnits += 1
          elif isinstance(val, str):
        self.otherUnits[insertionPointOtherUnits].whatever = val
        insertionPointOtherUnits += 1
    
    if __name__ == "__main__":
      bundle = Bundle()  
      print "Bundle: %s" % bundle
      newUnit = Unit()
      bundle.append(newUnit)
      print "Bundle: %s" % bundle
      bundle.vals = [1, 2, "bye", 3, "how are you", 4, "doing ok"]
      print "Bundle (reassigned): %s" % bundle
      print "Bundle has %s items" % len(bundle) #Thanks to overwritting __len__
      for i, item in enumerate(bundle):
        print "bundle[%s]: %s" % (i, item) #Thanks to overwritting __iter__
      print "Does 'bundle' contain 'bye'?: %s" % ('bye'in bundle) #Thanks to overwritting __contains__
      print "Does 'bundle' contain 5?: %s" % (5 in bundle) #Thanks to overwritting __contains__
      print "Item 1 (should be Unit with val '2': %s" % bundle[1] #Thanks to overwritting __getitem__
      print "Item 5 (should be OtherUnit with val 'how are you' (4 Units + 1 OtherUnit... then ours!): %s" % bundle[5] #Thanks to overwritting __getitem__
      try:
        print "Item 9 (should raise IndexError): %s" % bundle[9]
      except IndexError, ie:
        print "Wooops: %s" % ie
    

    希望这会有所帮助。玩得开心!

    【讨论】:

      【解决方案2】:

      在 Python 2 中,property 仅适用于 新样式 对象;你的Bundle必须继承自object

      class Bundle(object):
          ...
      

      一旦您进行了更正,该属性就会按预期工作:

      >>> b.vals = [4,5,6]
      >>> b.vals
      [4, 5, 6]
      >>> b.getvals()
      [4, 5, 6]
      >>> [unit.val for unit in b.units]
      [4, 5, 6]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-08-01
        • 2014-10-24
        • 2022-06-15
        • 1970-01-01
        • 2021-04-19
        • 1970-01-01
        • 2011-09-29
        • 1970-01-01
        相关资源
        最近更新 更多