来自@Ignacio Vazquez-Abrams 的当前accepted answer 就足够了。但是,对这个问题感兴趣的其他人可能想要考虑从abstract base class (ABC) 继承他们的类(例如在standard module collections.abc 中找到的类)。这做了很多事情(there are probably others as well):
- 确保提供“像____一样”对待对象所需的所有方法
- 它是自我记录的,因为阅读您的代码的人能够立即知道您希望您的对象“像____一样”。
- 允许
isinstance(myobject,SomeABC) 正常工作。
- 通常会自动提供方法,因此我们不必自己定义它们
(请注意,除了上述之外,创建自己的ABC 可以让您测试任何对象中是否存在特定方法或方法集,并基于此将该对象声明为ABC 的子类,即使该对象不直接继承自ABC。See this answer for more information.)
示例:使用ABC 实现一个只读的、类似list 的类
现在作为一个例子,让我们为原始问题中的类选择并实现一个ABC。有两个要求:
- 类是可迭代的
- 按索引访问类
显然,这个类将是某种集合。所以我们要做的是查看我们的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__、index 和count。如果您考虑一下,所有的东西应该包含在任何索引对象中。如果您忘记包含它们,您的代码的用户(可能包括您自己!)可能会非常恼火(我知道我会的)。
但是,还有第二个 ABC 也提供了这种功能组合(可迭代,并可由 [] 访问):Mapping。我们要使用哪一个?
我们记得要求是能够按索引(如list 或tuple)访问对象,即不 按键(如dict)。因此,我们选择Sequence 而不是Mapping。
侧边栏:请务必注意 Sequence 是只读的(Mapping 也是如此),因此它不允许我们执行 myobject[i] = value 或 random.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!