如何在Python中为类应用多态?

多态性允许灵活性和松散耦合,从而可以随着时间的推移扩展和容易地维护代码。 本教程将介绍如何在Python中应用多态。

介绍

多态性是为不同的底层形式(如数据类型或类)使用相同的界面的能力。 这允许功能在不同时间使用不同类型的实体。

对于Python中的面向对象编程,这意味着属于特定类的特定对象可以以与属于不同类的不同对象相同的方式使用。

多态性允许灵活性和松散耦合,从而可以随着时间的推移扩展和容易地维护代码。

本教程将介绍如何在Python中应用多态。

什么是多态性?

多态是Python中类定义的一个重要特征,当您在类或子类之间具有通用命名方法时被使用。 这允许函数使用任何这些多态类的对象,而不需要意识到跨类的区别。

Python中的多态使用了编程语言对鸭子打字的使用。 “鸭打字”一词来自作家詹姆斯·惠特康特·莱利(James Whitcomb Riley)的报价:“当我看到一只像鸭子一样的鸟儿,像鸭子一样游走,像鸭子一样跳动的鸟儿,我把这只鸟称为鸭子”。意大利语计算机工程师Alex Martelli在向comp.lang.python新闻组发来一则消息中指出,使用鸭式打字涉及到为特定目的确定对象的适用性。 当使用正常打字时,这种适用性由单独的对象类型确定,但是使用鸭式打字,方法和属性的存在用于确定适合性,而不是所讨论对象的实际类型。 也就是说,您检查对象是否像鸭子一样像鸭子一样走动,而不是询问对象是否是鸭子。

当几个类具有相同的方法名称,但是对于这些相同方法的不同实现时,这些类是多态的。 一个函数将能够在不知道调用哪些类的情况下评估这些多态方法。

创建多态类

为了利用多态,我们将创建两个不同的类来使用两个不同的对象。 每个这些不同的类都需要有一个共同的接口,以便它们可以被多态使用,所以我们给它们不同但具有相同名称的方法。

我们将创建一个Shark类和一个Clownfish类,每个类都定义了swim()swim_backwards()skeleton()

polymorphic_fish.py
class Shark():
    def swim(self):
        print("The shark is swimming.")

    def swim_backwards(self):
        print("The shark cannot swim backwards, but can sink backwards.")

    def skeleton(self):
        print("The shark's skeleton is made of cartilage.")


class Clownfish():
    def swim(self):
        print("The clownfish is swimming.")

    def swim_backwards(self):
        print("The clownfish can swim backwards.")

    def skeleton(self):
        print("The clownfish's skeleton is made of bone.")

在上面的代码中, SharkClownfish类都有三个相同名称的方法。 然而,这些方法的每个功能对于每个类别都是不同的。

我们将这些类实例化为两个对象:

polymorphic_fish.py
...
sammy = Shark()
sammy.skeleton()

casey = Clownfish()
casey.skeleton()

当我们使用python polymorphic_fish.py命令运行程序时,我们可以看到每个对象的行为如预期的那样:

The shark's skeleton is made of cartilage.
The clownfish's skeleton is made of bone.

现在我们有两个使用通用接口的对象,我们可以以相同的方式使用这两个对象,而不管其各自的类型。

多类型与类方法

为了显示Python如何以同样的方式使用这些不同的类类型,我们可以先创建一个遍历对象元组for循环 那么我们可以调用这些方法,而不用担心每个对象的类类型。 我们只会假设这些方法实际上存在于每个类中。

polymorphic_fish.py
...
sammy = Shark()

casey = Clownfish()

for fish in (sammy, casey):
    fish.swim()
    fish.swim_backwards()
    fish.skeleton()

我们有两个对象, Shark类的sammyClownfish类的casey 我们的for循环遍历这些对象,在每个对象上调用swim()swim_backwards()skeleton()方法。

当我们运行程序时,输出如下:

The shark is swimming.
The shark cannot swim backwards, but can sink backwards.
The shark's skeleton is made of cartilage.
The clownfish is swimming.
The clownfish can swim backwards.
The clownfish's skeleton is made of bone.

for循环首先通过Shark类的sammy实例化,然后是Clownfish类的casey对象,所以我们首先看到与Shark类相关的方法,然后Clownfish类。

这表明Python以某种方式使用这些方法,而不知道或关心每个这些对象的类型。 也就是说,以多态方式使用这些方法。

多功能与功能

我们还可以创建一个可以接受任何对象的功能,允许多态。

我们创建一个名为in_the_pacific()的函数,它接收一个可以称之为fish的对象。 虽然我们使用的是fish ,但是任何实例化的对象都可以被调用到这个函数中:

polymorphic_fish.py
…
def in_the_pacific(fish):

接下来,我们将给出使用我们传递给它的fish对象的功能。 在这种情况下,我们将调用swim()方法,每个方法在两个类SharkClownfish中定义:

polymorphic_fish.py
...
def in_the_pacific(fish):
    fish.swim()

接下来,如果我们还没有SharkClownfish课程,我们将会创建实例化。 有了这些,我们可以使用相同的in_the_pacific()函数来调用它们的动作:

polymorphic_fish.py
...
def in_the_pacific(fish):
    fish.swim()

sammy = Shark()

casey = Clownfish()

in_the_pacific(sammy)
in_the_pacific(casey)

当我们运行程序时,输出如下:

The shark is swimming.
The clownfish is swimming.

即使我们在定义它时将随机对象( fish )传递给in_the_pacific()函数,我们仍然能够有效地使用它来实现SharkClownfish类的实例。 称为在Clownfish类中定义的swim()方法的casey对象,以及Shark类中定义的称为swim()方法的sammy对象。

结论

通过允许不同的对象以类似的方式通过多态来利用函数和方法,利用这个Python特性提供了更大的灵活性和面向对象代码的可扩展性。