【问题标题】:How to inherit and extend a list object in Python?如何在 Python 中继承和扩展列表对象?
【发布时间】:2010-11-04 00:54:56
【问题描述】:

我对使用 python 列表对象感兴趣,但功能略有改变。特别是,我希望列表为 1 索引而不是 0 索引。例如:

>> mylist = MyList()
>> mylist.extend([1,2,3,4,5])
>> print mylist[1]

输出应该是:1

但是当我更改 __getitem__()__setitem__() 方法来执行此操作时,我收到了 RuntimeError: maximum recursion depth exceeded 错误。我对这些方法进行了很多修改,但这基本上就是我所拥有的:

class MyList(list):
    def __getitem__(self, key):
        return self[key-1]
    def __setitem__(self, key, item):
        self[key-1] = item

我猜问题是self[key-1] 本身调用了它定义的相同方法。如果是这样,我如何使它使用list() 方法而不是MyList() 方法?我尝试使用super[key-1] 而不是self[key-1] 但这导致投诉TypeError: 'type' object is unsubscriptable

有什么想法吗?另外,如果您能指出一个很好的教程,那就太好了!

谢谢!

【问题讨论】:

  • 这明显违反了里氏替换原则。如果您实际上不能在任何需要list 的地方实际使用它,那么子类化list 可能没有太多价值。在这种情况下,组合可能是更合适的策略?
  • 不太明白; “组成”是什么意思?另外,我们如何确定不能用 MyList 代替标准列表?有什么好的方法可以告诉吗?比如内部方法使用__getitem____setitem__会不会有问题?
  • mindthief: en.wikipedia.org/wiki/Composition_over_inheritance -- 如果您更改 list 的现有界面(即,正是您在这里要求的),那么您不能用 MyList 代替list。即使像x[0] 这样的基本内容也不起作用。

标签: python list inheritance


【解决方案1】:

使用super()函数调用基类的方法,或者直接调用方法:

class MyList(list):
    def __getitem__(self, key):
        return list.__getitem__(self, key-1)

class MyList(list):
    def __getitem__(self, key):
        return super(MyList, self).__getitem__(key-1)

但是,这不会改变其他列表方法的行为。例如,索引保持不变,这可能会导致意想不到的结果:

numbers = MyList()
numbers.append("one")
numbers.append("two")

print numbers.index('one')
>>> 1

print numbers[numbers.index('one')]
>>> 'two'

【讨论】:

  • 另外,请注意其他列表方法的破坏,因为您正在更改作为列表对象基本部分的索引行为。
  • 如果你注意到递归发生的原因,我会 +1。你给出了解决方案而不解释问题。
  • 问题中完美解释了为什么会发生递归。
  • 这里要指出的另一个问题是:您认为 super(MyList, self)[key-1] 会起作用,但事实并非如此。 super() 明确地不适用于任何“隐式查找”,例如 [] 而不是 getitem
  • “提防其他列表方法破坏”这些方法不应该在内部使用__getitem____setitem__ 吗?
【解决方案2】:

取而代之的是,使用相同的方法将所有数字定义为从您设置的数字中减去 1 的整数子类。瞧。

对不起,我不得不这样做。这就像微软将黑暗定义为标准的笑话。

【讨论】:

  • 微软将黑暗定义为标准的笑话是什么? 编辑:nvm,found it“问:换一个灯泡需要多少微软硬件工程师?答:没有,他们将黑暗重新定义为行业标准”
【解决方案3】:

您可以通过创建一个继承自抽象类collections.MutableSequence 的类来避免违反Liskov Substitution principle。它看起来像这样:

def indexing_decorator(func):
    def decorated(self, index, *args):
        if index == 0:
            raise IndexError('Indices start from 1')
        elif index > 0:
            index -= 1
        return func(self, index, *args)
    return decorated


class MyList(collections.MutableSequence):
    def __init__(self):
        self._inner_list = list()

    def __len__(self):
        return len(self._inner_list)

    @indexing_decorator
    def __delitem__(self, index):
        self._inner_list.__delitem__(index)

    @indexing_decorator
    def insert(self, index, value):
        self._inner_list.insert(index, value)

    @indexing_decorator
    def __setitem__(self, index, value):
        self._inner_list.__setitem__(index, value)

    @indexing_decorator
    def __getitem__(self, index):
        return self._inner_list.__getitem__(index)

    def append(self, value):
        self.insert(len(self) + 1, value)

【讨论】:

  • x = MyList(); x.append(4); y = MyList(); print(y) 试试看。你会得到有趣的结果...
  • 快到了。您不应调整insert 的索引。另外,尝试处理 index == 0 的情况。
  • 是的,你是对的。但是,索引将不一致。我稍微编辑了答案中的代码。我认为它现在应该可以正常工作了。
  • 如果继承自collections.UserList会违反Liskov替换原则吗?例如类 MyList(collections.UserList):
【解决方案4】:
class ListExt(list):
    def extendX(self, l):
        if l:
            self.extend(l)

【讨论】:

    猜你喜欢
    • 2014-11-28
    • 1970-01-01
    • 2014-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-16
    • 1970-01-01
    • 2017-10-30
    相关资源
    最近更新 更多