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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3487434/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 11:19:58  来源:igfitidea点击:

Overriding append method after inheriting from a Python List

pythonlistinheritance

提问by chaindriver

I want to create a list that can only accept certain types. As such, I'm trying to inherit from a list in Python, and overriding the append() method like so:

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

This will of cause an infinite loop because the body of append() calls itself, but I'm not sure what to do other than using self.append(item).

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

How I should go about doing this?

我该怎么做呢?

采纳答案by Manoj Govindan

I have made some changes to your class. This seems to be working.

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

A couple of suggestions: don't use typeas a keyword - typeis a built in function. Python instance variables are accessed using the self.prefix. So use self.<variable name>.

一些建议:不要type用作关键字 -type是内置函数。Python 实例变量使用self.前缀访问。所以使用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'>

回答by Gabi Purcaru

instead of self.append(item)use super(TypedList, self).append(item)(see http://docs.python.org/library/functions.html#super)

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

回答by msw

By not doing it in the first place.

一开始就没有这样做。

If you don't want something of type X in your list, why are you putting it there?

如果你不想在你的列表中出现 X 类型的东西,你为什么要把它放在那里?

This is not a sarcastic response. Adding type restrictions as you are trying is either unnecessary or affirmatively counter-productive. It is however, a common request from people coming from a language background which has strict compile-time type checking.

这不是讽刺的回应。在您尝试时添加类型限制是不必要的,或者肯定会适得其反。然而,这是来自具有严格编译时类型检查的语言背景的人们的常见要求。

For the same reason you wouldn't attempt 'a string' / 2.0, you have the same control over what gets put in a list. Since a list will happily contain heterogeneous types, at best TypedListwill move a run-time TypeError from the point where you use the item forward in time to where you append it to the list. Given Python's duck-typingexplicitly checking isinstanceprecludes later expansion of the list to contain non-typeinstances while providing no benefit.

出于同样的原因,您不会尝试'a string' / 2.0,您对放入列表的内容具有相同的控制权。由于列表会很高兴地包含异构类型,因此充其量TypedList会将运行时 TypeError 从您使用该项目的时间点向前移动到您将其附加到列表的位置。鉴于 Python 的鸭子类型明确检查isinstance排除了列表的后续扩展以包含非type实例,而没有提供任何好处。

added OrderedDict information:

添加了 OrderedDict 信息

Per request in comment, assuming Python 2.7 or greater collections.OrderedDictwill do it. Per that documentation page, given 2.4 or greater you have to add one.

每个评论中的请求,假设 Python 2.7 或更高版本collections.OrderedDict会这样做。根据该文档页面,给定 2.4 或更高版本,您必须添加一个.

回答by Alex Martelli

I want to create a list that can only accept certain types. As such, I'm trying to inherit from a list in Python

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

Not the best approach! Python lists have so many mutating methods that you'd have to be overriding a bunch (and would probably forget some).

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

Rather, wrapa list, inherit from collections.MutableSequence, and add your checks at the very few "choke point" methods on which MutableSequencerelies to implement all others.

相反,包装一个列表,从 继承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)

The oktypesargument is normally a tuple of types that you want to allow, but it's OK to pass a single type there of course (and, by making that one type an abstract base class, ABC, you can easily perform any kind of type-checking of your choice that way -- but, that's a different issue).

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

Here's some example code using this class:

下面是一些使用这个类的示例代码:

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

the output is:

输出是:

