Python面向对象中类的多态(第5节)


1、多态

通过继承可以避免重复编写相同的代码,但是有时候子类的行为方法不一定和父类完全一样。例如:

动手练一练:

class Person:
    def say(self):
        print("你好!")

class Student(Person):
    pass

class Teacher(Person):
    pass

student = Student()
student.say()  # 输出 你好!

teacher = Teacher()
teacher.say()  # 输出 你好!

执行以上代码,输出结果为:

你好
你好

上面的例子中,Person类是父类,Student类和Teacher类都是子类。虽然子类继承了父类的say方法,但是我们如何才能让Student类和Teacher类在调用say方法时分别输出不同的内容呢?其实方法很简单,例如:

动手练一练:

class Person:
    def say(self):
        print("你好!")

class Student(Person):
    def say(self):
        print("你好!我是学生。")

class Teacher(Person):
    def say(self):
        print("你好!我是老师。")

student = Student()
student.say()  # 输出 你好!我是学生。

teacher = Teacher()
teacher.say()  # 输出 你好!我是老师。

执行以上代码,输出结果为:

你好我是学生
你好我是老师

上面的例子中,Student类和Teacher类分别调用了各自的say方法,并输出了不同的内容。

当不同的子类调用相同父类的方法时,在子类中重写了父类的方法,这样子类在运行时总是调用重写的方法,这就是多态。多态是面向对象编程中的一个重要概念,它允许不同的类具有相同的方法名,但可以根据其具体的类型执行不同的操作。Python是一种完全支持多态性的面向对象编程语言。

判断一个对象是否是某个类或其子类的实例,可以使用isinstance()函数,例如:

动手练一练:

class Person:
    def say(self):
        print("你好!")

class Student(Person):
    def say(self):
        print("你好!我是学生。")

class Teacher(Person):
    def say(self):
        print("你好!我是老师。")

student = Student()
teacher = Teacher()

print(isinstance(student, Student))
print(isinstance(student, Person))
print(isinstance(teacher, Teacher))
print(isinstance(teacher, Person))

执行以上代码,输出结果为:

True
True
True
True

上面的例子中,isinstance()函数返回值为布尔类型True,可以判断出“student”不仅仅是“Student”的实例,而且还是“Person”的实例。“teacher”和“student”类似,都是从“Person”继承下来的。

其实在程序开发中,一般不会像上面那样去使用多态,而是使用更加优雅的写法,例如:

动手练一练:

class Person:
    def say(self):
        print("你好!")

class Student(Person):
    def say(self):
        print("你好!我是学生。")

class Teacher(Person):
    def say(self):
        print("你好!我是老师。")

def speak(a):
    a.say()

student = Student()
teacher = Teacher()

speak(student)  # 输出 你好!我是学生。
speak(teacher)  # 输出 你好!我是老师。

执行以上代码,输出结果为:

你好我是学生
你好我是老师

上面的例子中可以看出,我们只需要知道它是“Person”类型或子类型,无需确切地知道它是哪个子类型,就可以放心地调用speak()方法(调用方只管调用,不管细节)。上面的例子中,当需要新增功能时,只需要新增一个“Person”的子类实现speak()方法,就可以在原来的基础上进行功能扩展,这就是著名的“开放封闭”原则。“开放封闭”原则是面向对象设计中的重要原则之一,它要求软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭。这意味着当需要添加新功能时,不应该修改现有的代码,而是应该通过扩展来实现。利用多态机制来实现“开放封闭”原则,从而使系统更加灵活、可扩展和易于维护。

2、鸭子类型

鸭子类型(Duck Typing)是Python编程语言中的一种动态类型系统的概念,它的核心思想是“当你看到一只鸟走起来像鸭子,游泳起来鸭子,叫起来也像鸭子,那么这只鸟就被称为鸭子类型”。这个概念的基本思想是根据对象的行为来确定其类型,而不是根据其明确的类或子类声明。

鸭子类型是属于多态的一种形式,重点关注的是对象的行为方法,而不是对象的具体类型或类。如果一个对象具备特定的方法或属性,那么它就可以被视为拥有特定的类型,而不必显式声明或继承该类型。这种方式允许Python在运行时动态确定对象的类型,从而使代码更加灵活和简洁。

下面是一个简单的例子,使用鸭子类型来实现多态:

动手练一练:

# 鸭子类型示例
class Student():
    def say(self):
        print("你好!我是学生。")

class Teacher():
    def say(self):
        print("你好!我是老师。")

def speak(a):
    a.say()

# 使用鸭子类型,不关心具体的类型
student = Student()
teacher = Teacher()

speak(student)  # 输出 你好!我是学生。
speak(teacher)  # 输出 你好!我是老师。

执行以上代码,输出结果为:

你好我是学生
你好我是老师

上面的例子中,Student类和Teacher类都没有继承自同一个父类,但是它们都实现了say()方法。因此,speak()函数可以接受任何具有say()方法的对象作为参数,无需关注其类型。这就是鸭子类型的体现,它允许你编写通用的函数和代码,以处理多种不同类型的对象,只要它们具备所需的行为方法。这种灵活性使得Python成为一种强大而简洁的编程语言。但请注意,过度使用鸭子类型也可能导致代码不够明确和易于理解,因此需要谨慎使用。