前面的文章里面,我们讲了面向对象的入门概念:Python面向对象编程:入门篇(类和对象),接下来,我们继续学习进阶的内容封装、继承和多态。
如果没有看前面的内容,一定要去学习一下哦!!!!!
封装、继承和多态作为Python面向对象编程(OOP)三个核心概念,它们是面向对象程序设计的基石,允许我们以更加模块化、可维护的方式编写代码。下面我们一起来学习。
1. 封装 (Encapsulation)
封装是指将数据(属性)和操作数据的方法(方法)捆绑在一起的机制。在封装中,对象的内部细节被隐藏起来,只有特定的方法才能访问和操作这些细节。这有助于确保数据的安全性和代码的可维护性。
封装具有以下优势:
优势 | 描述 |
---|---|
安全性 | 隐藏对象的实现细节,防止外部直接访问和修改对象的内部状态,从而保护数据的安全性。 |
简化调用 | 使用者无需了解对象的内部实现细节,只需知道如何使用对象提供的接口即可。这简化了调用对象的过程,降低了使用的复杂度。 |
降低耦合度 | 封装使得对象的内部改变不会影响到外部的代码。对象的内部细节可以自由改变,而不会影响到使用该对象的其他部分。 |
提高代码的可维护性 | 封装使得代码模块化,每个模块都有清晰的功能和接口。当需要修改代码时,可以更容易地定位和修改相关的部分。 |
1.1 如何实现封装?
封装可以通过访问控制和访问修饰符来实现。主要有两种访问修饰符:公有属性和方法、私有属性和方法。
- 公有属性和方法 (Public Attributes and Methods)
可以被类的外部访问。在 Python 中,默认情况下,类的所有属性和方法都是公有的。
class Person:
def __init__(self, name, age):
self.name = name # 公有属性
self.age = age # 公有属性
def get_name(self):
return self.name # 公有方法
# 使用公有属性和方法
person1 = Person("Tiyong", 30)
print(person1.name) # 输出: Tiyong
print(person1.get_name()) # 输出: Tiyong
在这个例子中,Person类有一个公有方法 get_name()
,那么其他类或代码可以通过调用这个方法来获取对象的名称。同样,有一个公有属性 age
,那么其他类或代码可以直接访问和修改这个属性。所以,我们的实例对象就可以访问公共的属性和方法。
- 私有属性和方法 (Private Attributes and Methods)
只能在类的内部访问,外部无法直接访问。在 Python 中,可以在属性名或方法名前加上双下划线 ‘__
’ 来定义私有属性和方法。
class Person:
def __init__(self, name, age):
self.__name = name # 私有属性
self.__age = age # 私有属性
def __display_info(self):
return f"Name: {self.__name}, Age: {self.__age}" # 私有方法
# 外部无法直接访问私有属性和方法
person1 = Person("TiYong", 25)
# print(person1.__name) # 这会引发错误,因为__name是私有属性
# print(person1.__display_info()) # 这会引发错误,因为__display_info()是私有方法
在上面的例子中:person1对象就不能访问__name私有属性和__display_info()私有方法。
尽管外部无法直接访问私有属性和方法,但我们仍然可以通过公有方法来间接访问和操作它们。这种间接访问的方式使得我们可以控制对象的状态和行为,确保数据的一致性和安全性。
class Person:
def __init__(self, name, age):
self.__name = name # 私有属性
self.__age = age # 私有属性
def get_name(self):
return self.__name # 公有方法
def set_name(self, new_name):
self.__name = new_name # 公有方法,用于修改私有属性
def display_info(self):
return f"Name: {self.__name}, Age: {self.__age}" # 公有方法
# 通过公有方法访问和修改私有属性
person1 = Person("TiYong", 30)
print(person1.get_name()) # 输出: TiYong
person1.set_name("Toy")
print(person1.display_info()) # 输出: Name: Toy, Age: 30
那么,封装通过隐藏对象的内部细节、定义清晰的接口,提高了代码的安全性、可维护性和复用性,这就使得我们的程序更加健壮和易于开发与维护了。
2. 继承(Inheritance)
继承就是允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。子类可以继承父类的特性,并且可以在此基础上添加自己的新特性。这种机制允许代码的重用和层次化的设计。继承,就是字面上意思,继承。
2.2 继承的类型
2.2.1 单继承
单继承是指一个子类只能继承一个父类的属性和方法。这是最简单和最常见的继承类型。
class ParentClass:
def parent_method(self):
print("Parent method")
class ChildClass(ParentClass):
def child_method(self):
print("Child method")
# 子类继承父类的方法
child = ChildClass()
child.parent_method() # 输出: Parent method
child.child_method() # 输出: Child method
2.2.2 多继承
多继承是指一个子类可以同时继承多个父类的属性和方法。这使得子类可以具有多个父类的特性,但也可能引发一些复杂性和歧义。
class ParentClass1:
def method1(self):
print("Method 1 from ParentClass1")
class ParentClass2:
def method2(self):
print("Method 2 from ParentClass2")
class ChildClass(ParentClass1, ParentClass2):
def child_method(self):
print("Child method")
# 子类继承多个父类的方法
child = ChildClass()
child.method1() # 输出: Method 1 from ParentClass1
child.method2() # 输出: Method 2 from ParentClass2
child.child_method() # 输出: Child method
2.2.3 Python的继承顺序(MRO)
在多继承的情况下,Python 使用 C3 线性化算法来确定方法解析顺序(Method Resolution Order, MRO)。MRO 定义了类的方法解析顺序,确保在继承链中查找方法时,按照一定的顺序进行搜索。
可以通过调用类的 mro()
方法来查看方法的解析顺序:
print(ChildClass.mro())
# 输出: [<class '__main__.ChildClass'>, <class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>, <class 'object'>]
2.3 继承的优势
继承允许子类重用父类的代码,避免了重复编写相同的代码片段。子类可以直接使用父类已经定义的方法和属性。
优势 | 描述 |
---|---|
代码重用 | 子类可以直接使用父类已经定义的方法和属性,避免了重复编写相同的代码片段。 |
可扩展性 | 子类可以在不修改父类的情况下,添加新的属性和方法,从而使得代码更具可扩展性。这样可以在不影响父类的基础上,为程序添加新的功能。 |
2.4 继承的实际应用
2.4.1 创建子类
通过继承可以创建子类,实现代码的重用和层次化的设计。子类可以继承父类的方法和属性,并且可以添加自己的方法和属性。
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
dog = Dog()
print(dog.speak()) # 输出: Woof!
cat = Cat()
print(cat.speak()) # 输出: Meow!
2.4.2 覆盖父类方法(Method Overriding)
子类可以覆盖父类的方法,即在子类中重新定义与父类同名的方法。这样做可以根据子类的需求修改方法的实现。
class Bird:
def speak(self):
return "Chirp!"
class Parrot(Bird):
def speak(self):
return "Polly wants a cracker!"
parrot = Parrot()
print(parrot.speak()) # 输出: Polly wants a cracker!
2.4.3 调用父类方法(super()
函数)
子类中可以通过 super()
函数调用父类的方法。这在子类需要扩展父类方法的功能时非常有用。
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
class Square(Rectangle):
def __init__(self, side_length):
super().__init__(side_length, side_length) # 调用父类的 __init__() 方法
square = Square(5)
print(square.area()) # 输出: 25
在上面的例子中,Square
类继承自 Rectangle
类,并通过 super()
函数调用了 Rectangle
类的 __init__()
方法,实现了对正方形的初始化。
以上,就大概是继承的内容了。
3. 多态(Polymorphism)
多态是指允许对象在不同的情况下表现出不同的行为。简单地说,多态性意味着相同的方法调用可能会有不同的实现方式,具体取决于调用该方法的对象的类型或类的实现。
3.1 多态的实现方式
3.1.1 方法重写(Method Overriding)
方法重写是实现多态的一种方式,它允许子类覆盖(重写)父类的方法,以便在子类中实现特定的行为。当子类重新定义了与父类同名的方法时,调用这个方法时会执行子类的实现。通过一个例子展示方法重写:
class Animal:
def speak(self):
raise NotImplementedError("Subclass must implement abstract method")
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# 多态性的体现
def animal_sound(animal):
return animal.speak()
dog = Dog()
cat = Cat()
print(animal_sound(dog)) # 输出: Woof!
print(animal_sound(cat)) # 输出: Meow!
3.1.2 方法重载(Method Overloading)
方法重载是一种在同一个类中,方法名称相同但参数列表不同的技术。但是,在Python中,并没有像其他编程语言那样直接支持方法重载的特性,不过,可以通过一些技巧来模拟。
比如:使用默认参数值或者 *args
和 **kwargs
参数来实现类似方法重载的效果。
class Calculator:
def add(self, a, b):
return a + b
def add(self, a, b, c):
return a + b + c
calc = Calculator()
print(calc.add(2, 3)) # 输出: TypeError: add() missing 1 required positional argument: 'c'
print(calc.add(2, 3, 4)) # 输出: 9
3.2 多态的优势
优势 | 描述 |
---|---|
灵活性和可扩展性 | 允许同一个方法有不同的实现方式,使得代码更加灵活和可扩展。当需要添加新的功能时,只需添加新的子类或覆盖现有方法,而不需要修改现有代码。 |
代码简洁性 | 可以编写更加简洁和清晰的代码。通过使用多态性,可以将相同的操作应用于不同类型的对象,从而减少了重复代码的编写。 |
3.3 多态的实际应用
3.3.1 多态的实例
多态在实际应用中非常多。
class Shape:
def draw(self):
pass
class Circle(Shape):
def draw(self):
return "Drawing Circle"
class Square(Shape):
def draw(self):
return "Drawing Square"
# 多态性的体现
def draw_shape(shape):
return shape.draw()
circle = Circle()
square = Square()
print(draw_shape(circle)) # 输出: Drawing Circle
print(draw_shape(square)) # 输出: Drawing Square
例子,展示了在图形绘制中,不同的图形对象可以有不同的 draw
方法实现,但可以使用相同的方式进行绘制。Shape
是一个基类,Circle
和 Square
是它的子类,它们都有自己的 draw
方法实现。在调用 draw_shape
函数时,根据传入的参数不同,会执行不同子类的 draw
方法。
3.3.2 使用抽象基类(Abstract Base Classes)
abc
模块,可以通过抽象基类定义抽象方法,从而强制子类实现这些方法,实现接口的规范化。
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def draw(self):
pass
class Circle(Shape):
def draw(self):
return "Drawing Circle"
class Square(Shape):
def draw(self):
return "Drawing Square"
# 多态性的体现
def draw_shape(shape):
return shape.draw()
# 抽象基类确保子类实现了抽象方法
# rectangle = Shape() # 会引发错误,因为 Shape 是抽象基类,不能实例化
circle = Circle()
square = Square()
print(draw_shape(circle)) # 输出: Drawing Circle
print(draw_shape(square)) # 输出: Drawing Square
在这个例子中,Shape
是一个抽象基类,定义了一个抽象方法 draw
,所有继承自 Shape
的子类必须实现 draw
方法。这样可以确保所有的子类都有相同的接口,实现了多态的规范化。
通过上面的介绍,我们知道了多态使得代码更加灵活、可扩展、简洁。这也就是为什么面向对象那么受欢迎的原因了。
4. 实例展示
最后,我们通过两篇文章的学习,现在,我们通过实例代码来回顾一下所有的内容。
4.1 创建一个简单的封装类
示例: 定义一个 Car
类,封装汽车的属性和方法。
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def __str__(self):
return f"汽车信息:\n 品牌:{self.make}\n 型号:{self.model}\n 年份:{self.year}"
def start(self):
print(f"{self.make} {self.model} 已启动!")
car = Car("Toyota", "Camry", 2024)
print(car)
car.start()
输出如下:
汽车信息:
品牌:Toyota
型号:Camry
年份:2024
Toyota Camry 已启动!
4.2 实现一个继承的示例
示例: 定义一个 ElectricCar
类,继承 Car
类并添加续航里程属性和方法。
# 定义Car类
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def __str__(self):
return f"汽车信息:\n 品牌:{self.make}\n 型号:{self.model}\n 年份:{self.year}"
def start(self):
print(f"{self.make} {self.model} 已启动!")
# 继承Car
class ElectricCar(Car):
def __init__(self, make, model, year, range):
super().__init__(make, model, year)
self.range = range
def __str__(self):
return f"{super().__str__()}\n 续航里程:{self.range}公里"
electric_car = ElectricCar("Tesla", "Model 3", 2024, 600)
print(electric_car)
输出如下:
汽车信息:
品牌:Tesla
型号:Model 3
年份:2024
续航里程:600公里
4.3 演示多态的用例
示例: 定义一个 Vehicle
类,包含 drive()
方法,并定义 Car
和 Bicycle
两个子类,分别实现不同的 drive()
方法。
class Vehicle:
def drive(self):
raise NotImplementedError
# 多态重写
class Car(Vehicle):
def drive(self):
print("驾驶汽车...")
class Bicycle(Vehicle):
def drive(self):
print("骑自行车...")
car = Car()
bicycle = Bicycle()
vehicles = [car, bicycle]
for vehicle in vehicles:
vehicle.drive()
输出如下:
驾驶汽车...
骑自行车...
总结
类、对象、封装、继承和多态的重要性总结如下表:
概念 | 描述 | 重要性 |
---|---|---|
类 | 一种抽象数据类型,用于描述一组具有相同属性和方法的对象。 | 提高代码的组织性和可维护性,提高代码的复用性。 |
对象 | 类的实例,包含了类所定义的所有属性和方法。 | 提高代码的组织性和可维护性,提高代码的复用性。 |
封装 | 将对象的属性和方法隐藏在类中,对外只提供接口进行访问。 | 提高代码的安全性,提高代码的可维护性。 |
继承 | 允许子类继承父类的属性和方法的一种机制。 | 提高代码的复用性,提高代码的可扩展性。 |
多态 | 同一个方法可以对不同类型的对象执行不同的操作。 | 提高代码的灵活性,提高代码的可扩展性。 |
一些概念需要你记住的:
- 使用
class
关键字定义类。- 使用
__init__
方法初始化对象的属性。- 使用
self
关键字引用当前对象。- 使用
super()
函数调用父类的方法。- 使用
isinstance()
函数检查对象的类型。
这里,我们已经讲完了Python面向对象的知识:类(Class)、对象(Object)、封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)。掌握这些知识,可以帮助你编写出更优雅、更易读、更可维护的 Python 代码。
欢迎大家和我一起继续学习、记录python的下一个知识点。
如果感觉阅读对您还有些作用,可以评论留言,关注我。谢谢您的阅读!
往期学习:
VSCode安装教程(版本:1.87.0)Windows10
Python字符串操作及方法详解!一篇就搞定!-CSDN博客