如何在 Python 3 中对类应用多态性

简介

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

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

多态性具有灵活性和松耦合性,因此代码可以扩展并易于长期维护。

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

先决条件

您的计算机或服务器上安装了 Python 3 并设置了编程环境。如果没有设置编程环境,可以参考本地编程环境服务器上的编程环境 的安装和设置指南,这些指南适合您的操作系统(Ubuntu、CentOS、Debian 等)。

什么是多态性?

多态性是 Python 中类定义的一个重要特性,当您在类或子类中使用共同命名的方法时就会用到它。这样,函数就可以使用这些多态类中的任何对象,而无需注意类之间的区别。

多态性可以通过继承来实现,子类可以使用基类方法,也可以覆盖基类方法。

Python 的鸭子类型 是动态类型的一个特例,它使用了多态性的特征技术,包括 late bindingdynamic dispatch。鸭子类型 "一词源于作家詹姆斯-惠特科姆-莱利(James Whitcomb Riley)的一句话:"当我看到一只鸟走起路来像鸭子,游起来像鸭子,叫起来像鸭子,我就叫它鸭子。意大利计算机工程师 Alex Martelli 在 comp.lang.python 新闻组的一则消息中引用了鸭子键入法,认为使用鸭子键入法是为了确定对象是否适合特定用途。在使用普通类型时,这种适用性仅由对象的类型决定,但在使用 duck 类型时,适用性由方法和属性的存在决定,而不是由对象的实际类型决定。也就是说,我们检查的是对象的叫声是否像鸭子,走路是否像鸭子,而不是询问对象是否_是_只鸭子。

当多个类或子类具有相同的方法名,但这些相同方法的实现却不同时,这些类就是多态类,因为它们使用的是一个接口,可用于不同类型的实体。函数可以评估这些多态方法,而无需知道调用的是哪些类。

创建多态类

为了使用多态性,我们将创建两个不同的类,与两个不同的对象一起使用。这两个不同的类都需要有一个共同的接口,这样它们才能被多态地使用,因此我们将赋予它们不同但名称相同的方法。

<$>[信息] 信息: 要跟进本教程中的示例代码,请在本地系统上运行 python3 命令打开 Python 交互式 shell。然后,您可以复制、粘贴或编辑示例,将它们添加到 >> 提示符之后。 <$>

我们将创建一个 "鲨鱼 "类和一个 "小丑鱼 "类,每个类都将定义 "游泳()"、"向后游泳() "和 "骨骼() "的方法。

 1[label polymorphic_fish.py]
 2class Shark():
 3    def swim(self):
 4        print("The shark is swimming.")
 5
 6    def swim_backwards(self):
 7        print("The shark cannot swim backwards, but can sink backwards.")
 8
 9    def skeleton(self):
10        print("The shark's skeleton is made of cartilage.")
11
12class Clownfish():
13    def swim(self):
14        print("The clownfish is swimming.")
15
16    def swim_backwards(self):
17        print("The clownfish can swim backwards.")
18
19    def skeleton(self):
20        print("The clownfish's skeleton is made of bone.")

在上面的代码中,"鲨鱼 "类和 "小丑鱼 "类都有三个同名的方法。但是,每个类的这些方法的功能都不尽相同。

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

1[label polymorphic_fish.py]
2...
3sammy = Shark()
4sammy.skeleton()
5
6casey = Clownfish()
7casey.skeleton()

使用 "python polymorphic_fish.py "命令运行程序时,我们可以看到每个对象的行为都符合预期:

1[secondary_label Output]
2The shark's skeleton is made of cartilage.
3The clownfish's skeleton is made of bone.

现在,我们有了两个使用共同接口的对象,无论它们的类型如何,我们都可以以相同的方式使用这两个对象。

使用类方法的多态性

为了展示 Python 如何以相同的方式使用这些不同的类类型,我们可以首先创建一个 for循环,遍历对象的 元组。然后,我们就可以调用这些方法,而不必关心每个对象属于哪种类类型。我们只假设这些方法确实存在于每个类中。

 1[label polymorphic_fish.py]
 2...
 3sammy = Shark()
 4
 5casey = Clownfish()
 6
 7for fish in (sammy, casey):
 8    fish.swim()
 9    fish.swim_backwards()
10    fish.skeleton()

我们有两个对象:鲨鱼类的 sammy 和小丑鱼类的 casey。我们的 for 循环遍历这些对象,调用每个对象的 swim(), swim_backwards()skeleton() 方法。

运行程序后,输出结果如下:

1[secondary_label Output]
2The shark is swimming.
3The shark cannot swim backwards, but can sink backwards.
4The shark's skeleton is made of cartilage.
5The clownfish is swimming.
6The clownfish can swim backwards.
7The clownfish's skeleton is made of bone.

for "循环首先遍历了 "鲨鱼 "类的 "sammy "实例,然后是 "小丑鱼 "类的 "casey "对象,因此我们首先看到的是与 "鲨鱼 "类相关的方法,然后才是 "小丑鱼 "类。

这表明 Python 在使用这些方法时,并不知道或不关心这些对象各自是什么类类型。也就是说,这些方法是以多态的方式使用的。

使用函数的多态性

我们还可以创建一个可以接受任何对象的函数,从而实现多态性。

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

1[label polymorphic_fish.py]
2
3def in_the_pacific(fish):

接下来,我们将给函数一些使用我们传递给它的 fish 对象的操作。在这种情况下,我们将调用 swim() 方法,这两个方法分别在 SharkClownfish 两个类中定义:

1[label polymorphic_fish.py]
2...
3def in_the_pacific(fish):
4    fish.swim()

接下来,如果还没有鲨鱼小丑鱼类,我们将创建它们的实例。有了这些实例,我们就可以使用相同的 in_the_pacific() 函数调用它们的动作:

 1[label polymorphic_fish.py]
 2...
 3def in_the_pacific(fish):
 4    fish.swim()
 5
 6sammy = Shark()
 7
 8casey = Clownfish()
 9
10in_the_pacific(sammy)
11in_the_pacific(casey)

运行程序后,输出结果如下:

1[secondary_label Output]
2The shark is swimming.
3The clownfish is swimming.

尽管我们在定义 in_the_pacific() 函数时传递了一个随机对象 (ffish),但我们仍然能够有效地将它用于 SharkClownfish 类的实例化。casey "对象调用了 "Clownfish "类中定义的 "swim() "方法,而 "sammy "对象调用了 "Shark "类中定义的 "swim() "方法。

结论

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

Published At
Categories with 技术
comments powered by Disqus