在前面章节已经详细介绍了 Python3 的基础知识,你可以利用这些基础知识编写一些小功能(对字符串进行裁剪、简单数学运算等等),甚至可以操作本地的文件(读取和写入),但 Python3 的功能远远不止这些,它还支持面向对象编程,本章将介绍 Python3 关于面向对象编程相关的知识。
谈到面向对象编程,不得不了解一下面向过程,然后通过对比两者,深刻认识面向对象的好处。
编程指以过程或函数为核心来组织程序的逻辑结构。数据在流程中流动,自顶向下、分层模块化,顺序执行。
在面向过程编程中,整个程序被设计成一系列顺序执行的步骤,通过将复杂的任务分解为较小的、相互关联的子任务或函数来实现程序功能。这些函数按照一定的顺序依次调用,以完成特定的业务逻辑。如下图:

上图从主函数开始运行,一步一步的执行,直到程序结束。
面向过程强调数据的处理流程,从输入数据开始,经过一系列预定的处理步骤,最终产生输出结果。
面向过程编程注重程序执行的顺序和步骤的清晰性,有利于提高代码的可读性和可维护性,尤其适用于处理较为简单和线性的问题场景。
例如,在开发一个简单的文件处理程序时,可以使用面向过程编程,将打开文件、读取数据、处理数据、写入结果到文件等操作分别定义为不同的函数,然后按照流程依次调用这些函数来完成整个文件处理任务。如下:

面向对象是指将数据和对数据的操作封装在一起,形成一个个独立的“对象”。
面向对象编程(Object-Oriented Programming, OOP)是以对象为核心的编程范式,通过将现实世界中的事物抽象为类(Class)和对象(Object),来解决复杂问题。OOP 的核心思想是通过对象的交互来实现功能,而不是依赖函数的顺序执行。
例如,以洗衣机洗衣服为例,我们只和洗衣机对象进行交互,不用管对象内部怎么干。如下图:

