我有一个类,它本质上是事物的集合/列表。但我想在这个列表中添加一些额外的功能。我想要的是以下内容:
- 我有一个实例
li = MyFancyList()
。每当我将变量li
用作列表时,它的行为都应该像列表一样:[e for e in li]
,li.expand(...)
,for e in li
. - 另外它应该有一些特殊的功能,如
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(...)
这样的完整列表行为>.
第一个猜测是将 list
继承到 MyFancyList
中。但这是推荐的pythonic方式吗?如果是,需要考虑什么?如果没有,有什么更好的方法?
最佳答案
如果您只想要列表行为的一部分,请使用组合(即您的实例包含对实际列表的引用)并仅实现您想要的行为所需的方法。这些方法应该将工作委托(delegate)给您的类的任何实例所引用的实际列表,例如:
def __getitem__(self, item):
return self.li[item] # delegate to li.__getitem__
单独实现 __getitem__
将为您提供数量惊人的功能,例如迭代和切片。
>>> 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
是列表数据类型的完整 Python 实现。
那为什么不直接从list
继承呢?
直接从list
(或任何其他用C 编写的内置函数)继承的一个主要问题是内置函数的代码可能会或可能不会调用用户定义的类中覆盖的特殊方法。这是 pypy docs 的相关摘录:
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 页:
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 页以上:
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
:
>>> 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__
方法,而 __init__
我们的 UserDict
方法没有。
关于python - 让一个类表现得像 Python 中的一个列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36688966/