4

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

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)”是不等价的。你能告诉我为什么,以及如何使它正常运行吗?

4

2 回答 2

8

在 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]
于 2012-12-01T14:27:02.603 回答
1

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

1. Pythonlist是一个对象,所以你可以扩展你的类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对象,但我有点累了:-)检查模拟容器类型和下一点,用于模拟Python 文档的序列类型的附加方法以获得完整的参考。

#!/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

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

于 2012-12-01T17:12:16.320 回答