['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

Note in particular that we have notoverridden append-- yet append is there and behaves just as expected.

特别要注意的是,我们没有覆盖append——但是 append 存在并且表现得和预期的一样。

The not-so-secret behind that bit of magic is revealed in the traceback: _abcoll.py(the implementation module for the abstract base classes in the collectionsmodule), at line 556, implementsappend by calling our insert-- which we have, of course, properly overridden.

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

This "template method design pattern" (absolutely precious for all kinds of OOP -- look for my talks on design patterns on youtube and you'll find out why;-), among other advantages, gives us the "choke point effect" I mentioned earlier: by adding some checks at a very few methods that you must implement, you gain the advantage that those checks apply to _all__ the other relevant methods (and mutable sequences in Python have a lot of those;-).

这种“模板方法设计模式”(对于各种 OOP 绝对珍贵——在 youtube 上寻找我关于设计模式的演讲,你会发现原因;-),除其他优点外,还为我们提供了“瓶颈效应”我前面提到过:通过在必须实现的极少数方法中添加一些检查,您可以获得优势,即这些检查适用于 _all__ 其他相关方法(Python 中的可变序列有很多;-)。

It's no surprise that we end up with a very powerful and classic design pattern "behind the scenes", because the whole idea behind this implementation strategy comes right out of the immortal classic "Design Patterns" book (whose authors are often collectively referred to as the gang of four";-): prefer object composition over inheritance. Inheritance (from concrete classes) is a very rigid coupling mechanism, full of "gotchas" as soon as you're trying to use it to do anything even just slightly outside its rigorous limits; composition is extremely flexible and useful, and inheritance from appropriate abstractclasses can complete the picture very nicely.

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

Scott Meyers' excellent "Effective C++", item 33, puts it even more strongly: make non-leaf classes abstract. Since by "non-leaf" he means "any class that's ever inherited from", an equivalent phrasing would be "never inherit from a concrete class".

Scott Meyers 出色的“Effective C++”,第33 条,更强烈地强调:使非叶类抽象。由于“非叶”的意思是“任何曾经继承自的类”,因此等效的措辞是“永远不会从具体类继承”。

Scott is writing in a C++ context, of course, but Paul Haahrgives exactly the same advice for Java, phrased as Don't subclass concrete classes-- and I generally second it for Python, though I do favor the gang-of-four's softer phrasing, prefercomposition over (concrete class) inheritance (but I understand that both Scott and Paul are often writing for an audience which needs very direct and strongly phrased advice, almost phrased as "commandments" rather than advice, not softer phrased one that they might too easily ignore in the name of their convenience;-).

当然,Scott 是在 C++ 上下文中编写的,但Paul Haahr对 Java 给出了完全相同的建议,措辞为不要对具体类进行子类化——我通常支持 Python,尽管我确实支持四人组更柔和的措辞,更喜欢组合而不是(具体类)继承(但我知道斯科特和保罗经常为需要非常直接和措辞强烈的建议的观众写作,几乎被称为“诫命”而不是建议,而不是更温和的措辞他们可能很容易以方便的名义忽略;-)。

回答by Valar

You can also use build-in arrayclass. It works only with numeric types, but is probably best solution for such cases. It is optimized to minimize memory usage.

您还可以使用内置数组类。它仅适用于数字类型,但可能是此类情况的最佳解决方案。它经过优化以最大限度地减少内存使用。

Example:

例子:

from array import array
array('c', 'hello world')     # char array
array('u', u'hello \u2641')   # unicode array
array('l', [1, 2, 3, 4, 5])   # long array
array('d', [1.0, 2.0, 3.14])  # double array

You can perform the same operations as with normal list:

您可以执行与普通列表相同的操作:

chars = array('c')            
chars.extend('foo')

But when you try to insert other type then specified, an exception is raised:

但是当您尝试插入其他类型然后指定时,会引发异常:

>>> chars.extend([5,10])
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
TypeError: array item must be char  

For all available types look here

对于所有可用的类型,请看这里

回答by Tim Ludwinski

Although I like @Alex Martini's answer, it's unfortunately not practical for most situations. There are WAY too many things both in the standard library and in other library code that expect list types to inherit from list. You can't even do json.dumps()without inheriting from listunless you write your own serialize method for your type.

尽管我喜欢@Alex Martini 的回答,但不幸的是,它在大多数情况下都不实用。在标准库和其他库代码中,有太多东西希望列表类型从list. 除非您为您的类型编写自己的序列化方法,否则您甚至json.dumps()不能不继承 from list

I would disagree with @msw because you might want to expose something like this in a library and not have control over the other code.

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

The listclass unfortunately doesn't let you override a single function like in Alex's example. The following overrides all functions that could potentially add to the list (see here for all list functions: https://docs.python.org/2/library/stdtypes.html#mutable-sequence-types).

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 ])