人工智能之编程进阶 Python高级
第二章 面向对象
前言
Python 是一种面向对象编程(Object-Oriented Programming, OOP) 语言,支持封装、继承、多态等核心特性。掌握面向对象编程是编写结构清晰、可维护、可复用代码的关键。
一、面向对象核心概念
| 概念 | 说明 |
|---|---|
| 类(Class) | 对象的蓝图或模板,定义属性和方法 |
| 对象(Object) | 类的实例,具有具体的状态和行为 |
| 封装(Encapsulation) | 将数据和操作数据的方法绑定在一起,隐藏内部实现 |
| 继承(Inheritance) | 子类继承父类的属性和方法,实现代码复用 |
| 多态(Polymorphism) | 同一接口,不同实现(如方法重写) |
| 抽象(Abstraction) | 隐藏复杂细节,只暴露必要接口 |
二、定义类和创建对象
1. 基本语法
class ClassName:
# 类属性(所有实例共享)
class_variable = "共享值"
# 构造方法(初始化)
def __init__(self, param1, param2):
# 实例属性(每个对象独有)
self.instance_var1 = param1
self.instance_var2 = param2
# 实例方法
def method(self):
return f"{self.instance_var1} - {self.instance_var2}"
# 创建对象(实例化)
obj = ClassName("值1", "值2")
print(obj.method()) # 值1 - 值2
2. __init__ 方法
- 类似于构造函数,用于初始化对象
- 第一个参数必须是
self(代表当前实例)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f"Hello, I'm {self.name}, {self.age} years old."
p = Person("Alice", 25)
print(p.greet()) # Hello, I'm Alice, 25 years old.
三、封装与访问控制
Python 没有严格的 private/public 关键字,但通过命名约定实现封装:
| 命名方式 | 含义 | 访问建议 |
|---|---|---|
name | 公有属性 | 可自由访问 |
_name | 受保护属性 | “内部使用”,但可访问 |
__name | 私有属性 | 名称改写(name mangling),外部不可直接访问 |
示例
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner # 公有
self._balance = balance # 受保护(约定)
self.__pin = "1234" # 私有(实际为 _BankAccount__pin)
def deposit(self, amount):
if amount > 0:
self._balance += amount
def get_balance(self, pin):
if pin == self.__pin:
return self._balance
else:
raise ValueError("Invalid PIN")
acc = BankAccount("Alice", 100)
print(acc.owner) # ✅ 可访问
print(acc._balance) # ⚠️ 可访问,但不推荐
# print(acc.__pin) # ❌ AttributeError
print(acc._BankAccount__pin) # ✅ 但强烈不推荐(破坏封装)
✅ 最佳实践:使用属性(
@property)提供安全的访问接口。
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("温度不能低于绝对零度")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
temp = Temperature(25)
print(temp.celsius) # 25
print(temp.fahrenheit) # 77.0
temp.celsius = 30 # 自动验证
# temp.celsius = -300 # 抛出异常
四、继承(Inheritance)
子类继承父类的属性和方法,并可扩展或重写。
1. 基本继承
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("子类必须实现 speak 方法")
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # Buddy says Woof!
print(cat.speak()) # Whiskers says Meow!
2. super() 调用父类
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
class Manager(Employee):
def __init__(self, name, salary, bonus):
super().__init__(name, salary) # 调用父类构造函数
self.bonus = bonus
def total_income(self):
return self.salary + self.bonus
3. 多重继承
class Flyable:
def fly(self):
return "Flying..."
class Swimmable:
def swim(self):
return "Swimming..."
class Duck(Animal, Flyable, Swimmable):
def speak(self):
return "Quack!"
duck = Duck("Donald")
print(duck.fly()) # Flying...
print(duck.swim()) # Swimming...
⚠️ 注意:多重继承可能引发 方法解析顺序(MRO) 问题,可通过
ClassName.__mro__查看。
五、多态(Polymorphism)
同一接口,不同实现。Python 是动态类型语言,天然支持多态。
def animal_sound(animal: Animal):
print(animal.speak()) # 不关心具体类型,只要实现 speak()
animals = [Dog("Buddy"), Cat("Whiskers"), Duck("Donald")]
for a in animals:
animal_sound(a)
输出:
Buddy says Woof!
Whiskers says Meow!
Donald says Quack!
六、特殊方法(魔术方法 / Dunder Methods)
以双下划线开头和结尾的方法,用于自定义类的行为。
| 方法 | 用途 | 示例 |
|---|---|---|
__str__ | 用户友好的字符串表示 | print(obj) |
__repr__ | 开发者友好的字符串表示 | repr(obj) |
__len__ | 支持 len() | len(obj) |
__getitem__ | 支持索引访问 | obj[0] |
__call__ | 使对象可调用 | obj() |
__eq__ | 定义相等比较 | obj1 == obj2 |
示例:自定义容器类
class MyList:
def __init__(self, items=None):
self._items = items or []
def __len__(self):
return len(self._items)
def __getitem__(self, index):
return self._items[index]
def __str__(self):
return f"MyList({self._items})"
def __repr__(self):
return f"MyList({self._items!r})"
my_list = MyList([1, 2, 3])
print(len(my_list)) # 3
print(my_list[0]) # 1
print(my_list) # MyList([1, 2, 3])
七、类方法与静态方法
| 类型 | 装饰器 | 第一个参数 | 用途 |
|---|---|---|---|
| 实例方法 | 无 | self | 操作实例数据 |
| 类方法 | @classmethod | cls | 操作类数据,常用于替代构造函数 |
| 静态方法 | @staticmethod | 无 | 与类相关但不依赖类或实例 |
class Person:
species = "Homo sapiens"
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def get_species(cls):
return cls.species
@classmethod
def from_string(cls, person_str):
# 替代构造函数
name, age = person_str.split('-')
return cls(name, int(age))
@staticmethod
def is_adult(age):
return age >= 18
# 使用
p1 = Person.from_string("Alice-25")
print(Person.get_species()) # Homo sapiens
print(Person.is_adult(17)) # False
八、抽象基类(ABC)
强制子类实现特定方法。
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class Rectangle(Shape):
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)
# rect = Shape() # ❌ 不能实例化抽象类
rect = Rectangle(3, 4)
print(rect.area()) # 12
九、数据类(@dataclass,Python 3.7+)
简化只用于存储数据的类。
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
def distance_from_origin(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
p = Point(3.0, 4.0)
print(p) # Point(x=3.0, y=4.0)
print(p.distance_from_origin()) # 5.0
自动生成 __init__, __repr__, __eq__ 等方法。
十、面向对象设计原则(SOLID 简化版)
- 单一职责:一个类只做一件事
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换:子类能替换父类而不破坏程序
- 接口隔离:客户端不应依赖它不需要的接口
- 依赖倒置:依赖抽象,而非具体实现
十一、super()在 Python 中的作用及使用注意事项
在 Python 中,super() 的核心作用是调用父类(超类)的方法,尤其在继承体系中初始化子类时,它能简洁、安全地触发父类的构造方法(__init__),避免手动硬编码父类名导致的维护问题,同时支持多继承的方法解析顺序(MRO)。
1、先明确:为什么需要 super() 调用父类构造?
当子类定义了自己的 __init__ 方法时,会覆盖父类的__init__(除非主动调用)。如果父类的 __init__ 有必要的初始化逻辑(比如初始化属性、创建资源),子类必须显式触发父类构造,否则父类的属性 / 逻辑不会生效。
反面例子(不推荐):硬编码父类名调用
class Parent:
def __init__(self, name):
self.name = name # 父类初始化核心属性
class Child(Parent):
def __init__(self, name, age):
# 硬编码父类名 Parent,若后续父类名修改/继承关系变更,此处会出错
Parent.__init__(self, name) # 手动传递 self
self.age = age # 子类新增属性
child = Child("小明", 10)
print(child.name) # 正常输出:小明(但写法不灵活)
问题:如果后续将 Child 的父类改为 NewParent,则 Parent.__init__ 会变成错误调用,维护成本高。
2、super() 构造的核心作用
1. 简洁调用父类构造,解耦父类名
super() 会自动绑定当前类(Child)和实例(self),无需手动写父类名和传递 self,代码更简洁、可维护。
class Parent:
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
# 无需写 Parent,自动找到父类并调用其 __init__
super().__init__(name) # 等价于 Parent.__init__(self, name),但更灵活
self.age = age
child = Child("小红", 12)
print(child.name) # 输出:小红
print(child.age) # 输出:12
优势:即使后续修改父类(如 class Child(NewParent):),super() 仍能自动找到正确的父类,无需修改子类代码。
2. 支持多继承的 MRO 顺序(关键作用)
Python 多继承时,方法调用遵循方法解析顺序(MRO)(可通过 类名.__mro__ 查看)。super() 会严格按照 MRO 顺序调用下一个类的方法,避免多继承时的重复调用或调用顺序错误。
例子:多继承中的 super() 协同
class A:
def __init__(self):
print("A 的构造方法")
class B(A):
def __init__(self):
print("B 的构造方法")
super().__init__() # 按 MRO 调用下一个父类(A)
class C(A):
def __init__(self):
print("C 的构造方法")
super().__init__() # 按 MRO 调用下一个父类(A)
# 多继承:D 继承 B 和 C
class D(B, C):
def __init__(self):
print("D 的构造方法")
super().__init__() # 按 MRO 调用下一个父类(B)
# 查看 D 的 MRO 顺序:D → B → C → A → object
print(D.__mro__) # 输出:(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
# 实例化 D,观察构造方法调用顺序
d = D()
输出结果:
D 的构造方法
B 的构造方法
C 的构造方法
A 的构造方法
关键:super() 并非 “调用父类”,而是 “调用 MRO 顺序中的下一个类”。如果不用 super() 而硬编码 B.__init__(self),会跳过 C 的构造,导致多继承逻辑错误。
3. 兼容 Python 2 和 3(显式参数写法)
Python 3 中 super() 可省略参数(默认 super(当前类, self)),但 Python 2 中必须显式传递类和实例:
# Python 2 兼容写法(Python 3 也支持)
class Child(Parent):
def __init__(self, name, age):
super(Child, self).__init__(name) # 显式指定类和实例
self.age = age
这种写法在多继承中更清晰,明确当前类的 MRO 起点。
3、super() 构造的使用注意事项
- 参数匹配:父类
__init__所需的参数(除self外),子类必须通过super().__init__(...)传递,否则会报错。
class Parent:
def __init__(self, name, gender):
self.name = name
self.gender = gender
class Child(Parent):
def __init__(self, name, gender, age):
super().__init__(name, gender) # 必须传递父类所需的 name 和 gender
self.age = age
- 避免循环调用:多继承中,若父类构造未正确使用
super(),可能导致循环调用(需依赖 MRO 自动避免,无需手动处理)。 - 单继承 vs 多继承:单继承中
super()作用是 “解耦父类名”,多继承中是 “遵循 MRO 顺序调用”,后者是其核心价值。 object类的__init__无参数:如果父类是object(Python 3 中所有类默认继承object),其__init__不需要参数,若子类super().__init__()传递多余参数会报错。
4、总结
super() 构造的核心作用是:
- 解耦父类名:避免硬编码父类名,提升代码可维护性;
- 遵循 MRO 顺序:在多继承中安全触发所有父类的构造逻辑,避免调用顺序错误;
- 简洁传递参数:自动绑定
self,无需手动传递。
最佳实践:只要子类继承了有 __init__ 方法的父类(或多继承场景),都应使用 super().__init__(...) 初始化父类,而非硬编码父类名。
十二、总结
本文主要讲述python的面向对象具有的特性,以便于更加方便的实现业务逻辑以及高效的编程方式,便于理解业务和开发拓展功能。具体内容总结如下
| 特性 | Python 实现方式 |
|---|---|
| 封装 | 属性命名约定 +@property |
| 继承 | class Child(Parent):+super() |
| 多态 | 鸭子类型 + 方法重写 |
| 抽象 | abc.ABC+@abstractmethod |
| 便捷数据类 | @dataclass |
✅ 最佳实践:
- 优先组合而非继承
- 使用
@property控制属性访问- 用
dataclass简化数据载体类- 抽象基类用于定义接口契约
资料关注
相关资料获取:
公众号:咚咚王
《Python编程:从入门到实践》
《利用Python进行数据分析》
《算法导论中文第三版》
《概率论与数理统计(第四版) (盛骤) 》
《程序员的数学》
《线性代数应该这样学第3版》
《微积分和数学分析引论》
《(西瓜书)周志华-机器学习》
《TensorFlow机器学习实战指南》
《Sklearn与TensorFlow机器学习实用指南》
《模式识别(第四版)》
《深度学习 deep learning》伊恩·古德费洛著 花书
《Python深度学习第二版(中文版)【纯文本】 (登封大数据 (Francois Choliet)) (Z-Library)》
《深入浅出神经网络与深度学习+(迈克尔·尼尔森(Michael+Nielsen) 》
《自然语言处理综论 第2版》
《Natural-Language-Processing-with-PyTorch》
《计算机视觉-算法与应用(中文版)》
《Learning OpenCV 4》
《AIGC:智能创作时代》杜雨+&+张孜铭
《AIGC原理与实践:零基础学大语言模型、扩散模型和多模态模型》
《从零构建大语言模型(中文版)》
《实战AI大模型》
《AI 3.0》
Python面向对象高级编程详解
1171

被折叠的 条评论
为什么被折叠?



