简介
多态 是指利用相同的接口来处理不同的基础形式,比如数据类型或者类。这使得函数能够在不同的时间使用不同类型的实体。
对于 Python 中的面向对象编程,这意味着属于特定类的特定对象可以以与属于不同类的不同对象相同的方式使用。
多态允许灵活性和松散耦合,使得代码可以随着时间的推移进行扩展和轻松维护。
本教程将介绍如何在 Python 中将多态应用于类。
先决条件
您应该已经安装了 Python 3,并在计算机或服务器上设置了编程环境。如果您还没有设置编程环境,可以参考适用于您的操作系统(Ubuntu、CentOS、Debian 等)的本地编程环境或服务器编程环境的安装和设置指南。
什么是多态?
多态是 Python 类定义中的一个重要特性,当您在类或子类之间具有通用命名的方法时会用到。这允许函数使用任何这些多态类的对象,而无需了解类之间的区别。
多态可以通过继承来实现,子类可以使用基类方法或覆盖它们。
Python 的 鸭子类型 是动态类型的一个特例,使用了多态的特征,包括后期绑定和动态分派。 “鸭子类型” 这个术语源自作家詹姆斯·惠特科姆·赖利的一句话:“当我看到一只像鸭子一样走路、游泳和嘎嘎叫的鸟时,我就称它为鸭子。” 意大利计算机工程师亚历克斯·马特里利在 comp.lang.python 新闻组的一条消息中引用了这句话,使用鸭子类型是为了确定对象是否适合特定目的。在使用普通类型时,适用性仅由对象的类型确定,但是使用鸭子类型时,将使用方法和属性的存在来确定适用性,而不是对象的实际类型。也就是说,您检查对象是否像鸭子一样嘎嘎叫和走路,而不是询问对象是否 是 一只鸭子。
当几个类或子类具有相同的方法名称,但这些方法的实现不同时,这些类是多态的,因为它们使用单一接口与不同类型的实体一起使用。函数将能够评估这些多态方法,而无需知道调用了哪些类。
创建多态类
为了利用多态,我们将创建两个不同的类,用于处理两个不同的对象。这两个不同的类都需要具有共同的接口,以便它们可以多态地使用,因此我们将为它们提供具有相同名称但功能不同的方法。
我们将创建一个 Shark
类和一个 Clownfish
类,它们分别定义了 swim()
、swim_backwards()
和 skeleton()
方法。
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.")
在上面的代码中,Shark
和 Clownfish
类都有三个具有相同名称的方法。然而,这些方法的功能对每个类来说是不同的。
让我们将这些类实例化为两个对象:
...
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
循环,遍历对象的元组。然后我们可以调用这些方法,而不必关心每个对象的类类型。我们只需要假设这些方法实际上存在于每个类中。
...
sammy = Shark()
casey = Clownfish()
for fish in (sammy, casey):
fish.swim()
fish.swim_backwards()
fish.skeleton()
我们有两个对象,Shark
类的 sammy
和 Clownfish
类的 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
的名称,但任何实例化的对象都可以被调用到这个函数中:
def in_the_pacific(fish):
接下来,我们将为函数提供一些操作,使用我们传递给它的 fish
对象。在这种情况下,我们将调用 swim()
方法,这个方法在 Shark
和 Clownfish
两个类中都有定义:
...
def in_the_pacific(fish):
fish.swim()
接下来,如果我们还没有创建 Shark
和 Clownfish
类的实例,我们将创建它们。有了这些实例,我们可以使用相同的 in_the_pacific()
函数调用它们的动作:
...
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.
尽管我们在定义 in_the_pacific()
函数时传递了一个随机对象(fish
),但我们仍然能够有效地用它来实例化 Shark
和 Clownfish
类。casey
对象调用了 Clownfish
类中定义的 swim()
方法,而 sammy
对象调用了 Shark
类中定义的 swim()
方法。
结论
通过允许不同的对象以多态的方式使用函数和方法,利用 Python 的这一特性可以提供更大的灵活性和可扩展性,使您的面向对象代码更加灵活。