14_Python面向对象
面向过程与面向对象
在编程范式(programming paradigms)中,面向过程(Procedural Programming)和面向对象(Object-Oriented Programming,简称OOP)是两种主要的编程风格。
Python是一种多范式编程语言,它支持面向过程编程,也支持面向对象编程。
面向过程编程(Procedural Programming)
- 重点:面向过程编程主要关注的是过程,即完成任务的步骤。
- 结构:程序通常由一系列按照顺序执行的函数或语句组成。
- 数据:数据和逻辑(函数)是分离的。数据通常以全局变量的形式存在,可以被程序中的任何函数访问和修改。
- 复用:通过函数来复用代码。
- 例子:C语言是一种典型的面向过程的编程语言。
Python中的面向过程编程示例:
# alculate_area 的函数,它接受两个参数 length 和 width。
'''
这个函数的作用是计算一个矩形的面积,
计算方式是将长度和宽度相乘,
并返回计算结果。
- length 和 width 是函数的参数,它们分别代表矩形的长度和宽度。
- return 语句用于返回计算得到的面积。
'''
def calculate_area(length, width):
return length * width
# 定义名为 print_area 的函数,它接受两个参数 length 和 width。
'''
这个函数的作用是调用 calculate_area 函数来计算矩形的面积,
并打印出面积。
'''
def print_area(length, width):
# calculate_area函数调用,将 length 和 width 作为参数传递给 calculate_area 函数
area = calculate_area(length, width)
# 使用 print 函数输出字符串 “Area:” 和计算得到的面积。
print("Area:", area)
length = 10
width = 5
# 调用 print_area 函数,并将的 length 和 width 变量作为参数传递给它。
print_area(length, width)
# 结果:Area: 50
面向对象编程(Object-Oriented Programming)
- 重点:面向对象编程主要关注的是对象,即数据和行为的封装。
- 结构:程序由类(class)和对象(object)组成。类是对象的蓝图,对象是类的实例。
- 类:是一个抽象的模板或蓝图,它定义了一组属性(数据)和方法(函数)。类本身并不代表一个具体的实体,而是描述了属于该类的所有对象共有的特征和行为。
- 对象:是根据类创建的实体。每个对象都是类的一个实例,它拥有类定义的属性和方法的具体值。你可以将对象视为蓝图的具体实现或建筑物。
- 数据:数据和操作数据的方法(函数)被封装在一起,称为类的属性和方法。
- 复用:通过继承和多态来复用代码。
- 例子:Java和C++是典型的面向对象编程语言。
Python中的面向对象编程示例:
定义一个名为 Rectangle 的类
这里定义了一个名为 Rectangle 的类。类是面向对象编程中的一个核心概念,它为创建对象提供了一个模板。
构造函数 init
__init__
方法是类的构造函数,它在创建类的实例时自动被调用。这个方法的主要目的是初始化新创建的对象的状态。
- self 参数是对当前对象的引用,它允许我们在方法内部访问和修改对象的属性。
- length 和 width 是传递给构造函数的参数,它们定义了矩形的长度和宽度。
- self.length = length 和 self.width = width 将传入的参数值赋给对象的属性。这些属性是对象状态的组成部分。
方法 calculate_area
calculate_area 是一个实例方法,它定义了 Rectangle 类对象的行为。这个方法计算并返回矩形的面积。
- self 参数是必须的,因为它指向调用该方法的对象实例。
- 方法计算面积的方式是将对象的 length 属性与 width 属性相乘。
方法 print_area
print_area 是另一个实例方法,它调用 calculate_area 方法来获取矩形的面积,并将其打印出来。
- 同样,self 参数指向调用该方法的对象实例。
- 方法首先调用 calculate_area 方法获取面积,然后使用 print 函数输出面积。
# 定义一个名为 Rectangle 的类,
class Rectangle:
# 构造函数 __init__
def __init__(self, length, width):
self.length = length
self.width = width
def calculate_area(self):
return self.length * self.width
def print_area(self):
area = self.calculate_area()
print("Area:", area)
# 创建对象
# -Rectangle(10, 5) 调用 Rectangle 类的构造函数,并传递了长度为 10 和宽度为 5 的参数。
# -rect 是新创建的对象的引用名称。
rect = Rectangle(10, 5)
# 通过 rect 对象调用 print_area 方法。计算并打印出矩形的面积。
rect.print_area()
这段代码演示了面向对象编程的几个关键概念:
- 封装:数据和操作数据的方法被封装在 Rectangle 类中。
- 抽象:用户不需要知道 calculate_area 或 print_area 方法的具体实现细节,只需要知道如何使用它们。
- 实例化:通过类创建具体的对象 rect。
- 方法调用:通过对象调用方法来执行操作。
面向过程与面向对象对比
- 数据封装:面向对象编程通过类和对象提供更好的数据封装,而面向过程编程中数据和逻辑通常是分开的。
- 代码复用:面向对象编程通过继承和多态提供更高级的代码复用机制,而面向过程编程主要依赖于函数。
- 维护和扩展:面向对象编程更容易维护和扩展,因为它将功能划分为独立的、可重用的对象。
- 复杂性:面向对象编程可能引入更多的概念(如类、对象、继承、多态等),这可能使程序更复杂,但同时也更灵活。
Python的设计哲学是“优雅”、“明确”和“简单”,它允许程序员根据具体问题选择最适合的编程范式。在Python中,即使使用了面向对象的特性,也可以保持代码的简洁性。
私有属性和私有方法
在面向对象编程中,私有属性和私有方法(有时称为私有防范)是用于实现封装的机制。封装是面向对象编程的四大基本原则之一,它指的是将对象的实现细节隐藏起来,只暴露出有限的接口供外部使用。这样做的好处包括:
- 保护对象的状态不被外部直接修改,从而保持对象的完整性。
- 允许在不影响外部代码的情况下更改内部实现。
- 避免外部代码对内部工作原理的依赖,提高代码的可维护性和可重用性。
在Python中实现私有属性和方法的几种方式:
1. 单下划线前缀 _
在Python中,单下划线前缀是约定俗成的表示,表示属性或方法是受保护的。它告诉外部代码:“这是私有的,不要直接访问或修改”。
尽管这种做法并不强制,但它是一种编程约定,告诉其他开发者这些属性和方法是用于内部使用的。
2. 双下划线前缀 __
在Python中,双下划线前缀会触发名称修饰(name mangling),这是一种防止属性或方法被外部访问的机制。它通过在属性或方法名前加上类名和额外的下划线来“隐藏”它们。
私有防范的意义
私有防范确保了类的内部状态的安全性,并且提供了以下保护措施:
- 防止意外修改:外部代码不能直接修改私有属性,减少了因意外修改导致的错误。
- 控制访问:只能通过类的公共接口来访问和修改对象的状态,这允许类在内部实施复杂的逻辑和安全检查。
- 隐藏实现细节:类的内部实现细节对外部代码是隐藏的,这意味着可以在不影响外部代码的情况下更改内部实现。
通过使用私有属性和方法,可以更好地控制类的行为,并确保其状态的正确性和一致性。
下面是修改后的 Rectangle 类,加入了两个私有属性和一个私有方法。
class Rectangle:
# 构造函数 __init__
def __init__(self, length, width):
# 公有属性
self.length = length
self.width = width
# 私有属性,表示矩形是否是正方形
self.__is_square = length == width
# 私有属性,存储计算过的面积,避免重复计算
self.__area = None
# 公有方法,计算面积
def calculate_area(self):
# 如果私有属性 __area 已经有值,直接返回
if self.__area is not None:
return self.__area
# 计算面积并赋值给私有属性 __area
self.__area = self.length * self.width
return self.__area
# 公有方法,打印面积
def print_area(self):
area = self.calculate_area()
print("Area:", area)
# 私有方法,检查矩形是否是正方形
def __check_square(self):
# 更新私有属性 __is_square 的值
self.__is_square = self.length == self.width
return self.__is_square
# 公有方法,提供外部检查是否是正方形的接口
def is_square(self):
# 调用私有方法 __check_square 来检查
return self.__check_square()
# 创建对象
rect = Rectangle(10, 5)
# 通过 rect 对象调用 print_area 方法。计算并打印出矩形的面积。
rect.print_area()
# 尝试检查 rect 是否是正方形
print("Is square:", rect.is_square()) # 应该返回 False,因为长度和宽度不相等
# 创建一个正方形对象
square = Rectangle(5, 5)
# 通过 square 对象调用 print_area 方法。计算并打印出正方形的面积。
square.print_area()
# 尝试检查 square 是否是正方形
print("Is square:", square.is_square()) # 应该返回 True,因为长度和宽度相等
在这个例子中,__is_square 和 __area 是私有属性,它们只能在 Rectangle 类的内部被访问和修改。__check_square 是一个私有方法,用于检查矩形是否是正方形,并更新 __is_square 属性。外部代码不能直接访问这些私有属性和方法,但可以通过公有方法 is_square 来检查矩形是否是正方形。这样做可以保护类的内部状态,并允许类控制如何访问和修改这些状态。
Python中的继承和多态
在面向对象编程中,继承(Inheritance)和多态(Polymorphism)是非常重要的概念。
下面我将结合之前提供的 Rectangle 类的代码,来解释这两个概念,并提供相应的代码示例。
继承(Inheritance)
继承是一种使一个类(称为子类或派生类)能够继承另一个类(称为基类或超类)的属性和方法的方式。这使得我们可以重用代码,并在现有类的基础上构建新的类。
下面是一个继承的例子,我们将创建一个 Square 类,它继承自 Rectangle 类:
# 继承自 Rectangle 的 Square 类
class Square(Rectangle):
# 构造函数 __init__
def __init__(self, side):
# 调用基类 Rectangle 的构造函数,设置长度和宽度相等
super().__init__(side, side)
# Square 总是正方形,所以不需要检查 __is_square
self.__is_square = True
# 重写基类的 calculate_area 方法,直接返回边长的平方
def calculate_area(self):
return self.length ** 2
# 创建 Square 对象
square = Square(5)
# 通过 square 对象调用 print_area 方法。计算并打印出正方形的面积。
square.print_area()
在这个例子中,Square 类继承自 Rectangle 类。这意味着 Square 类继承了 Rectangle 类的所有属性和方法。Square 类的构造函数使用 super().init(side, side) 调用了 Rectangle 类的构造函数,并确保了正方形的边长相等。此外,Square 类还重写了 calculate_area 方法,因为正方形的面积计算可以简化为边长的平方。
多态(Polymorphism)
多态是指同一个方法在不同类型的对象上可以有不同的行为。在Python中,多态通常是通过继承和方法的覆盖(override)来实现的。
下面是一个多态的例子,我们将创建一个函数,它可以接受任何类型的 Rectangle 对象(包括 Square 对象),并调用它们的 calculate_area 方法:
def print_shape_area(shape):
# 不关心 shape 是 Rectangle 还是 Square,只调用 calculate_area 方法
print("Area:", shape.calculate_area())
# 创建 Rectangle 对象
rect = Rectangle(10, 5)
# 创建 Square 对象
square = Square(5)
# 使用相同的函数来打印不同形状的面积
print_shape_area(rect) # 输出矩形的面积
print_shape_area(square) # 输出正方形的面积
在这个例子中,print_shape_area 函数接受一个 shape 参数,该参数可以是任何 Rectangle 类型的对象。由于 Square 是 Rectangle 的子类,因此它也可以作为参数传递。当调用 shape.calculate_area() 时,根据 shape 的实际类型(Rectangle 或 Square),会调用相应类中的 calculate_area 方法。这就是多态性的体现:同一个方法名在不同的对象上有不同的行为。
通过继承和多态,我们可以创建可扩展和可维护的代码,因为它们允许我们在不修改现有代码的情况下添加新的功能和行为。
附件
本文对应的jupyter notebook源码链接,欢迎下载练习:https://download.csdn.net/download/fx_yzjy101/89775174
如有问题请留言。