在这里插入图片描述

文章目录


前言

在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中通过私有权限+公有接口实现封装,具体步骤:

  1. 将对象的敏感属性设置为私有属性(双下划线);

  2. 将内部实现细节的方法设置为私有方法;

  3. 定义公有方法(get/set方法),作为对外接口,间接操作私有属性;

  4. 在公有方法中添加数据校验,保证数据合法。

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元类、设计模式等进阶内容,在面向对象的基础上进一步提升代码质量,成为更优秀的开发者。如果在学习、开发、面试过程中遇到问题,随时翻阅本文,相信每一次重读都会有新的收获。


🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近您与目标的距离。 💡 如果本文对你有帮助,不妨 👍
点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