人工智能学习中深度学习之python基础之 类
前言:
笔者本小结主要阐述python使用中类,并整理而成的学习笔记,用于更好的掌握python的类以及其三大特征。
1.类
概念:类是 Python 面向对象编程(OOP)的基石,具有“封装”,“继承”,“多态”三大特性,我的理解本质:就是世间万物所具有的:特性(属性),行为(方法)。抽象出来形成“模板”称之为“类”(class)
class Bank:bank_name = '中国银行'address = '深圳'rate = 1.2customer_count = 0 # 实例对象计数def __init__(self, account, balance):self.account = accountif not isinstance(balance, (int, float)):raise ValueError("余额必须是数值类型")self.balance = balanceBank.customer_count += 1 # 实例计数+1def deposit(self, amount):if not Bank.is_valid_amount(amount):print("存款失败:金额必须是100的整数倍且为正数")returnself.balance += amountprint(f"存款成功,当前余额:{self.balance}")def withdraw(self, amount):if not Bank.is_valid_amount(amount):print("取款失败:金额必须是100的整数倍且为正数")returnif amount > self.balance:print("取款失败:余额不足")returnself.balance -= amountprint(f"取款成功,当前余额:{self.balance}")def inquire(self):print(f'账号:{self.account}, 金额:{self.balance}')return self.account, self.balance@classmethoddef change_rate(cls, new_rate):old_rate = cls.ratecls.rate = new_rateprint(f'修改前利息:{old_rate}, 修改后利息:{cls.rate}')@staticmethoddef is_valid_amount(amount):if not isinstance(amount, int) or amount <= 0:return Falsereturn amount % 100 == 0
依据上述案例解释核心概念:
1.类(class):抽象的 “模板”,定义对象的属性和方法
2.实例(instance):类的具体 “对象”,每个实例有独立的属性
3.属性(Attribute):分为 “类属性”(所有实例共享,如 Bank 的 bank_name)和 “实例属性”(每个实例独有,如账号 account)。
4.方法(Method):类中定义的函数,分为 “实例方法”(操作实例属性,如 deposit)、“类方法”(操作类属性,用 @classmethod,且第一个参数名为cls)、“静态方法”(无默认参数,用 @staticmethod)。
5.对象(object):根据类(模板)创建的实体,也成为实例,使用[对象.]方法调用
6.封装:将属性和方法封装到一起,保护数据,对外部隐藏内部实现的细节
7.继承:建立一个层级关系,子类可获取父类的属性和方法,避免代码复用
8.多态:不同的对象调用同一方法,产生不同的状态,提高灵活性、扩展性
关于三大特性详解:
1.1封装
简单理解就是将属性和方法封装到一个类中,隐藏内部实现细节,只通过公开接口与外部进行交互。在Python中常常以下划线【_】来控制外部属性和方法的访问权限。
1.单下划线结尾:主要用来避免系统保留字冲突,比如:def print_(): pass。
2.单下划线开头:表示该属性或方法的权限为protected,通常只允许类本身或子类进行访问
3.双下划线开头:表示该属性或方法的权限为private,这些属性和方法只允许类本身进行访问
4.首尾双下划线:称为魔法函数。如__len__、__getitem__、__call__等,作用是让该类具有该函数的特性,可以理解为类的装饰方法
注意:正常来讲python封装后,其他地方是访问不到加了下划线的属性和方法的,但是使用[实例化对象._类名__方法名称]则可以在警告的情况下强制访问私有信息
class UserInfo:def __init__(self, first_name, age):self.first_name = first_nameself._age = agedef __calc_birth(self):return f'{self.first_name} 出生于{2025 - self._age}年'e = UserInfo('wjj', 24)print(f'出生信息{e._UserInfo__calc_birth()}') #出生信息wjj 出生于2001年封装的核心要点:
1.理解属性及方法定义时前置的'_'数量以及其作用。
2.了解私有属性或方法可以强制读取。
1.2继承
本质:允许在一个类的基础上进行重写或拓展,称之为继承,其继承的类称之为父类,自身称之为子类,简单理解就是“继承”了父类的属性和方法。
单继承:一个子类继承一个父类。
多继承:一个子类继承多个父类。根据其先后顺序确定其具体继承的属性/方法的实现。
单/多继承的时候:子类调用父类中的方法有两种调用方式,常用super().方法(参数)
1.【父类.方法(self)】:硬编码调用指定父类初始化,第一个参数必须为self,跳转到指定父类初始化。
2.【super().方法(参数)】:自动遵循 MRO 顺序,自动解析下一个类,参数不需要加self。
【super().方法(参数)】是推荐的常规用法,尤其在单继承和多继承中,能自动遵循 MRO 顺序,保证继承链的完整性(例如确保所有父类的 __init__ 都被调用),减少代码维护成本(无需硬编码父类名称)
'''1.知识要点''''''
多个父类最终继承自同一个基类时,会形成 “菱形结构”,此时子类调用父类方法可能出现歧义。
Python 通过 MRO(Method Resolution Order,方法解析顺序) 解决此问题
'''#1.查看MRO顺序,类名.__mro__ 查看方法解析顺序 或 类名.mro() 查看print(AdvancedDataProcessor.__mro__)
# 输出(简化):
# (AdvancedDataProcessor, DataProcessor, LogMixin, CacheMixin, object)MRO(Method Resolution Order) 方法解析顺序,遵循以下三条原则:
1.子类的方法永远在父类前面
2.多个父类,会根据在列表中的顺序被检查
3.如果多个类中有相同的方法重写,优先选择第一个父类继承
多继承推荐使用【组合继承】
组合继承:设计类模式中,可以将继承+组合,两者结合称之为组合继承。
组合 Composition:一个类把另一个类的对象作为属性来使用 (has-a 关系)。
继承 Inheritance:一个类是另一个类的子类 (is-a 关系)
案例:
#多继承实现源代码class NewPerson:def __init__(self, name, age, **kwargs):self.name = nameself.age = agesuper().__init__(** kwargs) # 将剩余参数传递给下一级父类class NewFather(NewPerson):def __init__(self, name, age, father_job, **kwargs):super().__init__(name, age,** kwargs) # 传递参数给父类NewPersonself.father_job = father_jobclass NewMother(NewPerson):def __init__(self, name, age, mother_job, sex='女', **kwargs):super().__init__(name, age,** kwargs) # 传递参数给父类NewPersonself.mother_job = mother_jobself.sex = sexclass NewSon(NewFather, NewMother):def __init__(self, name, age, own_job, school, father_job, mother_job):# 通过super()传递所有参数,依赖MRO顺序解析super().__init__(name=name,age=age,father_job=father_job,mother_job=mother_job,sex='女' # 这里的sex会被NewMother的默认值或子类覆盖)self.school = schoolself.own_job = own_jobself.sex = '男' # 子类自身的性别# 实例化并打印结果 son = NewSon("小明", 12, "学生", "深圳小学", "医生", "护士") print(f'MRO顺序: {NewSon.__mro__}') print(f'姓名: {son.name}') print(f'年龄: {son.age}') print(f'学校: {son.school}') print(f'职业: {son.own_job}') print(f'父亲职业: {son.father_job}') print(f'母亲职业: {son.mother_job}') print(f'性别: {son.sex}')''' MRO顺序: (<class '__main__.NewSon'>, <class '__main__.NewFather'>, <class '__main__.NewMother'>, <class '__main__.NewPerson'>, <class 'object'>) 姓名: 小明 年龄: 12 学校: 深圳小学 职业: 学生 父亲职业: 医生 母亲职业: 护士 性别: 男 '''我们使用组合继承法代替多继承
核心思路:
NewSon只继承NewPerson(体现 “儿子是一个人” 的is-a关系)- 通过组合
NewFather和NewMother的实例,获取父母的信息(体现 “儿子有父亲 / 母亲” 的has-a关系)- 子类继承父类,再通过传入父类的实例,来实现属性和方法
#只继承一个父类,组合父母的实例class NewPerson:def __init__(self, name, age):self.name = nameself.age = ageclass NewFather(NewPerson):def __init__(self, name, age, father_job):super().__init__(name, age)self.father_job = father_jobclass NewMother(NewPerson):def __init__(self, name, age, mother_job, sex='女'):super().__init__(name, age)self.mother_job = mother_jobself.sex = sexclass NewSon(NewPerson): # 只继承NewPerson(儿子是一个人)def __init__(self, name, age, own_job, school, father: NewFather, mother: NewMother):super().__init__(name, age) # 继承父类的姓名和年龄# 组合:通过参数接收父亲和母亲的实例self.father = fatherself.mother = mother# 自身属性self.own_job = own_jobself.school = schoolself.sex = '男'# 先创建父亲和母亲的实例 father = NewFather("张三", 40, "医生") # 父亲的姓名、年龄、职业 mother = NewMother("李四", 38, "护士") # 母亲的姓名、年龄、职业(性别默认女)# 创建儿子实例(传入父母的实例) son = NewSon(name="小明",age=12,own_job="学生",school="深圳小学",father=father, # 组合父亲实例mother=mother # 组合母亲实例 )# 打印结果 print(f'姓名: {son.name}') print(f'年龄: {son.age}') print(f'学校: {son.school}') print(f'职业: {son.own_job}') print(f'父亲职业: {son.father.father_job}') # 通过组合的父亲实例获取职业 print(f'母亲职业: {son.mother.mother_job}') # 通过组合的母亲实例获取职业 print(f'性别: {son.sex}')''' 姓名: 小明 年龄: 12 学校: 深圳小学 职业: 学生 父亲职业: 医生 母亲职业: 护士 性别: 男 '''
1.3Mixin类
意义:实际开发的过程中采取Mixin类+super()继承函数组合继承。
1.Mixin类:没有定义 __init__ 方法的类,只是作为一个辅助类给主类增加某些功能,但不影响主类的继承结构。专门单一功能复用的类,一般通过多继承给其他类 “注入” 特定功能(如日志、缓存、序列化等)。命名以Mixin为后缀,比如LogMin。
2.super() 函数:按方法解析顺序(MRO) 动态调用父类方法。
Mixin 类的特点:避免多继承中的复杂性,通过不单独使用。设计时尽量把问题分解为小的、可复用的模块,避免代码复用,也可以理解为是多个类之间的一个共享的方法(工具类)。
多继承代码实例:
1.设计Mixin类(封装独立功能)
class LogMixin:"""Mixin:提供日志记录功能"""def __init__(self):# 调用下一个父类的初始化(通过super()维持链完整性)super().__init__()self.log_prefix = "[Log]" # Mixin可包含自身状态def log(self, message):"""记录日志的通用方法"""print(f"{self.log_prefix} {message}")class CacheMixin:"""Mixin:提供结果缓存功能"""def __init__(self):super().__init__() # 继续传递初始化调用self.cache = {} # 缓存字典def cache_result(self, key, result):"""缓存结果"""self.cache[key] = resultprint(f"[Cache] 已缓存 {key}: {result}")def get_cached(self, key):"""获取缓存结果"""return self.cache.get(key, None)2.设计主类(核心逻辑)
class DataProcessor:"""核心类:处理数据的主逻辑"""def __init__(self, data):super().__init__() # 调用父类(未来可能是Mixin)的初始化self.data = data # 核心数据def process(self):"""核心处理逻辑:模拟数据转换"""return [x * 2 for x in self.data]3.组合Mixin和主类(多继承)
class AdvancedDataProcessor(DataProcessor, LogMixin, CacheMixin):"""增强版处理器:组合核心逻辑 + 日志 + 缓存"""def process(self):# 1. 用LogMixin记录开始self.log(f"开始处理数据:{self.data}")# 2. 调用核心类的process获取结果(用super()确保调用父类逻辑)result = super().process()# 3. 用CacheMixin缓存结果self.cache_result("processed_data", result)# 4. 用LogMixin记录结束self.log(f"处理完成,结果:{result}")return result4.验证效果(实例化并调用)
# 创建增强版处理器实例 processor = AdvancedDataProcessor(data=[1, 2, 3]) # 调用处理方法 result = processor.process()# 查看缓存 print("缓存的结果:", processor.get_cached("processed_data"))5.执行结果
[Log] 开始处理数据:[1, 2, 3] [Log] 处理完成,结果:[2, 4, 6] [Cache] 已缓存 processed_data: [2, 4, 6] 缓存的结果: [2, 4, 6]''' 1.初始化时,super() 按 MRO 顺序调用 DataProcessor.__init__ → LogMixin.__init__ → CacheMixin.__init__ → object.__init__(所有父类初始化都被执行)。2.方法调用时,super().process() 正确找到 DataProcessor 的核心处理逻辑,Mixin 方法(log、cache_result)被无缝复用。 '''
1.4多态
核心:同一接口,不同实现,灵活调用
# 1. 定义一个基础类(父类),统一接口
class Animal:def speak(self):# 父类定义方法,但不具体实现(或抛出异常)raise NotImplementedError("子类类必须实现speakspeak()方法")# 2. 不同子类实现不同的speak()
class Dog(Animal):def speak(self):return "汪汪汪!"class Cat(Animal):def speak(self):return "喵喵喵!"class Duck(Animal):def speak(self):return "嘎嘎嘎!"# 3. 统一调用接口(多态的核心:不管关心具体类型,直接调用)
def make_animal_speak(animal):# 不管是狗、猫还是鸭子,都调用speak()print(animal.speak())# 4. 测试
if __name__ == "__main__":# 创建不同动物实例dog = Dog()cat = Cat()duck = Duck()# 统一调用,自动适配不同实现make_animal_speak(dog) # 输出:汪汪汪!make_animal_speak(cat) # 输出:喵喵喵!make_animal_speak(duck) # 输出:嘎嘎嘎!2.动态绑定
本质:指的是在程序运行过程中,可以为对象或类动态添加属性和方法,而无需在类定义时预先声明。
2.1动态绑定属性
1.为单个对象动态绑定属性:只对当前对象生效,不影响同类类的其他实例。
2.为类动态绑定属性:对类的所有实例生效(包括绑定后创建的新实例)
3. 动态删除属性:
del关键字可以删除动态绑定的属性(包括对象属性和类属性)
#具体实践案例#1.为单个对象动态绑定属性class Person:def __init__(self, name):self.name = name # 初始化时定义的属性# 创建对象
p1 = Person("张三")
p2 = Person("李四")# 为 p1 动态添加属性(只属于 p1)
p1.age = 20 # 新增 age 属性
p1.gender = "男" # 新增 gender 属性print(p1.age) # 输出:20(p1 有 age 属性)
print(p1.gender) # 输出:男# p2 没有动态添加的属性,访问会报错
# print(p2.age) # AttributeError: 'Person' object has no attribute 'age'#2.为类动态绑定属性
class Person:def __init__(self, name):self.name = name# 为 Person 类动态添加属性(类属性)
Person.species = "人类" # 所有 Person 实例都能访问# 已创建的实例
p1 = Person("张三")
print(p1.species) # 输出:人类(p1 继承类属性)# 新创建的实例
p2 = Person("李四")
print(p2.species) # 输出:人类(新实例也有该属性)#3.动态删除属性
# 删除对象属性
del p1.age # 移除 p1 的 age 属性# 删除类属性
del Person.species # 所有实例的 species 属性都会被移除2.2动态绑定方法
1.为对象动态绑定实例方法:
Ⅰ.且需要通过
types.MethodType将函数 “绑定” 到对象# 为对象 p1 动态绑定 say_hello 方法(通过 MethodType 绑定) p1.say_hello = types.MethodType(say_hello, p1)Ⅱ.只对当前对象生效
2.为类动态绑定实例方法:
Ⅰ.函数赋值给类即可
# 为 Person 类绑定实例方法 Person.say_goodbye = say_goodbyeⅡ.对类的所有实例生效
3. 为类动态绑定类方法:
Ⅰ.
# 定义一个类方法(用 @classmethod 装饰) @classmethod def get_species(cls):print(f"物种:{cls.species}")# 为类绑定类方法 Person.get_species = get_speciesⅡ.绑定到类,所有实例共享
4. 为类动态绑定静态方法
Ⅰ.
# 定义静态方法(用 @staticmethod 装饰) @staticmethod def print_rules():print("人类的基本规则:遵纪守法")# 为类绑定静态方法 Person.print_rules = print_rulesⅡ.
类和实例都能调用5.动态删除方法:
Ⅰ.
# 删除对象的方法 del p1.say_hello# 删除类的方法 del Person.say_goodbyeⅡ.
用
del删除动态绑定的方法
#具体实践案例#1.为对象动态绑定实例方法(简单理解就是通过 MethodType 将一个函数绑定到指定对象)
import typesclass Person:def __init__(self, name):self.name = name# 定义一个普通函数(准备作为实例方法)
def say_hello(self):print(f"你好,我是{self.name}")# 创建对象
p1 = Person("张三")
p2 = Person("李四")# 为 p1 动态绑定 say_hello 方法(通过 MethodType 绑定)
p1.say_hello = types.MethodType(say_hello, p1)# p1 可以调用该方法
p1.say_hello() # 输出:你好,我是张三# p2 未绑定该方法,调用会报错
# p2.say_hello() # AttributeError: 'Person' object has no attribute 'say_hello'#2.为类动态绑定实例方法(通过类.函数名=函数名实现类绑定实例方法)
class Person:def __init__(self, name):self.name = name# 定义一个普通函数(准备作为类的实例方法)
def say_goodbye(self):print(f"{self.name}说:再见!")# 为 Person 类绑定实例方法
Person.say_goodbye = say_goodbye# 所有实例都能调用该方法
p1 = Person("张三")
p1.say_goodbye() # 输出:张三说:再见!p2 = Person("李四")
p2.say_goodbye() # 输出:李四说:再见!#3.为类动态绑定类方法(类.函数名=函数名,区别是该函数上面有 @classmethod 装饰)
class Person:species = "人类"# 定义一个类方法(用 @classmethod 装饰)
@classmethod
def get_species(cls):print(f"物种:{cls.species}")# 为类绑定类方法
Person.get_species = get_species# 类和实例都能调用
Person.get_species() # 输出:物种:人类
p = Person()
p.get_species() # 输出:物种:人类#4.为类动态绑定静态方法(类.方法名=函数名,该方法名上面有@staticmethod 装饰)
class Person:pass# 定义静态方法(用 @staticmethod 装饰)
@staticmethod
def print_rules():print("人类的基本规则:遵纪守法")# 为类绑定静态方法
Person.print_rules = print_rules# 类和实例都能调用
Person.print_rules() # 输出:人类的基本规则:遵纪守法
p = Person()
p.print_rules() # 输出:人类的基本规则:遵纪守法#5.动态删除方法(del 对象.方法名/del 类.方法名)
# 删除对象的方法
del p1.say_hello# 删除类的方法
del Person.say_goodbye
del Person.get_species2.3底层原理和适用场景
1.底层原理:
Python 中,类和对象的属性 / 方法都存储在字典中,这是动态绑定的基础:
- 对象的属性存储在
对象.__dict__中(字典类型)。 - 类的属性和方法存储在
类.__dict__中。
class Person:def __init__(self, name):self.name = namep = Person("张三")
p.age = 20 # 动态绑定对象属性print(p.__dict__) # 输出:{'name': '张三', 'age': 20}(对象的属性字典)
print(Person.__dict__) # 输出类的属性和方法字典(包含 __init__ 等)2.适用场景
#1.快速拓展功能
p = Person("测试用户")
p.debug_info = "这是一个测试实例" # 临时添加调试属性
def debug(self):print(f"调试:{self.debug_info}")
p.debug = types.MethodType(debug, p)
p.debug() # 输出:调试:这是一个测试实例# 插件化开发
class PluginSystem:pass # 核心系统类# 第三方插件:为系统添加日志功能
def log(self, message):print(f"[日志] {message}")# 注册插件(动态绑定方法)
PluginSystem.log = log# 系统使用插件功能
system = PluginSystem()
system.log("系统启动成功") # 输出:[日志] 系统启动成功动态绑定原理归纳:

