【问题标题】:Python class accessible by iterator and index迭代器和索引可访问的 Python 类
【发布时间】:2011-07-18 14:19:31
【问题描述】:

可能是一个 n00b 问题,但我目前有一个实现迭代器的类,所以我可以做类似的事情

for i in class():

但我希望能够通过索引访问类,就像

class()[1]

我该怎么做?

谢谢!

【问题讨论】:

  • 附注数据当然包含一个列表属性,我可以做 class().list[1] 但我可以做 class()[1] 吗?
  • 无关紧要,但如果你像这样使用class,你肯定会出错。

标签: python list iterator


【解决方案1】:

实现__iter__()__getitem__() 等方法。

【讨论】:

  • 你能提供一个如何实现这些的例子吗?
【解决方案2】:

来自@Ignacio Vazquez-Abrams 的当前accepted answer 就足够了。但是,对这个问题感兴趣的其他人可能想要考虑从abstract base class (ABC) 继承他们的类(例如在standard module collections.abc 中找到的类)。这做了很多事情(there are probably others as well):

  • 确保提供“像____一样”对待对象所需的所有方法
  • 它是自我记录的,因为阅读您的代码的人能够立即知道您希望您的对象“像____一样”。
  • 允许isinstance(myobject,SomeABC) 正常工作。
  • 通常会自动提供方法,因此我们不必自己定义它们

(请注意,除了上述之外,创建自己的ABC 可以让您测试任何对象中是否存在特定方法或方法集,并基于此将该对象声明为ABC 的子类,即使该对象不直接继承自ABCSee this answer for more information.)


示例:使用ABC 实现一个只读的、类似list 的类

现在作为一个例子,让我们为原始问题中的类选择并实现一个ABC。有两个要求:

  1. 类是可迭代的
  2. 按索引访问类

显然,这个类将是某种集合。所以我们要做的是查看我们的menu of collection ABC's 以找到合适的ABC(注意还有numeric ABCs)。适当的ABC 取决于我们希望在类中使用哪些抽象方法。

如果我们想使用 __iter__() 方法,我们会看到 Iterable 是我们所追求的,这是我们执行 for o in myobject: 这样的事情所需要的。但是,Iterable 不包括方法__getitem__(),这是我们执行myobject[i] 之类的操作所需要的。所以我们需要使用不同的ABC

在抽象基类的collections.abc 菜单下,我们看到Sequence 是提供我们所需功能的最简单的ABC。而且 - 你会看吗 - 我们将 Iterable 功能作为一种混合方法 - 这意味着我们不必自己定义它 - 免费!我们还得到__contains____reversed__indexcount。如果您考虑一下,所有的东西应该包含在任何索引对象中。如果您忘记包含它们,您的代码的用户(可能包括您自己!)可能会非常恼火(我知道我会的)。

但是,还有第二个 ABC 也提供了这种功能组合(可迭代,并可由 [] 访问):Mapping。我们要使用哪一个?

我们记得要求是能够按索引(如listtuple)访问对象,即 按键(如dict)。因此,我们选择Sequence 而不是Mapping


侧边栏:请务必注意 Sequence 是只读的(Mapping 也是如此),因此它不允许我们执行 myobject[i] = valuerandom.shuffle(myobject) 之类的操作。如果我们想要做这样的事情,我们需要继续向下ABCs 的菜单并使用MutableSequence(或MutableMapping),这将需要实现几个额外的方法。


示例代码

现在我们可以开始上课了。我们定义它,并让它继承自Sequence

from collections.abc import Sequence

class MyClass(Sequence):
    pass

如果我们尝试使用它,解释器会告诉我们需要实现哪些方法才能使用它(注意这些方法也列在 Python 文档页面上):

>>> myobject = MyClass()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyClass with abstract methods __getitem__, __len__

这告诉我们,如果我们继续实现__getitem____len__,我们将能够使用我们的新类。我们可能会在 Python 3 中这样做:

from collections.abc import Sequence

class MyClass(Sequence):
    def __init__(self,L):
        self.L = L
        super().__init__()
    def __getitem__(self, i):
        return self.L[i]
    def __len__(self):
        return len(self.L)

# Let's test it:
myobject = MyClass([1,2,3])
try:
    for idx,_ in enumerate(myobject):
        print(myobject[idx])
except Exception:
    print("Gah! No good!")
    raise
# No Errors!

It works!

【讨论】:

  • 你会发现 Internetz 上的一些 Pythonistas 似乎不喜欢将抽象类和方法作为语言的一部分的想法(为了总结/模仿他们的推理,Python 应该是“不是Java!”)。但是,它们似乎非常有用,所以我很高兴它们在那里。
  • 另外:请务必查看abc module as well,它允许您创建自己的 ABC。
  • 这似乎是 Python 3 特有的。
  • 感谢您指出这一点,我只是希望实现索引,但我想我会继续使用 ABC 代替。非常方便!
  • 抽象基类在 python 3 中从 collections 移动到 collections.abc。在 python 2 中只需使用 from collections import Sequence
猜你喜欢
  • 2021-07-03
  • 2016-08-25
  • 2012-03-19
  • 2021-07-18
  • 2018-04-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-11
  • 2018-06-18
相关资源
最近更新 更多