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

cha*_*ver 13 python inheritance list

我想创建一个只能接受某些类型的列表.因此,我试图从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)
Run Code Online (Sandbox Code Playgroud)

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

我该怎么做呢?

Ale*_*lli 47

我想创建一个只能接受某些类型的列表.因此,我试图从Python中的列表继承

不是最好的方法!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)
Run Code Online (Sandbox Code Playgroud)

oktypes说法通常是要允许类型的元组,但它的确定有通过单一类型的,当然(和,通过使一种类型的抽象基类,ABC,你可以轻松地执行任何种类的类型检查的您选择的方式 - 但是,这是一个不同的问题).

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

x = TypedList((str, unicode), 'foo', 'bar')
x.append('zap')
print x
x.append(23)
Run Code Online (Sandbox Code Playgroud)

输出是:

['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
Run Code Online (Sandbox Code Playgroud)

特别注意我们没有覆盖append- 但是追加是存在的并且行为与预期一样.

在回溯中揭示了这一点魔法背后的不那么秘密:( _abcoll.py模块中抽象基类的实现collections模块),在第556行,通过调用我们实现追加insert- 当然,我们已经正确覆盖.

这种"模板方法设计模式"(对于各种OOP来说绝对珍贵 - 在youtube上寻找我对设计模式的讨论,你会发现原因;-),以及其他优点,给我们"阻塞点效应"我前面提到过:通过在必须实现的极少数方法上添加一些检查,你可以获得这些检查适用于_all__其他相关方法的优点(Python中的可变序列有很多这些;-).

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

Scott Meyers的优秀"Effective C++",第33项,更强烈地说:使非叶类抽象化.因为"非叶子"他的意思是"任何曾经继承过的阶级",所以相同的措词将是"永远不会从具体阶级继承".

当然,Scott正在用C++语言写作,但是Paul Haahr对Java给出了完全相同的建议,表达为不要具体类的子类 - 我通常将它用于Python,尽管我赞成四人组合更柔和的措辞,更喜欢构成而不是(具体阶级)继承(但我明白斯科特和保罗都经常为观众写作,需要非常直接和强烈措辞的建议,几乎被称为"诫命"而不是建议,而不是更柔和的措辞他们可能太容易忽视他们的便利;-).


Man*_*dan 17

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

一些建议:不要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'>
Run Code Online (Sandbox Code Playgroud)


Tim*_*ski 6

虽然我喜欢@Alex Martini 的答案,但不幸的是,它对于大多数情况来说并不实用。在标准库和其他库代码中,有太多的东西期望列表类型继承自list. 除非您为您的类型编写自己的序列化方法,否则 您甚至不能不json.dumps()继承。list

我不同意@msw,因为你可能想在库中公开类似的东西,而无法控制其他代码。

不幸的是,该类list不允许您像 Alex 的示例中那样重写单个函数。以下内容覆盖可能添加到列表中的所有函数(有关所有列表函数,请参阅此处: https: //docs.python.org/2/library/stdtypes.html#mutable-sequence-types)。

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 ])
Run Code Online (Sandbox Code Playgroud)


Gab*_*aru 5

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