3.运算符重载
本质:通过在类中重写这些方法,让自定义对象支持该运算符。将“运算符操作”转化为“调用对象的特殊方法”。例a+b本质是a.__add__(b)。
原则:
1.语义合理,例'+'就是相加的功能,别自主定义作用
2.运算符的优先级与算数运算一致,无法通过运算符重载改变
3.1算数运算符
| 运算符 | 对应特殊方法 | 作用 | 注意事项 |
|---|---|---|---|
+ | __add__(self, other) | 自我 + 其他对象 | 需返回新对象,不修改原对象 |
- | __sub__(self, other) | 自我 - 其他对象 | other 是运算符右侧的对象 |
* | __mul__(self, other) | 自我 * 其他对象 | 支持多类型 other(如数字、序列) |
/ | __truediv__(self, other) | 自我 / 其他对象(浮点数) | Python 3 中区分 /(真除法)和 //(地板除) |
// | __floordiv__(self, other) | 自我 // 其他对象(整数) | - |
% | __mod__(self, other) | 自我 % 其他对象(取模) | 可用于实现格式化(如 datetime) |
算数运算符,常规的加减乘除
class Point:def __init__(self, x, y):self.x = xself.y = y# 重载 +:两个Point相加,返回新Pointdef __add__(self, other):# 先判断other是否为Point类型,避免类型错误if isinstance(other, Point):return Point(self.x + other.x, self.y + other.y)# 支持与数字相加(x/y分别加该数字)elif isinstance(other, (int, float)):return Point(self.x + other, self.y + other)else:# 不支持的类型,抛出异常(符合Python内置行为)raise TypeError(f"不支持 {type(other)} 类型与 Point 相加")# 重载 -:类似+的逻辑def __sub__(self, other):if isinstance(other, Point):return Point(self.x - other.x, self.y - other.y)elif isinstance(other, (int, float)):return Point(self.x - other, self.y - other)raise TypeError(f"不支持 {type(other)} 类型与 Point 相减")# 重载 *:支持Point * 数字(缩放坐标)def __mul__(self, other):if isinstance(other, (int, float)):return Point(self.x * other, self.y * other)raise TypeError(f"不支持 Point 与 {type(other)} 相乘")# 重载 /:支持Point / 数字(缩放坐标)def __truediv__(self, other):if isinstance(other, (int, float)):return Point(self.x / other, self.y / other)raise TypeError(f"不支持 Point 与 {type(other)} 相除")# 重载 //:支持Point // 数字整除def __floordiv__(self,other):if isinstance(other, (int, float)):return Point(self.x // other, self.y // other)raise TypeError(f"不支持 Point 与 {type(other)} 整除")# 重载字符串打印(方便调试,非算术重载但常用)def __str__(self):return f"Point({self.x}, {self.y})"# 测试
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1 + p2) # 输出 Point(4, 6)(调用__add__)
print(p1 + 5) # 输出 Point(6, 7)(支持与数字相加)
print(p2 * 2) # 输出 Point(6, 8)(调用__mul__)
print(p2 - p1) # 输出 Point(2, 2)(调用__sub__)
print(p2 / 2) #输出 Point(1.5, 2.0)(调用__truediv__)
print(p2 // 2.5)#输出Point(1.0, 1.0)(调用__floordiv__)3.2比较运算符
class Student:def __init__(self, student_id, name, score):self.student_id = student_id # 学号(唯一标识)self.name = name # 姓名self.score = score # 成绩# 1. 重载 == :学号相同则相等def __eq__(self, other):# 先判断other是否为Student类型,避免与非学生对象比较if not isinstance(other, Student):return False # 非Student类型直接不相等# 核心逻辑:比较学号return self.student_id == other.student_id# 2. 重载 != :学号不同则不等(可省略,Python会自动推导为 not __eq__)# def __ne__(self, other):# return not self.__eq__(other)# 3. 重载 < :成绩更低则更小def __lt__(self, other):if not isinstance(other, Student):# 与非Student类型比较无意义,抛出类型错误(符合Python习惯)raise TypeError("只能比较Student类型的对象")# 核心逻辑:比较成绩return self.score < other.score# 4. 重载 > :成绩更高则更大(可省略,Python会自动推导为 other < self)# def __gt__(self, other):# return other.__lt__(self)# 5. 重载 <= :成绩更低或相等(可省略,Python会自动推导为 not __gt__)# def __le__(self, other):# return not self.__gt__(other)# 6. 重载 >= :成绩更高或相等(可省略,Python会自动推导为 not __lt__)# def __ge__(self, other):# return not self.__lt__(other)# 自定义打印格式(方便调试)def __str__(self):return f"Student(id={self.student_id}, name='{self.name}', score={self.score})"# 测试案例
if __name__ == "__main__":# 创建4名学生s1 = Student(101, "张三", 85)s2 = Student(102, "李四", 92)s3 = Student(101, "张三(重名)", 85) # 学号与s1相同(模拟重复数据)s4 = Student(103, "王五", 85) # 成绩与s1相同# 测试 == 和 !=print(f"s1 == s2: {s1 == s2}") # False(学号不同)print(f"s1 == s3: {s1 == s3}") # True(学号相同)print(f"s1 != s2: {s1 != s2}") # True(自动推导)# 测试 < 和 >print(f"s1 < s2: {s1 < s2}") # True(85 < 92)print(f"s2 > s1: {s2 > s1}") # True(自动推导)print(f"s1 < s4: {s1 < s4}") # False(85 不小于 85)# 测试 <= 和 >=print(f"s1 <= s4: {s1 <= s4}") # True(85 <= 85)print(f"s2 >= s1: {s2 >= s1}") # True(92 >= 85)print(f"s4 >= s2: {s4 >= s2}") # False(85 < 92)# 测试与非Student对象比较(应报错)try:print(s1 < 100) # 尝试与整数比较except TypeError as e:print(f"错误提示: {e}") # 输出:只能比较Student类型的对象'''
s1 == s2: False
s1 == s3: True
s1 != s2: True
s1 < s2: True
s2 > s1: True
s1 < s4: False
s1 <= s4: True
s2 >= s1: True
s4 >= s2: False
错误提示: 只能比较Student类型的对象
'''4.魔法函数
本质:它们的核心作用是让自定义对象能够像内置对象(如列表、字典)一样支持 Python 原生语法(如 +、len()、for 循环等),是实现 Python 灵活性和一致性的关键。
特点:魔法函数自动触发,无需调用,且模拟内置行为无需继承,魔法函数的核心是 “模拟内置行为”,而非炫技。
4.1初始化和销毁
| 魔法函数 | 触发时机 | 作用 |
|---|---|---|
__new__(cls) | 对象创建时(在 __init__ 之前) | 负责创建实例(构造函数),返回实例对象 |
__init__(self) | 对象初始化时 | 初始化实例属性(初始化函数) |
__del__(self) | 对象被垃圾回收时 | 释放资源(如关闭文件、连接) |
单例模式:(通过__new__确保类只有一个实例)
class Singleton:_instance = None # 存储唯一实例def __new__(cls, *args, **kwargs):if not cls._instance:# 首次创建时,调用父类的 __new__ 生成实例cls._instance = super().__new__(cls)return cls._instance # 后续调用直接返回已有实例# 测试:两个实例实际是同一个对象 s1 = Singleton() s2 = Singleton() print(s1 is s2) # 输出:True
4.2字符串的表示
| 魔法函数 | 触发时机 | 作用 |
|---|---|---|
__str__(self) | str(obj) 或 print(obj) 时 | 返回用户友好的字符串(可读性优先) |
__repr__(self) | repr(obj) 或交互式解释器中 | 返回开发者友好的字符串(用于调试) |
__format__(self, fmt) | format(obj, fmt) 时 | 自定义格式化字符串(如时间格式化) |
自定义数据类的字符串输出:
class User:def __init__(self, name, age):self.name = nameself.age = agedef __str__(self):# 用户看到的友好信息return f"User: {self.name} (age: {self.age})"def __repr__(self):# 开发者调试用(最好能通过 eval 重建对象)return f"User(name='{self.name}', age={self.age})"u = User("Alice", 20) print(u) # 触发 __str__:输出 User: Alice (age: 20) print(repr(u)) # 触发 __repr__:输出 User(name='Alice', age=20)
4.3运算符重载
| 魔法函数 | 触发时机 | 作用示例 |
|---|---|---|
__add__(self, other) | self + other | 定义加法运算 |
__sub__(self, other) | self - other | 定义减法运算 |
__mul__(self, other) | self * other | 定义乘法运算 |
__truediv__(self, other) | self / other | 定义除法运算 |
__lt__(self, other) | self < other | 定义小于比较 |
__eq__(self, other) | self == other | 定义等于比较 |
__contains__(self, item) | item in self | 定义 in 运算符支持 |
关于运算符的重载上文已做了详细解释,此处就不冗余了
#基础向量加法的运算class Vector:def __init__(self, x, y):self.x = xself.y = y# 支持向量加法:v1 + v2def __add__(self, other):if isinstance(other, Vector):return Vector(self.x + other.x, self.y + other.y)raise TypeError("只能与 Vector 类型相加")# 支持打印向量def __str__(self):return f"Vector({self.x}, {self.y})"v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2 # 触发 __add__
print(v3) # 输出:Vector(4, 6)#比较运算符的运算
class User:def __init__(self, id):self.id = id# 两个用户 ID 相同则认为相等def __eq__(self, other):if isinstance(other, User):return self.id == other.idreturn Falseu1 = User(100)
u2 = User(100)
print(u1 == u2) # 输出:True(默认会返回 False)4.4容器相关自定义数据结构
| 魔法函数 | 触发时机 | 作用 |
|---|---|---|
__len__(self) | len(obj) 时 | 返回容器长度 |
__getitem__(self, key) | obj[key] 或切片 obj[start:end] 时 | 获取索引 / 键对应的值 |
__setitem__(self, key, value) | obj[key] = value 时 | 设置索引 / 键对应的值 |
__delitem__(self, key) | del obj[key] 时 | 删除索引 / 键对应的值 |
__iter__(self) | for item in obj 时 | 返回迭代器(用于遍历) |
实战场景:实现自定义列表(支持索引、切片、迭代)
class MyList:def __init__(self, data):self.data = list(data)# 支持 len()def __len__(self):return len(self.data)# 支持索引和切片(如 obj[0], obj[1:3])def __getitem__(self, key):return self.data[key]# 支持修改索引值(如 obj[0] = 10)def __setitem__(self, key, value):self.data[key] = value# 支持迭代(for 循环)def __iter__(self):# 返回内置列表的迭代器(也可自定义)return iter(self.data)# 测试 ml = MyList([1, 2, 3, 4]) print(len(ml)) # 触发 __len__:输出 4 print(ml[1]) # 触发 __getitem__:输出 2 ml[2] = 100 # 触发 __setitem__ print(ml[1:3]) # 触发 __getitem__ 切片:输出 [2, 100] for item in ml: # 触发 __iter__print(item, end=" ") # 输出:1 2 100 4
4.5调用与属性访问
| 魔法函数 | 触发时机 | 作用 |
|---|---|---|
__call__(self, *args) | obj(*args) 时(把对象当函数调用) | 让对象可调用(类似函数) |
__getattr__(self, name) | 访问不存在的属性时(obj.name) | 自定义属性不存在时的处理逻辑 |
__setattr__(self, name, value) | 设置属性时(obj.name = value) | 自定义属性赋值逻辑(如校验) |
__delattr__(self, name) | 删除属性时(del obj.name) | 自定义属性删除逻辑 |
重点理解__call__魔法函数的使用
#1.可调用对象(模拟函数)
class Counter:def __init__(self):self.count = 0# 让对象可调用:每次调用计数+1def __call__(self):self.count += 1return self.countcounter = Counter()
print(counter()) # 触发 __call__:输出 1
print(counter()) # 输出 2#2.属性访问控制(防止误设不存在的属性)
class StrictUser:# 只允许设置 name 和 age 属性allowed_attrs = {"name", "age"}def __setattr__(self, name, value):if name not in self.allowed_attrs:raise AttributeError(f"不允许设置属性:{name}")# 注意:不能直接用 self.name = value(会递归调用 __setattr__)super().__setattr__(name, value) # 调用父类的方法设置user = StrictUser()
user.name = "Alice" # 正常设置
user.age = 20 # 正常设置
user.email = "a@x.com" # 报错:AttributeError: 不允许设置属性:email4.6上下文管理
| 魔法函数 | 触发时机 | 作用 |
|---|---|---|
__enter__(self) | 进入 with 代码块时 | 获取资源(如打开文件),返回操作对象 |
__exit__(self, exc_type, exc_val, exc_tb) | 离开 with 代码块时 | 释放资源(如关闭文件),处理异常 |
让该类具有文件功能
#自定义文件管理器 class FileManager:def __init__(self, filename, mode):self.filename = filenameself.mode = modeself.file = Nonedef __enter__(self):self.file = open(self.filename, self.mode)return self.file # 作为 with 语句的 as 变量def __exit__(self, exc_type, exc_val, exc_tb):self.file.close() # 无论是否出错,都会关闭文件# 如果返回 True,会抑制异常(不推荐,除非明确处理)return False# 使用 with 语句自动管理文件 with FileManager("test.txt", "w") as f:f.write("Hello, 魔法函数!") # 离开 with 块后,文件自动关闭
