第七章:Python3 之 面向对象编程

文章目录
前言
在Python编程体系中,面向对象编程(Object-Oriented
Programming,简称OOP)是核心进阶知识点,也是现代软件开发中最主流的编程思想,贯穿前端开发、后端业务搭建、运维自动化、AI模型开发等全场景。无论是刚入门的初级开发者,还是有一定基础的中级工程师,亦或是深耕各领域的后端、前端、运维人员以及AI爱好者,吃透面向对象,都能彻底改变编程思维,写出结构更清晰、复用性更强、扩展性更好、更易维护的代码。
相较于面向过程编程,面向对象更注重对现实事物的抽象与封装,把数据和操作数据的方法绑定在一起,以“对象”为核心展开编程,完美贴合现实世界的逻辑思维,大幅降低大型项目的开发与维护难度。市面上很多教程要么过于理论晦涩难懂,要么过于浅显覆盖不全,很难兼顾零基础入门与进阶提升。
本文全程摒弃晦涩术语,用通俗语言拆解核心概念,搭配大量可直接运行的代码+详细注释、图文示意图、底层原理剖析、全场景项目实战以及高频面试题答案,从基础的类与对象定义,到封装、继承、多态三大特性,再到底层原理、项目落地、面试答疑,循序渐进搭建完整的面向对象知识体系。全文内容可直接复制发布,代码无冗余、无报错,既能用来系统学习,也能作为日常开发的参考手册,助力每一位开发者快速掌握Python面向对象编程,顺利进阶提升。
一、理解面向对象
1.1 编程思想的演变:从面向过程到面向对象
在学习面向对象之前,我们先厘清两种最主流的编程思想,明白面向对象诞生的意义,才能更透彻地理解其核心价值。
1.1.1 面向过程编程(POP)
面向过程是一种以“过程”为核心的编程思想,注重解决问题的步骤与流程。编程时,我们会把需求拆解成一个个步骤,逐行编写代码实现每一步逻辑,最终完成整个需求,核心是“怎么做”。
这种思想适合简单、小型的脚本开发,逻辑直观、上手简单,但缺点也极为明显:代码冗余度高、复用性差、耦合性极强,一旦需求变更,需要修改大量代码,很难维护和扩展,完全无法适配大型项目开发。
# 面向过程:实现学生信息打印
# 逐步骤编写逻辑,代码复用性极差
stu_name = "张三"
stu_age = 18
stu_score = 95
# 打印信息
def print_stu_info():
print(f"姓名:{stu_name},年龄:{stu_age},成绩:{stu_score}")
print_stu_info()
1.1.2 面向对象编程(OOP)
面向对象是一种以“对象”为核心的编程思想,把现实世界中所有事物都抽象成“对象”,每个对象都拥有自己的特征(属性)和行为(方法)。编程时,我们注重抽象出事物的共性,创建对应的模板,再通过模板生成具体对象,调用对象的属性和方法完成需求,核心是“谁来做”。
这种思想将数据与操作绑定,实现了代码的高度复用、模块化拆分,大幅降低了代码耦合度,项目扩展性、维护性极强,是当下大型项目、框架开发、AI工程化的首选编程思想。
# 面向对象:实现学生信息打印
# 抽象学生模板,可无限创建学生对象,复用性极强
class Student:
# 学生特征:属性
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
# 学生行为:方法
def print_info(self):
print(f"姓名:{self.name},年龄:{self.age},成绩:{self.score}")
# 创建具体学生对象
stu1 = Student("张三", 18, 95)
stu1.print_info()
stu2 = Student("李四", 19, 92)
stu2.print_info()
1.2 面向对象核心概念:类与对象
1.2.1 类(Class)
类是对现实世界中一类具有相同特征和行为事物的抽象概括,是一个“模板”或者“图纸”,不代表具体事物。比如现实中的“学生”,所有学生都有姓名、年龄、成绩等特征,都有学习、考试、打印信息等行为,我们把这些共性抽象出来,就形成了“学生类”。
类只定义了对象拥有哪些属性和方法,本身不占用内存,只有通过类创建对象时,才会分配内存空间。
1.2.2 对象(Object)
对象是类的具体实例,是根据类这个“模板”创建出来的具体事物,也叫实例。比如“学生类”是抽象模板,而“张三”这个具体的学生,就是学生类创建出来的对象。
每个对象都拥有独立的属性值和类中定义的方法,对象之间相互独立、互不干扰。
1.2.3 类与对象的关系(图文示意)
💡 核心关系:类是对象的模板,对象是类的实例;一个类可以创建无数个对象,每个对象都属于对应的类。
就像我们根据图纸(类)建造房子(对象),一张图纸可以建造无数套房子,每套房子都有独立的户型、面积(属性),也都具备居住、装修的功能(方法),但图纸本身并不是真实的房子。
1.3 面向对象的核心特性
面向对象编程拥有三大核心特性,也是其核心优势所在,后续章节会逐一深度讲解:
封装:将对象的属性和方法私有化,隐藏内部实现细节,只对外暴露调用接口,保证数据安全,降低使用复杂度;
继承:子类可以继承父类的属性和方法,无需重复编写相同代码,实现代码复用,同时可以扩展自身功能;
多态:不同类的对象调用相同的方法,会产生不同的执行结果,提升代码的灵活性和扩展性。
1.4 面向对象的适用场景
面向对象并非只能用于大型项目,全场景均可适配,不同岗位开发者都能借助OOP提升开发效率:
前端开发者:封装DOM操作、组件逻辑、数据处理模块,提升代码复用性;
后端开发者:搭建业务模块、封装数据库操作、接口逻辑,实现业务解耦;
运维工程师:编写自动化运维脚本、封装服务器操作、日志处理工具,简化批量操作;
AI爱好者:封装数据预处理、模型训练、推理模块,实现AI工程化落地;
初级开发者:培养模块化编程思维,为后续进阶学习打下坚实基础。
二、类的定义与使用
2.1 类的定义语法
Python中使用class关键字定义类,class是“类”的固定标识,后面紧跟类名,类名遵循大驼峰命名法(每个单词首字母大写),这是Python面向对象编程的规范。
2.1.1 完整定义语法
class 类名:
"""类的文档字符串:说明类的功能、作用"""
# 类属性(所有对象共享的属性)
属性名 = 属性值
# 实例方法(对象可调用的方法)
def 方法名(self, 参数列表):
"""方法文档字符串"""
方法体代码
2.1.2 语法细节拆解
-
class关键字:定义类的固定标识,全小写,不可修改;
-
类名:自定义名称,遵循大驼峰命名法(如Student、Person、Car),禁止使用Python关键字,见名知意;
-
冒号:固定语法,代表类体开始,不可省略;
-
类文档字符串:三引号包裹,用于说明类的功能,规范编程必备,可通过help()查看;
-
类属性:定义在类内部、方法外部的属性,所有该类的对象共享一份,不单独占用内存;
-
实例方法:类内部定义的函数,代表对象的行为,第一个参数必须是self,代表对象本身。
2.2 对象的创建与使用
2.2.1 创建对象(实例化对象)
类定义完成后,并不会分配内存,需要通过类创建对象,这个过程也叫实例化,语法极为简单:
# 对象名 = 类名()
对象名 = 类名(参数列表)
2.2.2 调用对象的属性和方法
对象创建完成后,可通过**点号(.)**调用对象的属性和方法,这是面向对象最常用的操作。
# 调用属性
对象名.属性名
# 调用方法
对象名.方法名(参数列表)
2.3 self关键字详解
self是实例方法的第一个参数,是Python自动定义的形参,无需手动传入实参,解释器会自动将当前调用方法的对象传递给self。
-
self本质:代表当前调用方法的对象本身,哪个对象调用方法,self就指向哪个对象;
-
self作用:在类的内部,通过self调用当前对象的属性和方法,区分不同对象的属性;
-
self名称:并非固定必须叫self,只是编程规范约定,也可以用其他名称,但强烈建议使用self。
2.4 类与对象基础实战
# 定义一个人类Person
class Person:
"""人类:描述人的特征和行为"""
# 类属性:所有对象共享
species = "人类"
# 实例方法:定义人的行为
def eat(self):
"""人的吃饭行为"""
# self指向调用该方法的对象
print(f"{self.name}正在吃饭")
def sleep(self):
"""人的睡觉行为"""
print(f"{self.name}正在睡觉")
# 实例化对象:创建具体的人
p1 = Person()
# 给对象p1添加属性
p1.name = "张三"
p1.age = 20
# 调用对象属性和方法
print(f"物种:{p1.species}") # 调用类属性
print(f"姓名:{p1.name}") # 调用实例属性
p1.eat() # 调用实例方法
p1.sleep()
# 创建第二个对象
p2 = Person()
p2.name = "李四"
p2.age = 21
p2.eat()
p2.sleep()
2.5 动态绑定属性与方法
Python是动态语言,允许在创建对象后,动态给对象绑定新的属性和方法,且不同对象的属性相互独立,这是Python面向对象的一大灵活特性。
# 沿用上述Person类
# 给p1动态绑定性别属性
p1.gender = "男"
print(p1.gender)
# 给p2动态绑定身高属性
p2.height = 180
print(p2.height)
# 动态绑定方法
def run(self):
print(f"{self.name}正在跑步")
# 给p1绑定run方法
p1.run = run
p1.run(p1)
⚠️ 注意:动态绑定的属性和方法,只属于当前对象,其他同类型对象无法调用。
三、类的构造方法
3.1 构造方法的作用
在之前的代码中,我们需要手动给对象添加属性,操作繁琐且容易出错。为了解决这个问题,Python提供了构造方法,这是类中一个特殊的实例方法,无需手动调用,创建对象时会自动执行。
构造方法的核心作用:创建对象的同时,初始化对象的属性,给对象的属性赋值,让每个对象创建后就拥有对应的属性值。
3.2 构造方法的定义语法
构造方法是Python内置的固定方法,方法名固定为**init**(前后各两个下划线),语法如下:
def __init__(self, 参数1, 参数2, ...):
# 初始化对象属性
self.属性名1 = 参数1
self.属性名2 = 参数2
# 可编写初始化执行的代码
3.3 构造方法核心特点
-
方法名固定为__init__,不可修改,修改后就不是构造方法;
-
创建对象时自动调用,无需手动调用,且只会执行一次;
-
第一个参数必须是self,代表当前对象;
-
构造方法没有返回值,默认返回None,禁止写return返回其他数据;
-
一个类中只能定义一个构造方法,Python不支持构造方法重载。
3.4 构造方法实战示例
# 定义学生类,使用构造方法初始化属性
class Student:
"""学生类:使用构造方法初始化属性"""
# 构造方法:创建对象时自动执行,初始化属性
def __init__(self, name, age, score, grade):
"""
构造方法:初始化学生属性
:param name: 学生姓名
:param age: 学生年龄
:param score: 学生成绩
:param grade: 学生年级
"""
# 将参数赋值给对象属性,self.属性名 代表对象的属性
self.name = name
self.age = age
self.score = score
self.grade = grade
# 实例方法:打印学生信息
def print_stu_info(self):
print(f"年级:{self.grade},姓名:{self.name},年龄:{self.age},成绩:{self.score}")
# 创建学生对象:创建时传入参数,构造方法自动初始化属性
stu1 = Student("张三", 18, 95, "高三1班")
stu1.print_stu_info()
stu2 = Student("李四", 17, 92, "高二2班")
stu2.print_stu_info()
# 直接调用对象属性
print(f"stu1的姓名:{stu1.name}")
print(f"stu2的成绩:{stu2.score}")
3.5 无参构造方法
如果对象属性不需要外部传参,可定义无参构造方法,直接给属性赋固定值:
class Car:
# 无参构造方法
def __init__(self):
self.brand = "特斯拉"
self.color = "黑色"
self.price = 250000
def print_car_info(self):
print(f"品牌:{self.brand},颜色:{self.color},价格:{self.price}")
# 创建对象,无需传参
car = Car()
car.print_car_info()
四、类的访问权限
4.1 访问权限的意义
在实际开发中,对象的某些属性和方法,不希望被外部直接访问和修改,比如用户的密码、银行卡信息、内部核心方法等。如果随意被外部修改,会导致数据不安全、程序逻辑混乱。
Python通过设置访问权限,限制外部对类内部属性和方法的访问,实现数据隐藏,保证数据安全性,这也是封装特性的基础。
4.2 Python类的三种访问权限
4.2.1 公有属性/公有方法(Public)
公有权限是Python类默认的访问权限,属性名和方法名前无任何下划线,类内部、类外部、子类中都可以自由访问,无任何限制。
class Person:
def __init__(self, name):
# 公有属性:无下划线
self.name = name
# 公有方法:无下划线
def public_method(self):
print(f"公有方法:{self.name}")
# 外部直接访问公有属性和方法
p = Person("张三")
print(p.name)
p.public_method()
4.2.2 受保护属性/受保护方法(Protected)
受保护权限的属性和方法,属性名/方法名前有一个下划线(_),约定俗成表示该属性/方法为受保护类型。
规则:允许类内部和子类访问,不建议类外部直接访问,但Python语法上不强制限制,属于编程规范层面的约束。
class Person:
def __init__(self, name):
# 受保护属性:单下划线
self._name = name
# 受保护方法:单下划线
def _protected_method(self):
print(f"受保护方法:{self._name}")
# 外部不建议访问,但语法上可以访问
p = Person("张三")
print(p._name)
p._protected_method()
4.2.3 私有属性/私有方法(Private)
私有权限是最高级别的访问限制,属性名/方法名前有两个下划线(__),只能在当前类内部访问,类外部、子类都无法直接访问,彻底保证数据安全。
这是Python实现封装的核心方式,也是实际开发中保护敏感数据最常用的权限设置。
class Person:
def __init__(self, name, password):
self.name = name
# 私有属性:双下划线,存储敏感密码
self.__password = password
# 私有方法:双下划线
def __private_method(self):
print(f"私有方法:密码为{self.__password},仅内部可访问")
# 公有方法:内部调用私有属性和方法,对外提供接口
def get_password(self):
# 类内部可自由调用私有属性和方法
self.__private_method()
return self.__password
# 实例化对象
p = Person("张三", "123456")
# 1. 外部直接访问私有属性:报错 AttributeError
# print(p.__password)
# 2. 外部直接调用私有方法:报错 AttributeError
# p.__private_method()
# 3. 通过公有方法间接访问私有属性和方法
print(p.get_password())
4.3 私有属性的修改与获取
私有属性无法被外部直接访问和修改,想要操作私有属性,必须在类内部定义公有方法,通过公有方法间接实现,这样可以在方法中添加数据校验逻辑,避免非法数据。
class Student:
def __init__(self, name, score):
self.name = name
# 私有成绩属性
self.__score = score
# 获取私有属性:get方法
def get_score(self):
return self.__score
# 修改私有属性:set方法,添加数据校验
def set_score(self, score):
# 校验成绩是否合法
if 0 <= score <= 100:
self.__score = score
print("成绩修改成功")
else:
print("成绩不合法,修改失败")
# 创建对象
stu = Student("张三", 95)
# 获取私有属性
print(stu.get_score())
# 修改私有属性
stu.set_score(98)
print(stu.get_score())
# 传入非法成绩
stu.set_score(105)
4.4 名称改写(Name Mangling)原理
Python并非真正意义上禁止访问私有属性,而是通过名称改写机制,将私有属性/方法的名称进行了修改。私有属性__attr会被改写成_类名__attr,因此理论上仍可通过改写后的名称访问,但强烈不建议这样做。
# 沿用上述Student类
stu = Student("张三", 95)
# 通过名称改写强行访问私有属性(不推荐)
print(stu._Student__score)
五、继承
5.1 继承的核心意义
继承是面向对象三大特性之一,也是实现代码复用的核心手段。现实生活中,继承意味着子女继承父母的财产和特征;在编程中,继承指一个类(子类/派生类)可以获取另一个类(父类/基类)的所有公有属性和公有方法,无需重复编写相同代码,同时子类可以扩展自身独有的属性和方法,甚至重写父类方法。
比如定义一个动物类(父类),拥有吃饭、睡觉的属性和方法;再定义猫类、狗类(子类),直接继承动物类,就无需重新编写吃饭、睡觉的代码,只需编写自身独有的抓老鼠、看家等方法即可。
5.2 继承的语法
# 父类/基类
class 父类名:
父类代码
# 子类/派生类
class 子类名(父类名):
"""子类文档字符串"""
# 子类独有属性和方法
子类代码
5.2.1 语法说明
-
子类名后紧跟小括号,括号内写入要继承的父类名;
-
子类默认继承父类所有公有属性和公有方法;
-
子类无法继承父类的私有属性和私有方法;
-
所有Python类默认继承object类(Python所有类的顶级父类)。
5.3 单继承实战
单继承指子类只继承一个父类,是最常用的继承方式,结构清晰、逻辑简单。
# 父类:动物类
class Animal:
"""动物父类"""
def __init__(self, name):
self.name = name
# 父类公有方法
def eat(self):
print(f"{self.name}正在吃饭")
def sleep(self):
print(f"{self.name}正在睡觉")
# 子类:狗类,继承自动物类
class Dog(Animal):
"""狗类:继承自动物类"""
# 子类独有方法
def watch_home(self):
print(f"{self.name}正在看家")
# 子类:猫类,继承自动物类
class Cat(Animal):
"""猫类:继承自动物类"""
# 子类独有方法
def catch_mouse(self):
print(f"{self.name}正在抓老鼠")
# 创建子类对象,直接使用父类属性和方法
dog = Dog("旺财")
dog.eat() # 调用父类方法
dog.sleep() # 调用父类方法
dog.watch_home()# 调用子类独有方法
cat = Cat("咪咪")
cat.eat()
cat.sleep()
cat.catch_mouse()
5.4 子类重写父类方法
子类可以定义与父类同名的方法,这个操作叫做方法重写。当子类对象调用该方法时,会优先执行子类重写后的方法,而非父类方法,实现子类的个性化逻辑。
# 父类
class Animal:
def __init__(self, name):
self.name = name
def shout(self):
print(f"{self.name}正在叫")
# 子类:狗,重写shout方法
class Dog(Animal):
# 重写父类shout方法
def shout(self):
print(f"{self.name}汪汪叫")
# 子类:猫,重写shout方法
class Cat(Animal):
# 重写父类shout方法
def shout(self):
print(f"{self.name}喵喵叫")
# 调用重写后的方法
dog = Dog("旺财")
dog.shout() # 执行子类重写的方法
cat = Cat("咪咪")
cat.shout()
5.5 子类调用父类方法
子类重写父类方法后,仍想调用父类的原方法,可通过**super()**关键字实现,super()代表父类对象,可直接调用父类的属性和方法。
class Dog(Animal):
def shout(self):
# 调用父类原方法
super().shout()
# 子类扩展逻辑
print(f"{self.name}的叫声很响亮")
dog = Dog("旺财")
dog.shout()
5.6 子类构造方法
子类定义构造方法时,必须先调用父类的构造方法,初始化父类属性,再初始化子类独有属性。
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
# 子类构造方法
def __init__(self, name, breed):
# 调用父类构造方法,初始化父类属性
super().__init__(name)
# 子类独有属性
self.breed = breed
def print_dog_info(self):
print(f"狗狗姓名:{self.name},品种:{self.breed}")
dog = Dog("旺财", "金毛")
dog.print_dog_info()
六、多态
6.1 多态的核心概念
多态是面向对象三大特性之一,建立在继承和方法重写的基础上,指不同子类对象调用同一个父类方法,会执行各自重写后的逻辑,产生不同的执行结果。
多态的核心是一个接口,多种实现,不关注对象的具体类型,只关注对象是否拥有对应的方法,极大提升了代码的灵活性和扩展性,降低代码耦合度。
6.2 多态的前提条件
-
必须存在继承关系(子类继承父类);
-
子类必须重写父类的同名方法;
-
父类引用指向子类对象(Python中无需刻意实现,自动支持)。
6.3 多态实战示例
# 1. 定义父类
class Animal:
def __init__(self, name):
self.name = name
# 父类方法:等待子类重写
def shout(self):
pass
# 2. 定义子类,继承父类并重写方法
class Dog(Animal):
def shout(self):
return f"{self.name}汪汪叫"
class Cat(Animal):
def shout(self):
return f"{self.name}喵喵叫"
class Duck(Animal):
def shout(self):
return f"{self.name}嘎嘎叫"
# 3. 定义统一调用接口,实现多态
def animal_shout(animal):
"""
统一调用方法:不关注传入的具体对象类型
只要传入的是Animal子类对象,都能调用shout方法
"""
print(animal.shout())
# 4. 创建不同子类对象,传入同一个方法,产生不同结果
dog = Dog("旺财")
cat = Cat("咪咪")
duck = Duck("小黄")
# 多态体现:同一个方法,不同对象执行结果不同
animal_shout(dog)
animal_shout(cat)
animal_shout(duck)
6.4 Python多态的特点:鸭子类型
Python是动态语言,支持鸭子类型,这是一种特殊的多态实现,不强制要求继承关系,只要对象拥有对应的方法,就可以调用,无需关注对象是否属于同一父类。
💡 鸭子类型:如果一只生物走路像鸭子、游泳像鸭子、叫声像鸭子,那么它就可以被当做鸭子。
# 无继承关系,但是拥有同名方法
class Car:
def run(self):
print("汽车正在奔跑")
class Bike:
def run(self):
print("自行车正在奔跑")
# 统一调用方法,只要有run方法即可
def device_run(device):
device.run()
# 鸭子类型实现多态
car = Car()
bike = Bike()
device_run(car)
device_run(bike)
七、封装
7.1 封装的核心意义
封装是面向对象三大特性之一,是保护数据、简化代码的核心手段。封装就是把对象的属性和实现细节隐藏起来,对外只暴露公共的调用接口,外部无需关注内部实现逻辑,只需通过接口调用即可。
封装的核心优势:
隐藏实现细节,避免外部随意修改,保证数据安全;
简化代码调用,外部无需了解复杂内部逻辑;
可在接口中添加校验逻辑,保证数据合法性;
降低代码耦合度,方便后期维护和扩展。
7.2 封装的实现方式
Python中通过私有权限+公有接口实现封装,具体步骤:
-
将对象的敏感属性设置为私有属性(双下划线);
-
将内部实现细节的方法设置为私有方法;
-
定义公有方法(get/set方法),作为对外接口,间接操作私有属性;
-
在公有方法中添加数据校验,保证数据合法。
7.3 封装完整实战
# 封装用户类,保护用户敏感信息
class User:
"""用户类:实现完整封装"""
def __init__(self, username, password, phone):
# 公有属性:可对外暴露
self.username = username
# 私有属性:敏感信息,禁止外部直接访问
self.__password = password
self.__phone = phone
# 私有方法:内部逻辑,禁止外部调用
def __check_password(self, old_pwd):
"""私有方法:校验旧密码,内部使用"""
return self.__password == old_pwd
# 公有接口:获取手机号(隐藏中间四位)
def get_phone(self):
return self.__phone[:3] + "****" + self.__phone[-4:]
# 公有接口:修改密码
def set_password(self, old_pwd, new_pwd):
"""
修改密码:先校验旧密码,再修改
:param old_pwd: 旧密码
:param new_pwd: 新密码
:return: 修改结果
"""
# 调用私有方法校验
if self.__check_password(old_pwd):
# 校验新密码长度
if len(new_pwd) >= 6:
self.__password = new_pwd
return "密码修改成功"
else:
return "新密码长度过短,修改失败"
else:
return "旧密码错误,修改失败"
# 创建用户对象
user = User("张三", "123456", "13800138000")
# 1. 访问公有属性
print(f"用户名:{user.username}")
# 2. 通过公有接口获取脱敏手机号
print(f"手机号:{user.get_phone()}")
# 3. 通过公有接口修改密码
print(user.set_password("123456", "654321"))
# 4. 旧密码错误,修改失败
print(user.set_password("123", "789012"))
# 5. 直接访问私有属性:报错
# print(user.__password)
7.4 封装的层级把控
实际开发中,无需将所有属性都设为私有,非敏感数据可设为公有,敏感数据(密码、身份证、银行卡、密钥等)必须封装;内部核心逻辑、辅助方法设为私有,对外提供简洁易用的公共接口即可。
八、多重继承
8.1 多重继承的概念
单继承是子类继承一个父类,而多重继承指一个子类同时继承多个父类,可以获取所有父类的公有属性和方法,极大提升代码复用性。
比如一个学生既是学生,也是运动员,同时拥有学生类和运动员类的属性和方法,就可以通过多重继承实现。
8.2 多重继承语法
class 子类名(父类名1, 父类名2, ...):
"""多重继承子类"""
子类代码
8.3 多重继承实战
# 父类1:学生类
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def study(self):
print(f"{self.name}是{self.grade}学生,正在学习")
# 父类2:运动员类
class Athlete:
def __init__(self, sport):
self.sport = sport
def train(self):
print(f"擅长{self.sport},正在训练")
# 子类:学生运动员,多重继承
class StudentAthlete(Student, Athlete):
"""多重继承子类:同时继承Student和Athlete"""
# 子类构造方法,初始化所有父类属性
def __init__(self, name, grade, sport):
# 初始化第一个父类
Student.__init__(self, name, grade)
# 初始化第二个父类
Athlete.__init__(self, sport)
# 子类独有方法
def show_info(self):
print(f"{self.name},年级:{self.grade},运动项目:{self.sport}")
# 创建多重继承子类对象
sa = StudentAthlete("张三", "高三1班", "篮球")
# 调用父类1方法
sa.study()
# 调用父类2方法
sa.train()
# 调用子类独有方法
sa.show_info()
8.4 多重继承的方法查找顺序(MRO)
多重继承中,如果多个父类拥有同名方法,Python会按照MRO(方法解析顺序)查找方法,遵循从左到右的继承顺序,优先查找左侧父类方法,再查找右侧父类方法。
可通过**类名.mro**查看方法查找顺序。
# 查看多重继承子类的方法查找顺序
print(StudentAthlete.__mro__)
8.5 多重继承注意事项
-
多重继承会让代码结构复杂化,尽量少用,优先使用单继承+组合;
-
避免多个父类出现同名属性和方法,防止调用混乱;
-
子类构造方法需要手动初始化所有父类的属性;
-
复杂场景下,优先使用mixins机制实现复用。
九、获取对象信息
9.1 判断对象类型:type()
type()函数用于获取对象的类型,判断对象属于哪个类,返回对象的类对象。
class Student:
pass
stu = Student()
# 判断对象类型
print(type(stu))
print(type(stu) == Student)
print(type(123) == int)
9.2 判断继承关系:isinstance()
isinstance()函数用于判断对象是否是指定类(或其父类)的实例,支持继承关系判断,是面向对象中最常用的类型判断方式。
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
# 判断对象是否是子类实例
print(isinstance(dog, Dog))
# 判断对象是否是父类实例
print(isinstance(dog, Animal))
# 判断对象是否是顶级父类object实例
print(isinstance(dog, object))
9.3 获取对象所有属性和方法:dir()
dir()函数用于获取对象的所有属性和方法名称,返回一个字符串列表,包含公有、私有、内置方法。
stu = Student("张三", 18)
# 获取对象所有属性和方法
print(dir(stu))
9.4 操作对象属性:getattr()、setattr()、hasattr()
通过内置函数可动态操作对象的属性,适合动态编程场景:
-
hasattr(obj, attr):判断对象是否拥有指定属性;
-
getattr(obj, attr):获取对象指定属性的值;
-
setattr(obj, attr, value):设置对象指定属性的值。
class Student:
def __init__(self, name):
self.name = name
stu = Student("张三")
# 判断是否有name属性
print(hasattr(stu, "name"))
# 获取name属性
print(getattr(stu, "name"))
# 设置age属性
setattr(stu, "age", 18)
print(stu.age)
十、类的专有方法
10.1 专有方法概念
类的专有方法也叫魔法方法、双下方法,是Python内置的特殊方法,方法名前后各有两个下划线,无需手动调用,在特定场景下会自动触发执行,让自定义类实现Python内置类型的操作,提升代码简洁度。
10.2 常用专有方法详解
10.2.1 init:构造方法
创建对象时自动触发,初始化对象属性,前文已详细讲解。
10.2.2 str:字符串输出
打印对象时自动触发,默认打印对象的内存地址,重写该方法可自定义打印内容。
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
# 重写__str__方法,自定义打印内容
def __str__(self):
return f"学生:{self.name},成绩:{self.score}"
stu = Student("张三", 95)
# 打印对象时自动触发__str__
print(stu)
10.2.3 del:析构方法
对象被销毁、程序结束时自动触发,用于释放资源、清理内存。
class Student:
def __del__(self):
print("对象被销毁,资源释放")
stu = Student()
del stu # 手动销毁对象,触发__del__
10.2.4 len:长度获取
调用len()函数获取对象长度时自动触发,需返回数值。
class MyList:
def __init__(self):
self.items = []
def add_item(self, item):
self.items.append(item)
def __len__(self):
return len(self.items)
my_list = MyList()
my_list.add_item(1)
my_list.add_item(2)
# 触发__len__方法
print(len(my_list))
10.2.5 运算符重载专有方法
通过重写专有方法,实现自定义对象的运算符操作:
-
add:加法运算符+
-
sub:减法运算符-
-
eq:等于运算符==
-
lt:小于运算符<
class Number:
def __init__(self, num):
self.num = num
# 重载加法运算符
def __add__(self, other):
return Number(self.num + other.num)
def __str__(self):
return str(self.num)
n1 = Number(10)
n2 = Number(20)
# 对象相加,触发__add__
n3 = n1 + n2
print(n3)
十一、类的底层原理
11.1 类与对象的内存布局
在Python中,类本身也是一个对象,叫做类对象,存储在堆内存中;通过类创建的对象叫做实例对象,每个实例对象都拥有独立的内存空间,存储自身的实例属性。
类对象存储了类的定义、属性、方法,所有实例对象共享类对象中的方法;实例对象单独存储自身的实例属性,不同实例之间互不干扰。
11.2 type创建类的本质
Python中一切皆对象,类本质上是type类的实例对象,我们平时使用class关键字定义类,底层都是通过type类动态创建的。type是Python的元类,也就是创建类的类,负责生成所有类对象。
11.2.1 type创建类的语法
# type(类名, 父类元组, 类属性与方法字典)
类名 = type(类名字符串, (父类,), {属性名:属性值, 方法名:函数})
11.2.2 type动态创建类实战
# 定义实例方法
def eat(self):
print(f"{self.name}正在吃饭")
# 使用type动态创建Person类
Person = type("Person", (object,), {"name":"张三","eat":eat})
# 创建对象
p = Person()
p.eat()
# 查看类的类型
print(type(Person))
print(type(p))
11.3 object顶级父类
object是Python所有类的默认顶级父类,哪怕定义类时没有写明继承object,Python解释器也会默认让其继承object类。object类中定义了很多基础的专有方法,比如__init__、str、__del__等,这也是所有自定义类都能调用这些方法的根本原因。
可以说,object是Python面向对象体系的根基,所有内置类、自定义类,最终都继承自object。
11.4 方法的存储与调用原理
类的方法并不会存储在每个实例对象中,而是存储在类对象里,所有实例对象共享类中的方法。当实例对象调用方法时,Python解释器会先在当前实例对象中查找方法,找不到则去类对象中查找,类对象中没有就去父类中查找,遵循MRO方法解析顺序,直到找到对应的方法,最终完成调用。
这种存储方式极大节省了内存空间,避免了重复存储相同的方法代码,也是面向对象代码复用的底层支撑。
十二、具体项目实战
理论知识最终都要落地到实际开发中,本章节将结合前端、后端、运维、AI全场景,通过三个完整的实战项目,把面向对象所有知识点融会贯通,所有代码均可直接运行、直接复用至实际工作中。
项目一:学生信息管理系统(后端/全栈入门)
12.1.1 项目需求
-
实现学生信息的添加、删除、修改、查询功能;
-
对学生敏感成绩进行封装,防止外部随意修改;
-
支持批量查看所有学生信息;
-
加入数据校验,保证信息合法性。
12.1.2 项目实现代码(完整可运行)
# 学生类:封装学生属性与方法
class Student:
def __init__(self, stu_id, name, age, grade):
# 学生基础信息
self.stu_id = stu_id
self.name = name
self.age = age
self.grade = grade
# 私有成绩属性,外部无法直接修改
self.__score = 0
# 设置成绩:带数据校验
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
return True
return False
# 获取成绩
def get_score(self):
return self.__score
# 返回学生信息字符串
def show_stu_info(self):
return f"学号:{self.stu_id} | 姓名:{self.name} | 年龄:{self.age} | 年级:{self.grade} | 成绩:{self.get_score()}"
# 学生管理类:核心业务逻辑
class StudentManager:
def __init__(self):
# 存储所有学生对象
self.stu_list = []
# 添加学生
def add_student(self, student):
# 校验学号是否重复
for stu in self.stu_list:
if stu.stu_id == student.stu_id:
print("该学号已存在,添加失败!")
return False
self.stu_list.append(student)
print("学生添加成功!")
return True
# 根据学号删除学生
def del_student(self, stu_id):
for stu in self.stu_list:
if stu.stu_id == stu_id:
self.stu_list.remove(stu)
print("学生删除成功!")
return True
print("未找到该学生,删除失败!")
return False
# 修改学生成绩
def modify_score(self, stu_id, score):
for stu in self.stu_list:
if stu.stu_id == stu_id:
if stu.set_score(score):
print("成绩修改成功!")
return True
else:
print("成绩不合法,修改失败!")
return False
print("未找到该学生!")
return False
# 查询所有学生
def show_all_student(self):
if not self.stu_list:
print("暂无学生信息!")
return
print("n========== 所有学生信息 ==========")
for index, stu in enumerate(self.stu_list, 1):
print(f"{index}. {stu.show_stu_info()}")
print("==================================n")
# 项目测试
if __name__ == '__main__':
# 创建学生管理对象
manager = StudentManager()
# 创建学生对象
stu1 = Student("1001", "张三", 18, "高三1班")
stu1.set_score(95)
stu2 = Student("1002", "李四", 17, "高二2班")
stu2.set_score(92)
# 添加学生
manager.add_student(stu1)
manager.add_student(stu2)
# 查看所有学生
manager.show_all_student()
# 修改学生成绩
manager.modify_score("1001", 98)
# 删除学生
# manager.del_student("1002")
# 再次查看
manager.show_all_student()
项目二:运维日志分析工具(运维场景)
12.2.1 项目需求
-
封装日志读取、过滤、统计功能;
-
支持按关键词筛选日志、统计日志行数;
-
代码复用性强,可直接对接服务器日志;
-
加入异常处理,防止日志文件读取报错。
12.2.2 项目实现代码(完整可运行)
class LogAnalyzer:
"""运维日志分析工具类"""
def __init__(self, file_path):
self.file_path = file_path
# 存储日志内容
self.log_content = []
# 读取日志文件
def read_log(self):
try:
with open(self.file_path, "r", encoding="utf-8") as f:
self.log_content = f.readlines()
print(f"日志读取成功,共{len(self.log_content)}行日志")
return True
except Exception as e:
print(f"日志读取失败:{e}")
return False
# 按关键词过滤日志
def filter_log(self, keyword):
if not self.log_content:
print("请先读取日志文件!")
return []
filter_result = [line.strip() for line in self.log_content if keyword in line]
print(f"关键词【{keyword}】筛选结果:共{len(filter_result)}条")
return filter_result
# 统计日志总行数
def count_log_lines(self):
return len(self.log_content)
# 导出筛选后的日志
def export_filter_log(self, keyword, export_path):
filter_data = self.filter_log(keyword)
if not filter_data:
return False
with open(export_path, "w", encoding="utf-8") as f:
f.write("n".join(filter_data))
print(f"筛选日志导出成功,路径:{export_path}")
return True
# 测试(可新建log.txt文件测试)
if __name__ == '__main__':
# 创建日志分析对象
analyzer = LogAnalyzer("log.txt")
# 读取日志
analyzer.read_log()
# 筛选错误日志
analyzer.filter_log("ERROR")
# 统计总行数
print(f"日志总行数:{analyzer.count_log_lines()}")
# 导出错误日志
# analyzer.export_filter_log("ERROR", "error_log.txt")
项目三:AI数据预处理类(AI爱好者场景)
12.3.1 项目需求
-
封装AI训练数据预处理常用功能;
-
支持数据清洗、归一化、缺失值填充;
-
适配机器学习、深度学习前期数据处理;
-
代码简洁,可直接嵌入AI训练流程。
12.3.2 项目实现代码(完整可运行)
class AIDataPreprocess:
"""AI数据预处理类"""
def __init__(self, data):
# 初始化原始数据
self.data = data
self.processed_data = []
# 数据清洗:去除空值、异常值
def data_clean(self):
self.processed_data = [item for item in self.data if item is not None and str(item).strip() != ""]
print(f"数据清洗完成,原始数据:{len(self.data)}条,清洗后:{len(self.processed_data)}条")
return self.processed_data
# 缺失值填充
def fill_missing_value(self, fill_value=0):
if not self.processed_data:
self.data_clean()
self.processed_data = [fill_value if item is None else item for item in self.processed_data]
print("缺失值填充完成")
return self.processed_data
# 数据归一化(0-1标准化)
def data_normalization(self):
if not self.processed_data:
print("请先进行数据清洗!")
return []
try:
max_val = max(self.processed_data)
min_val = min(self.processed_data)
if max_val == min_val:
self.processed_data = [0.0 for _ in self.processed_data]
else:
self.processed_data = [(item - min_val)/(max_val - min_val) for item in self.processed_data]
print("数据归一化完成")
return self.processed_data
except Exception as e:
print(f"归一化失败:{e}")
return []
# 测试
if __name__ == '__main__':
# 原始数据(含缺失值、异常值)
raw_data = [12, 25, None, 36, 48, 29, None, 55, 0]
# 创建数据预处理对象
preprocess = AIDataPreprocess(raw_data)
# 数据清洗
preprocess.data_clean()
# 缺失值填充
preprocess.fill_missing_value()
# 数据归一化
result = preprocess.data_normalization()
print(f"预处理后数据:{result}")
十三、常见面试题汇总及答案
面向对象是Python岗位面试必考知识点,本章节整理了前端、后端、运维、AI岗位高频面试题,涵盖基础概念、代码实操、原理剖析,附带标准答案,助力面试通关。
13.1 基础概念类面试题
13.1.1 什么是面向对象?面向对象和面向过程有什么区别?
答案:面向对象是一种以对象为核心的编程思想,将现实事物抽象为对象,绑定属性与方法,注重“谁来做”;面向过程是以过程为核心,注重解决问题的步骤,注重“怎么做”。
区别:面向过程适合小型脚本,上手简单,但复用性、扩展性差;面向对象适合大型项目,代码复用性强、耦合度低、易维护,更贴合复杂业务场景。
13.1.2 简述面向对象三大特性及其作用
答案:三大特性为封装、继承、多态。封装通过私有化属性隐藏内部细节,保护数据安全,对外暴露调用接口;继承实现子类复用父类属性与方法,减少代码冗余;多态实现一个接口多种实现,提升代码灵活性与扩展性。
13.1.3 什么是类?什么是对象?二者有什么关系?
答案:类是对一类事物共性的抽象,是模板,不占用内存;对象是类的具体实例,是真实存在的个体,占用内存。二者关系为:类是对象的模板,对象是类的实例,一个类可以创建无数个对象。
13.1.4 Python中self关键字的作用是什么?
答案:self代表当前调用方法的对象本身,用于在类内部区分不同对象的属性与方法,绑定对象与属性,保证每个对象调用方法时,操作的都是自身的属性。
13.1.5 __init__方法有什么作用?可以有返回值吗?
答案:__init__是构造方法,创建对象时自动调用,用于初始化对象属性;该方法没有返回值,默认返回None,禁止return返回除None外的其他数据,否则会报错。
13.2 权限与封装面试题
13.2.1 Python中公有、受保护、私有属性/方法的区别?
答案:公有属性/方法无下划线,内外均可自由访问;受保护属性/方法单下划线,仅允许类内部与子类访问,外部不建议访问;私有属性/方法双下划线,仅允许当前类内部访问,外部与子类均无法直接访问。
13.2.2 如何访问和修改类的私有属性?
答案:无法直接访问修改,需要在类内部定义公有get/set方法,通过公有方法间接访问和修改私有属性,同时可在方法中添加数据校验,保证数据合法性。
13.2.3 Python的私有属性是真正的私有吗?
答案:不是,Python通过名称改写机制,将私有属性__attr改名为_类名__attr,理论上可通过改写后的名称强行访问,但编程规范中禁止这种操作。
13.3 继承与多态面试题
13.3.1 什么是方法重写?重写与重载有什么区别?
答案:方法重写是子类定义与父类同名的方法,覆盖父类方法逻辑;重载是同一个类中存在多个同名方法,通过参数列表区分。Python不支持方法重载,可通过默认参数、可变参数模拟,仅支持方法重写。
13.3.2 什么是多重继承?MRO是什么?
答案:多重继承是一个子类同时继承多个父类;MRO是方法解析顺序,Python通过MRO确定多重继承时方法的查找顺序,遵循从左到右的原则,可通过类名.__mro__查看。
13.3.3 什么是多态?Python多态有什么特点?
答案:多态是不同子类对象调用同一父类方法,产生不同执行结果;Python多态基于鸭子类型,不强制要求继承关系,只要对象拥有对应方法,即可实现多态,灵活性极强。
13.3.4 子类可以继承父类的私有属性和方法吗?
答案:不可以,父类私有属性和方法仅能在当前父类内部访问,子类无法直接继承和调用,可通过父类公有方法间接获取。
13.4 原理与实操面试题
13.4.1 Python中创建类的本质是什么?
答案:Python中一切皆对象,类本质是type元类的实例对象,使用class关键字定义类,底层都是通过type类动态创建的。
13.4.2 super()关键字的作用是什么?
答案:super()用于调用父类的方法与属性,常用于子类重写父类方法后,调用父类原方法,也用于子类构造方法中初始化父类属性。
13.4.3 简述__str__和__repr__方法的区别
答案:__str__面向用户,打印对象时自动调用,返回可读性强的字符串;__repr__面向开发者,在交互式环境中直接输入对象时调用,返回可重新创建该对象的字符串。
十四、总结
至此,Python3面向对象编程的所有核心知识点、实战技巧、底层原理、面试考点已经全部讲解完毕,全文从基础的类与对象定义,到三大核心特性,再到实战落地与面试答疑,形成了完整的知识闭环,无论你是零基础入门、还是职场进阶,都能通过本文彻底吃透Python面向对象。
Python面向对象的核心,是学会用抽象思维看待现实事物,把复杂业务拆解为一个个对象,通过封装、继承、多态,实现代码的高复用、高扩展、易维护。对于不同岗位的开发者而言,面向对象都是必备技能:后端开发者靠它搭建业务架构,前端开发者用它封装组件,运维工程师用它简化自动化脚本,AI爱好者用它实现工程化落地,掌握面向对象,就是掌握了大型项目开发的核心思维。
学习面向对象不能只停留在理论层面,一定要多动手编写代码,尝试把日常的脚本代码改写成面向对象写法,结合本文的实战项目反复练习,吃透每一个知识点、每一行代码。同时要牢记面试高频考点,夯实理论基础,做到理论与实操双精通。
后续还可以深入学习Python元类、设计模式等进阶内容,在面向对象的基础上进一步提升代码质量,成为更优秀的开发者。如果在学习、开发、面试过程中遇到问题,随时翻阅本文,相信每一次重读都会有新的收获。
🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近您与目标的距离。 💡 如果本文对你有帮助,不妨 👍
点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)