通过面向对象编程,开发者能够更高效地组织代码,提高代码的复用性和扩展性,便于应对日益复杂的软件开发需求。
面向对象编程(OOP)主要特性包括:
注意,这些特性使得面向对象编程能够更直观地表达问题和解决方案,提高代码的可重用性、可扩展性和可维护性。
类(Class)和对象(Object)是面向对象编程的两大核心。一个类可以实例化多个对象。可以简单理解为:类是产品的设计图纸,对象则是依据图纸生产出的具体实例。
类(Class)是对一类事物的抽象描述与模板。类不代表某个具体东西,而是定义了这类东西共同拥有什么特征、能做什么事。例如:
在面向对象的世界里,类 = 模板 / 图纸 / 设计方案。
注意,一个标准的类,主要包含如下两部分:
对象(Object)是类的具体实例,是真实存在、可以使用的个体。
类是抽象模板,对象就是根据这个模板造出来的真实东西
类是图纸,对象就是按图纸造好的成品
类是“手机”这个概念,对象就是你手里正在用的这部手机。
总结起来:
类定义结构,对象存具体数据
在 Python 中,类表示具有相同属性和方法的对象的集合。在使用类时,需要先定义类,然后创建类的实例,通过类的实例就可以访问类中的属性和方法。下面将进行具体介绍:
在 Python 中,类的定义使用 class 关键字来实现,语法如下:
class 类名称:
"""类的帮助信息""" # 类文档字符串
statement # 类的代码体参数说明如下:
注意,在定义类时,如果没想好类的具体功能,可以在类体中直接使用 pass 语句代替。
示例:定义一个名为 Person 的类,该类拥有一个构造方法和成员方法,将在后续详细介绍
class Person:
# 构造方法:创建实例自动执行
def __init__(self, name, age):
# self = 当前创建的对象自己
self.name = name # 实例属性
self.age = age # 实例属性
# 成员方法
def introduce(self):
print(f"我叫{self.name},今年{self.age}岁")定义完类后,并不会真正创建一个实例。这就有点像一辆汽车的设计图,设计图可以告诉我们汽车是什么样子,如何进行制造。设计图并不是一辆汽车,也不能被开走,它只能用来制造真正的汽车,而且可以制造很多汽车。
在 Python 中,创建类的实例语法如下:
ClassName(parameterList)其中:
示例 1:一个非常简单的类,打印问候信息
class Hello:
"""第一个类"""
# 打招呼的方法
def hi(self, name):
print(f"你好!{name}")
# 创建 Hello 类的实例
if __name__ == "__main__":
p = Hello()
p.hi("张三")运行结果:
你好!张三示例 2:创建一个拥有构造参数的 Person 类,通过构造参数传递值
class Person:
# 构造方法:创建实例自动执行
def __init__(self, name, age):
# self = 当前创建的对象自己
self.name = name # 实例属性
self.age = age # 实例属性
# 成员方法
def introduce(self):
print(f"我叫{self.name},今年{self.age}岁")
if __name__ == '__main__':
p1 = Person("张三", 20)
p1.introduce()
p2 = Person("李四", 30)
p2.introduce()运行结果:
我叫张三,今年20岁
我叫李四,今年30岁注意,__name__ == '__main__' 是 Python 里用来判断当前脚本是不是被直接运行的常用写法。当你直接运行这个 .py 文件时,Python 会自动把内置变量 __name__ 设为 '__main__',下面的代码就会执行。当这个文件被别的脚本 import 导入时,__name__ 会变成模块名,条件不成立,下面的代码就不会运行。
在创建类后,通常会创建一个 __init__() 方法,该方法是一个特殊的方法,类似 Java 语言中的构造方法。每当创建一个类的新实例时,Python 都会自动执行它。
注意,__self__() 方法中必须包含一个 self 参数,并且必须是第一个参数。self 参数是一个指向实例本身的引用,用于访问类中的属性和方法。在方法被调用时会自动传递实际参数 self。因此,在 __init__() 方法中只有一个参数的情况下,不需要指定实际参数。
示例 1:仅有 self 参数的构造方法
class Hello:
"""打招呼的类"""
# 构造函数
def __init__(self):
print(f"Hello 类的初始化方法")
# 打招呼的方法
def hi(self, name):
print(f"你好!{name}")
# 创建 Hello 类的实例
if __name__ == "__main__":
p = Hello()
p.hi("张三")执行结果:
Hello 类的初始化方法
你好!张三从运行结果可以看出,在创建 Hello 类的实例时,虽然没有为 __init__() 方法指定参数,但是该方法会自动执行。
示例 2:定义一个构造方法,接收用户姓名,当调用 hi() 方法时自动输出打招呼信息
class Hello:
"""打招呼的类"""
# 定义成员
name = "" # 姓名
# 构造函数
def __init__(self, name):
self.name = name
# 打招呼的方法
def hi(self):
print(f"你好!{self.name}")
# 创建 Hello 类的实例
if __name__ == "__main__":
p = Hello("张三")
p.hi()运行结果:
你好!张三示例 3:使用构造函数接收两个业务参数,分别是用户的姓名和年龄,其中年龄设置默认值
class Person:
# 构造函数,并且为 age 指定默认值 18
def __init__(self, name, age=18):
self.name = name
self.age = age
if __name__ == "__main__":
p1 = Person("小红")
print(p1.name) # 小红
print(p1.age) # 18运行结果:
小红
18类的成员主要由实例方法和数据成员组成。在类中创建了类的成员后,可以通过类的实例进行访问。
所谓的实例方法,是指在类中定义的函数。该函数是一种在类的实例上操作的函数。同 __init__() 方法一样,实例方法的第一个参数必须是 self,并且必须包含一个 self 参数。
创建语法如下:
def functionName(self, parameterlist):
block # 函数体代码块参数说明:
在成功创建实例方法后,可以通过类的实例名称和点(.)操作符进行访问,语法如下:
instanceName.functionName(parameterList)参数说明:
示例:
class Person:
# 构造函数,并且为 age 指定默认值 18
def __init__(self, name, age=18):
self.name = name
self.age = age
self.deposit = 0
# 发工资
def payoff(self, salary):
self.deposit += salary
# 打印对象的方法
def print_info(self):
print(f"姓名:{self.name},年龄:{self.age}, 存款:{self.deposit:.2f}")
if __name__ == "__main__":
p1 = Person("小红", 20)
p1.payoff(1000) # 发工资 1000
p1.print_info()运行结果:
姓名:小红,年龄:20, 存款:1000.00数据成员指在类中定义的变量,即属性,根据定义位置,又可以分为类属性和实例属性。下面将分别介绍:
类属性是指定义在类中,并且在函数体外的属性。类属性可以在类的所有实例之间共享值,也就是在所有实例化的对象中公用。注意,类属性可以通过类名称或者实例名被访问。
示例 1:在 Person 类中定义两个类属性,分别表示姓名和年龄。
class Person:
# 类属性
name = "小红"
age = 20
# 构造函数,内部将访问类属性
def __init__(self):
print("Person 构造函数")
print(f"姓名:{self.name},年龄:{self.age}")
if __name__ == "__main__":
Person() # 调用构造函数
# 使用类名称访问类属性
print(f"姓名:{Person.name}, 年龄:{Person.age}")运行结果:
Person 构造函数
姓名:小红,年龄:20
姓名:小红, 年龄:20如果你熟悉 Java 语言,其实类属性就是 Java 的静态类成员变量。
示例 2:创建多个 Person 的实例,然后通过类名修改类属性的值,最后通过实例名打印信息,验证类属性是所有类实例共享的。
class Person:
# 类属性
name = "小红"
age = 20
# 打印信息
def info(self):
print(f"姓名:{self.name},年龄:{self.age}")
if __name__ == "__main__":
p1 = Person() # 调用构造函数
p1.info()
# 修改类属性
Person.name = "小明"
Person.age = 18
p2 = Person() # 调用构造函数
# 打印信息
p1.info()
p2.info()运行结果:
姓名:小红,年龄:20
姓名:小明,年龄:18
姓名:小明,年龄:18实例属性是指定义在类的方法中、且绑定在 self 上的属性。例如:
class Person:
# 构造函数
def __init__(self):
self.name = "小红" # 实例属性
self.age = 20 # 实例属性
# 打印信息
def info(self):
print(f"姓名:{self.name},年龄:{self.age}")
if __name__ == "__main__":
p1 = Person() # 调用构造函数
p2 = Person()
p1.name = "小明" # 使用实例名访问实例属性
p2.age = 43
p1.info()
p2.info()运行结果:
姓名:小明,年龄:20
姓名:小红,年龄:43从上面输出结果可以知道,p1 修改 name,p2 修改 age 实例属性,均只影响当前实例,对其他实例没有影响。实例属性的特点:
在类的内部可以定义属性和方法,在类的外部则可以直接调用这些属性或方法来操作数据,从而隐藏类内部的复杂实现逻辑。不过,Python 本身并没有对属性和方法提供严格的访问权限控制。为了保护类内部的某些属性或方法不被外部随意访问、修改,可以在属性或方法名称前添加双下划线(如 __calc),或在名称首尾均添加双下划线,以此实现访问权限限制。
其中,双下划线的具体作用如下:
示例:
class Person:
__version = "1.0.1" # 定义私有属性
# 构造函数
def __init__(self):
print(f"version={self.__version}") # 在实例方法中访问私有属性
if __name__ == "__main__":
p1 = Person() # 调用构造函数
print(p1._Person__version) # 通过 “实例名._类名__xxx” 方式访问私有属性运行结果:
version=1.0.1
1.0.1这里介绍的属性和上面介绍的类属性和实例属性不同,而是一种比较特殊的属性,访问它时将计算它的值。另外,该属性还可以为属性添加安全保护机制。
在 Python 中,可以通过 @property(装饰器)将一个方法转换为属性。将方法转换为属性后,可以直接通过方法名来访问,而无需再添加一对小括号,这样代码更简单。
创建属性的语法:
@property
def methodName(self):
block参数说明:
示例:下面将给出一个使用方法计算矩形周长和面积,然后再提供一个通过属性实现计算矩形周长和面积,比较两者的异同。
下面类通过方法的方式计算矩形的周长和面积:
class Rectangle:
"""
矩形类
"""
def __init__(self, width, height):
# 初始化矩形的宽和高
self.width = width
self.height = height
# 计算属性:面积(访问时自动计算)
def area(self):
return self.width * self.height
# 计算属性:周长(访问时自动计算)
def perimeter(self):
return 2 * (self.width + self.height)
if __name__ == "__main__":
rect = Rectangle(3, 4)
# 访问属性(不用加括号!)
print("宽:", rect.width)
print("高:", rect.height)
print("面积:", rect.area()) # 调用函数
print("周长:", rect.perimeter()) # 调用函数运行结果:
宽: 3
高: 4
面积: 12
周长: 14下面类为上面代码的 area() 和 perimeter() 函数添加 @property 装饰器,代码如下:
class Rectangle:
"""
矩形类
"""
def __init__(self, width, height):
# 初始化矩形的宽和高
self.width = width
self.height = height
# 计算属性:面积(访问时自动计算)
@property
def area(self):
return self.width * self.height
# 计算属性:周长(访问时自动计算)
@property
def perimeter(self):
return 2 * (self.width + self.height)
if __name__ == "__main__":
rect = Rectangle(3, 4)
# 访问属性(不用加括号!)
print("宽:", rect.width)
print("高:", rect.height)
print("面积:", rect.area) # 使用属性
print("周长:", rect.perimeter) # 使用属性运行结果:
宽: 3
高: 4
面积: 12
周长: 14注意,不能对属性进行赋值,如果重新对属性赋值,例如:
class Rectangle:
"""
矩形类
"""
def __init__(self, width, height):
# 初始化矩形的宽和高
self.width = width
self.height = height
# 计算属性:面积(访问时自动计算)
@property
def area(self):
return self.width * self.height
# 计算属性:周长(访问时自动计算)
@property
def perimeter(self):
return 2 * (self.width + self.height)
if __name__ == "__main__":
rect = Rectangle(3, 4)
rect.area = 10 # AttributeError将抛出“AttributeError: property 'area' of 'Rectangle' object has no setter”错误。
在编写类时,并非每次都需要从零开始实现。如果待开发的类与已有类之间存在继承关系,就可以通过继承实现代码复用,从而有效提高开发效率。
继承是面向对象编程中最为核心的特性之一,它的设计思想来源于人类认识客观世界的逻辑,也是自然界中普遍存在的传递与演化规律。在现实生活里,我们每个人都会从祖辈与父母身上继承相貌、体型等诸多特征,但同时又不会和父母完全相同 —— 每个人都会形成独属于自己的性格、习惯与特点,这些专属特征并不会完全照搬自长辈,而是在继承基础上形成的独特差异。如下图所示:

