我想创建一个只能接受某些类型的列表。因此,我试图从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)之外还要做什么。
我应该怎样做呢?
答案 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.py
(collections
模块中抽象基类的实现模块),第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