让类的行为就像是Python中的列表一样 [英] Let a class behave like it's a list in Python
问题描述
我有一个类,该类本质上是事物的集合/列表.但我想在此列表中添加一些额外的功能.我想要的是以下内容:
I have a class which is essentially a collection/list of things. But I want to add some extra functions to this list. What I would like, is the following:
- 我有一个实例
li = MyFancyList()
.每当我将变量li
用作列表时,其行为应与列表相同:[e for e in li]
,li.expand(...)
,for e in li
. - 此外,它还应具有一些特殊功能,例如
li.fancyPrint()
,li.getAMetric()
,li.getName()
.
- I have an instance
li = MyFancyList()
. Variableli
should behave as it was a list whenever I use it as a list:[e for e in li]
,li.expand(...)
,for e in li
. - Plus it should have some special functions like
li.fancyPrint()
,li.getAMetric()
,li.getName()
.
我目前使用以下方法:
class MyFancyList:
def __iter__(self):
return self.li
def fancyFunc(self):
# do something fancy
这可以用作像[e for e in li]
这样的迭代器,但是我没有像li.expand(...)
这样的完整列表行为.
This is ok for usage as iterator like [e for e in li]
, but I do not have the full list behavior like li.expand(...)
.
第一个猜测是将list
继承到MyFancyList
中.但这是推荐的pythonic方法吗?如果是,应考虑什么?如果没有,哪种方法更好?
A first guess is to inherit list
into MyFancyList
. But is that the recommended pythonic way to do? If yes, what is to consider? If no, what would be a better approach?
推荐答案
如果只希望列表行为的一部分,则使用组合(即,您的实例持有对实际列表的引用),并仅实现行为所需的方法你渴望.这些方法应将工作委派给类的任何实例引用的实际列表,例如:
If you want only part of the list behavior, use composition (i.e. your instances hold a reference to an actual list) and implement only the methods necessary for the behavior you desire. These methods should delegate the work to the actual list any instance of your class holds a reference to, for example:
def __getitem__(self, item):
return self.li[item] # delegate to li.__getitem__
单独实现__getitem__
会为您提供令人惊讶的功能,例如迭代和切片.
Implementing __getitem__
alone will give you a surprising amount of features, for example iteration and slicing.
>>> class WrappedList:
... def __init__(self, lst):
... self._lst = lst
... def __getitem__(self, item):
... return self._lst[item]
...
>>> w = WrappedList([1, 2, 3])
>>> for x in w:
... x
...
1
2
3
>>> w[1:]
[2, 3]
如果您希望列表的完整行为,请从继承collections.UserList
. UserList
是list数据类型的完整Python实现.
If you want the full behavior of a list, inherit from collections.UserList
. UserList
is a full Python implementation of the list datatype.
那为什么不直接从list
继承呢?
So why not inherit from list
directly?
直接从list
(或任何其他用C编写的内置函数)继承的一个主要问题是,内置函数的代码可能会调用也可能不会调用用户定义的类中重写的特殊方法.以下是 pypy文档的相关摘录:
One major problem with inheriting directly from list
(or any other builtin written in C) is that the code of the builtins may or may not call special methods overridden in classes defined by the user. Here's a relevant excerpt from the pypy docs:
正式地,对于隐式调用或不隐式调用内置类型的子类的确切重写方法,CPython完全没有规则.近似地,这些方法永远不会被同一对象的其他内置方法调用.例如,在dict的子类中覆盖的
__getitem__
不会被调用.内置的get
方法.
Officially, CPython has no rule at all for when exactly overridden method of subclasses of built-in types get implicitly called or not. As an approximation, these methods are never called by other built-in methods of the same object. For example, an overridden
__getitem__
in a subclass of dict will not be called by e.g. the built-inget
method.
另一句话,摘自Luciano Ramalho的 Fluent Python ,第351页:
Another quote, from Luciano Ramalho's Fluent Python, page 351:
直接将诸如dict或list或str之类的内置类型子类化是错误的- 容易发生,因为内置方法通常会忽略用户定义的方法 覆盖.不必继承内置类,而是派生您的类 来自集合的UserDict,UserList和UserString 模块,旨在易于扩展.
Subclassing built-in types like dict or list or str directly is error- prone because the built-in methods mostly ignore user-defined overrides. Instead of subclassing the built-ins, derive your classes from UserDict , UserList and UserString from the collections module, which are designed to be easily extended.
...以及更多,第370+页:
... and more, page 370+:
行为不当:错误或功能? 内置的dict,list和str类型是Python本身的基本构建块,因此 它们必须很快-它们中的任何性能问题都会严重影响 其他的一切.这就是CPython采用导致其内置的快捷方式的原因 不配合子类重写的方法而导致行为异常的方法.
Misbehaving built-ins: bug or feature? The built-in dict , list and str types are essential building blocks of Python itself, so they must be fast — any performance issues in them would severely impact pretty much everything else. That’s why CPython adopted the shortcuts that cause their built-in methods to misbehave by not cooperating with methods overridden by subclasses.
经过一番尝试后,内置的list
问题似乎并不那么关键(我试图在Python 3.4中打破它一段时间,但没有发现真正明显的意外行为),但是我仍然想发布关于原则上可能发生的事的演示,所以这是一个带有dict
和UserDict
的示例:
After playing around a bit, the issues with the list
builtin seem to be less critical (I tried to break it in Python 3.4 for a while but did not find a really obvious unexpected behavior), but I still wanted to post a demonstration of what can happen in principle, so here's one with a dict
and a UserDict
:
>>> class MyDict(dict):
... def __setitem__(self, key, value):
... super().__setitem__(key, [value])
...
>>> d = MyDict(a=1)
>>> d
{'a': 1}
>>> class MyUserDict(UserDict):
... def __setitem__(self, key, value):
... super().__setitem__(key, [value])
...
>>> m = MyUserDict(a=1)
>>> m
{'a': [1]}
如您所见,dict
中的__init__
方法忽略了被覆盖的__setitem__
方法,而我们UserDict
中的__init__
方法则没有.
As you can see, the __init__
method from dict
ignored the overridden __setitem__
method, while the __init__
method from our UserDict
did not.
这篇关于让类的行为就像是Python中的列表一样的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!