在程序设计中实现继承,意味着子类(或派生类)会自动拥有其继承的父类(或基类)的所有公有成员和受保护成员,无需重复定义这些成员,从而实现代码的高效复用。
在面向对象编程的规范中,我们通常将被继承的原有类称为父类(也可称为基类、超类),而通过继承创建的新类则被称为子类(也可称为派生类)。
通过继承,我们不仅能够高效实现代码的重用,避免重复编写相同的逻辑和代码,还可以清晰理顺不同类之间的层级关系,让类的结构更具逻辑性和可读性,便于后续的维护与扩展。
在 Python 中,实现类的继承非常简洁,只需在类定义语句中,在类名右侧用一对小括号,将需要继承的基类名称包裹起来即可。其语法格式如下:
class ClassName(baseClassList):
"""类的帮助信息""" # 类文档字符串
statement # 类体参数说明:
示例:先定义一个基类 Animal,然后定义 Dog 类,该类继承了 Animal 类。如下:
# 父类(基类):动物类
class Animal:
def __init__(self, name, age):
# 初始化属性
self.name = name
self.age = age
# 父类方法:叫
def speak(self):
print(f"{self.name} 发出了声音")
# 父类方法:介绍自己
def info(self):
print(f"我是 {self.name},今年 {self.age} 岁")
# 子类(派生类):狗类,继承自动物类
class Dog(Animal):
def __init__(self, name, age, breed):
# 调用父类的构造方法,继承父类的属性
super().__init__(name, age)
# 子类独有的属性:品种
self.breed = breed
# 方法重写(覆盖父类的speak方法)
def speak(self):
print(f"{self.name} 汪汪叫!")
# 子类独有的方法
def show_breed(self):
print(f"{self.name} 的品种是:{self.breed}")
if __name__ == '__main__':
# 创建子类对象
dog = Dog("旺财", 3, "金毛")
# 调用 继承自父类 的方法
dog.info() # 我是 旺财,今年 3 岁
# 调用 子类重写 的方法
dog.speak() # 旺财 汪汪叫!
# 调用 子类自己的方法
dog.show_breed() # 旺财 的品种是:金毛
# 访问 继承自父类 的属性
print("年龄:", dog.age) # 年龄: 3运行结果:
我是 旺财,今年 3 岁
旺财 汪汪叫!
旺财 的品种是:金毛
年龄: 3从该运行结果可知,使用 Dog 类的实例调用了 Dog 中没有定义的 info() 方法,该方法来自父类,同时在 Dog 中重新实现了 speak() 方法,这就是方法重写,下面将详细介绍。
基类中定义的属性和方法,都会被派生类直接继承使用。但在实际开发中,基类的某些方法往往只能实现通用逻辑,不一定完全适配派生类的具体需求。这时就需要在派生类中重新定义一个与父类同名、同参数的方法,覆盖掉从基类继承来的实现,这一机制称为方法重写。
这一特性与 Java 语言中的方法重写在设计思想和使用逻辑上是完全一致的。
示例:创建一个基类 Animal,然后创建 Dog 和 Cat 类继承 Animal 类,并且都重写 speak() 方法,如下:
# 父类(基类)
class Animal:
def speak(self):
print("动物发出声音")
# 子类(派生类)
class Dog(Animal):
# 重写父类的 speak 方法
def speak(self):
print("小狗汪汪叫")
class Cat(Animal):
# 同样重写 speak 方法
def speak(self):
print("小猫喵喵叫")
if __name__ == '__main__':
dog = Dog()
dog.speak() # 输出:小狗汪汪叫
cat = Cat()
cat.speak() # 输出:小猫喵喵叫运行结果:
小狗汪汪叫
小猫喵喵叫派生类如果定义了自己的 __init__(),不会自动调用基类的 __init__() 方法。基类中初始化的属性,就不会被赋值,派生类对象就无法使用。想要继承并使用基类的属性,必须手动调用基类的构造方法。
调用基类 __init__() 的方式有两种:
示例:定义一个基类 Person,创建一个派生类继承自 Student 类,然后在 Student 类的 __init__() 方法中手动调用父类的 __init__() 方法,如下:
# 基类
class Person:
def __init__(self, name):
self.name = name # 基类初始化属性
# 派生类
class Student(Person):
def __init__(self, name, score):
# 手动调用基类的 __init__
super().__init__(name)
# 派生类自己的属性
self.score = score
def show(self):
print(f"姓名:{self.name},分数:{self.score}")
if __name__ == "__main__":
s = Student("小明", 90)
s.show()运行结果:
姓名:小明,分数:90更多 Python3 知识,请继续学习后续章节。