虚拟子类的用途是什么?

25
class AnimalMeta(type):
    def __instancecheck__(cls, instance):
        return cls.__subclasscheck__(type(instance))

    def __subclasscheck__(cls, sub):
        return (hasattr(sub, 'eat') and callable(sub.eat) and
                hasattr(sub, 'sleep') and callable(sub.sleep))


class Animal(object):
    __metaclass__ = AnimalMeta
    pass


class Dog(object):
    def eat(self):
        print "eat"
    def sleep(self):
        print "sleep"


dog = Dog()
dog.eat()

print isinstance(dog, Animal)
print issubclass(dog, Animal)

输出:

eat
True
True

我正在尝试理解Python虚拟子类,如上所示的示例。实例中,虚拟子类不需要实现抽象方法。

虚拟子类的真正用途是什么?在我看来,虚拟子类的作用类似于鸭子类型和对象继承之间的某种东西。

鸭子类型--虚拟子类--对象继承


谢谢,请转成文字。 - weiwang
这个概念类似于Java的接口,但更加灵活,主要设计用于抽象基类(参见stdlib中的ABC模块)和/或类型提示注释。 - bruno desthuilliers
1个回答

45
我阅读了 Python中的接口:协议和抽象基类,对此有了更好的理解。在Python中我们有鸭子类型:

如果它走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子。

然而,BirdAeroplane 都可以使用 fly() 方法,但它们并不相同。因此,我们需要定义一个接口来区分它们(Python没有interface关键字,所以实际上我们使用的是抽象类)。
让我举个例子:
在我们的程序中有一个 DuckMyPlane。它们都实现了 fly() 方法。现在我们想从机库选择一架飞机,带一些人上飞机飞到另一个城市。显然,我们不能把人放到一只鸭子上,所以我们定义了一个叫做(实际上是一个抽象类)Plane 的接口。然后让 MyPlane 继承自 Plane

一切都运转良好。当我们想要选择一架飞机时,我们会检查它是否是Plane的子类。然而,波音公司开发了一个包,其中有一个Boeing747Plane。我们购买了这个飞机(from boeing_airplanes import Boeing747Plane),但它不被认为是一架飞机。它确实有一个fly()方法,但它没有从我们的Plane类继承,因此我们的Python解释器不会将其识别为飞机。

好消息是Python是一种灵活的语言。由于ABCMetaregister方法,当我们执行Plane.register(Boeing747Plane)后,Boeing747Plane现在是Plane的子类。我们可以像使用自己构建的Plane一样使用第三方的Boeing747Plane。太棒了!

所以你看,虚拟类用于当我们希望将来自第三方包的类作为我们自己抽象类的子类时。我们希望它实现我们的接口,但我们不能更改其代码,所以我们明确告诉解释器“它已经实现了我们的接口,请将其视为我们自己类的子类”。我认为通常情况下我们不想使用它,但当需要时,请谨慎使用。

正如Luca Cappelletti所说,这是Python允许的许多灵活性之一,遵循其“我们在此是成年人”的理念。


那么,注册和子类化之间唯一的区别是:1-你可以在类声明之后注册;2-你不需要实现接口的抽象方法,对吗? - almanegra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接