从Python列表继承后覆盖append方法

时间:2010-08-15 12:54:39

标签: python list inheritance

我想创建一个只能接受某些类型的列表。因此,我试图从Python中的列表继承,并重写append()方法,如下所示:

class TypedList(list):
    def __init__(self, type):
        self.type = type

    def append(item)
        if not isinstance(item, type):
            raise TypeError, 'item is not of type %s' % type
        self.append(item)  #append the item to itself (the list)

这会导致无限循环,因为append()的主体会自行调用,但我不知道除了使用self.append(item)之外还要做什么。

我应该怎样做呢?

5 个答案:

答案 0 :(得分:45)

  

我想创建一个只能的列表   接受某些类型。就这样,我   试图从列表中继承   蟒

不是最好的方法! Python列表有很多变异方法,你必须覆盖一堆(并且可能会忘记一些)。

相反,换行一个列表,继承自collections.MutableSequence,并在MutableSequence依赖于实现所有其他方法的极少数“阻塞点”方法中添加您的检查。

import collections

class TypedList(collections.MutableSequence):

    def __init__(self, oktypes, *args):
        self.oktypes = oktypes
        self.list = list()
        self.extend(list(args))

    def check(self, v):
        if not isinstance(v, self.oktypes):
            raise TypeError, v

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

    def __getitem__(self, i): return self.list[i]

    def __delitem__(self, i): del self.list[i]

    def __setitem__(self, i, v):
        self.check(v)
        self.list[i] = v

    def insert(self, i, v):
        self.check(v)
        self.list.insert(i, v)

    def __str__(self):
        return str(self.list)

oktypes参数通常是你想要允许的类型元组,但是当然可以传递一种类型(并且,通过使一种类型成为抽象基类,ABC,你可以可以轻松地对您选择的任何类型进行类型检查 - 但是,这是一个不同的问题。)

以下是使用此类的一些示例代码:

x = TypedList((str, unicode), 'foo', 'bar')
x.append('zap')
print x
x.append(23)

输出是:

['foo', 'bar', 'zap']
Traceback (most recent call last):
  File "tl.py", line 35, in <module>
    x.append(23)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_abcoll.py", line 556, in append
    self.insert(len(self), value)
  File "tl.py", line 25, in insert
    self.check(v)
  File "tl.py", line 12, in check
    raise TypeError, v
TypeError: 23

请特别注意我们已经重写了append - 但是追加是并且行为与预期一致。

在追溯中显示了这一点魔法背后的不那么秘密:_abcoll.pycollections模块中抽象基类的实现模块),第556行, implements 通过调用我们拥有的insert追加,当然,正确覆盖。

这种“模板方法设计模式”(对于各种OOP绝对珍贵 - 在youtube上寻找我对设计模式的讨论,你会发现原因;-),除了其他优点之外,还给我们带来了“阻塞点”效果“我之前提到:通过在必须实现的极少数方法上添加一些检查,您可以获得这些检查适用于_all__其他相关方法的优势(Python中的可变序列有很多这些方法; - )。< / p>

毫不奇怪,我们最终得到了一个非常强大和经典的“幕后”设计模式,因为这个实施策略背后的整个理念来自不朽的经典“设计模式”一书(其作者通常是集体的)被称为四人组“;-):更喜欢对象组合而非继承。继承(来自具体类)是一种非常严格的耦合机制,一旦你尝试就会充满”陷阱“使用它做任何事情,甚至只是略微超出其严格的限制;组合非常灵活和有用,并且从适当的抽象类继承可以非常好地完成图片。

Scott Meyers的优秀“有效C ++”,item 33,更强烈地表达了这一点:使非叶类抽象。因为“非叶子”他的意思是“任何曾经继承过的阶级”,所以相同的措辞将“永远不会从具体阶级继承”。

当然,斯科特正在用C ++语言写作,但是Paul Haahr给出了完全相同的Java建议,用不要具体类 - 并且我通常将它排在第二位对于Python来说,虽然我赞成四人组的更柔和的措辞,更喜欢构成而不是(具体的类)继承(但我知道Scott和Paul经常为需要非常直接的观众写作和强烈措辞的建议,几乎被称为“诫命”而非建议,而不是更柔和的措辞,他们可能很容易忽视他们的方便; - )。

