让类的行为就像是Python中的列表一样 [英] Let a class behave like it's a list in Python

查看:95
本文介绍了让类的行为就像是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(). Variable li 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-in get 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中打破它一段时间,但没有发现真正明显的意外行为),但是我仍然想发布关于原则上可能发生的事的演示,所以这是一个带有dictUserDict的示例:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