答案 1 :(得分:16)

我对你的班级做了一些改动。这似乎有效。

一些建议:不要将type用作关键字 - type是内置函数。使用self.前缀访问Python实例变量。因此,请使用self.<variable name>

class TypedList(list):
    def __init__(self, type):
        self.type = type

    def append(self, item):
        if not isinstance(item, self.type):
            raise TypeError, 'item is not of type %s' % self.type
        super(TypedList, self).append(item)  #append the item to itself (the list)

from types import *
tl = TypedList(StringType)
tl.append('abc')
tl.append(None)
Traceback (most recent call last):
  File "<pyshell#25>", line 1, in <module>
    tl.append(None)
  File "<pyshell#22>", line 7, in append
    raise TypeError, 'item is not of type %s' % self.type
TypeError: item is not of type <type 'str'>

答案 2 :(得分:3)

而不是self.append(item)使用super(TypedList, self).append(item)(请参阅http://docs.python.org/library/functions.html#super

答案 3 :(得分:1)

首先不要这样做。

如果您不想在列表中添加X类型的东西,为什么要将它放在那里?

这不是一个讽刺性的回应。在尝试时添加类型限制要么是不必要的,要么是肯定会适得其反。然而,来自具有严格编译时类型检查的语言背景的人的共同请求。

出于同样的原因,你不会尝试'a string' / 2.0,你对列入的内容有同样的控制权。由于列表将很乐意包含异构类型,因此最好TypedList将运行时TypeError从您将项目及时转发到将其追加到列表的位置移动。鉴于Python的duck-typing显式检查isinstance会阻止以后扩展列表以包含非type实例,同时不提供任何好处。

添加了OrderedDict信息

评论中的每个请求,假设Python 2.7或更高版本collections.OrderedDict将执行此操作。根据该文档页面,给定2.4或更高版本have to add one

答案 4 :(得分:1)

虽然我喜欢@Alex Martini的回答,但遗憾的是,在大多数情况下它并不实用。标准库和其他库代码中有太多东西需要列表类型从list继承。除非您为自己的类型编写自己的序列化方法,否则除非继承json.dumps(),否则您甚至无法list执行list

我不同意@msw,因为您可能希望在库中公开这样的内容而不能控制其他代码。

遗憾的是,import collections class CheckedList(list, collections.MutableSequence): def __init__(self, check_method, iterator_arg=None): self.__check_method = check_method if not iterator_arg is None: self.extend(iterator_arg) # This validates the arguments... def insert(self, i, v): return super(CheckedList, self).insert(i, self.__check_method(v)) def append(self, v): return super(CheckedList, self).append(self.__check_method(v)) def extend(self, t): return super(CheckedList, self).extend([ self.__check_method(v) for v in t ]) def __add__(self, t): # This is for something like `CheckedList(validator, [1, 2, 3]) + list([4, 5, 6])`... return super(CheckedList, self).__add__([ self.__check_method(v) for v in t ]) def __iadd__(self, t): # This is for something like `l = CheckedList(validator); l += [1, 2, 3]` return super(CheckedList, self).__iadd__([ self.__check_method(v) for v in t ]) def __setitem__(self, i, v): if isinstance(i, slice): return super(CheckedList, self).__setitem__(i, [ self.__check_method(v1) for v1 in v ]) # Extended slice... else: return super(CheckedList, self).__setitem__(i, self.__check_method(v)) def __setslice__(self, i, j, t): # NOTE: extended slices use __setitem__, passing in a tuple for i return super(CheckedList, self).__setslice__(i, j, [ self.__check_method(v) for v in t ]) 课程并不允许您覆盖像Alex的示例中的单个函数。以下内容将覆盖可能添加到列表中的所有函数(有关所有列表函数,请参见此处:https://docs.python.org/2/library/stdtypes.html#mutable-sequence-types)。

$ JEKYLL_ENV=production jekyll build