前言:
笔者本小结主要阐述python使用中类,并整理而成的学习笔记,用于更好的掌握python的类以及其三大特征。
1.类
概念:类是 Python 面向对象编程(OOP)的基石,具有"封装","继承","多态"三大特性,我的理解本质:就是世间万物所具有的:特性(属性),行为(方法)。抽象出来形成"模板"称之为"类"(class)
class Bank:
bank_name = '中国银行'
address = '深圳'
rate = 1.2
customer_count = 0 # 实例对象计数
def __init__(self, account, balance):
self.account = account
if not isinstance(balance, (int, float)):
raise ValueError("余额必须是数值类型")
self.balance = balance
Bank.customer_count += 1 # 实例计数+1
def deposit(self, amount):
if not Bank.is_valid_amount(amount):
print("存款失败:金额必须是100的整数倍且为正数")
return
self.balance += amount
print(f"存款成功,当前余额:{self.balance}")
def withdraw(self, amount):
if not Bank.is_valid_amount(amount):
print("取款失败:金额必须是100的整数倍且为正数")
return
if amount > self.balance:
print("取款失败:余额不足")
return
self.balance -= amount
print(f"取款成功,当前余额:{self.balance}")
def inquire(self):
print(f'账号:{self.account}, 金额:{self.balance}')
return self.account, self.balance
@classmethod
def change_rate(cls, new_rate):
old_rate = cls.rate
cls.rate = new_rate
print(f'修改前利息:{old_rate}, 修改后利息:{cls.rate}')
@staticmethod
def is_valid_amount(amount):
if not isinstance(amount, int) or amount <= 0:
return False
return 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_name self._age = age def __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 = name self.age = age super().__init__(** kwargs) # 将剩余参数传递给下一级父类 class NewFather(NewPerson): def __init__(self, name, age, father_job, **kwargs): super().__init__(name, age,** kwargs) # 传递参数给父类NewPerson self.father_job = father_job class NewMother(NewPerson): def __init__(self, name, age, mother_job, sex='女', **kwargs): super().__init__(name, age,** kwargs) # 传递参数给父类NewPerson self.mother_job = mother_job self.sex = sex class 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 = school self.own_job = own_job self.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 = name
self.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_job
self.sex = sexclass NewSon(NewPerson): # 只继承NewPerson(儿子是一个人)
def init(self, name, age, own_job, school, father: NewFather, mother: NewMother):
super().init(name, age) # 继承父类的姓名和年龄
# 组合:通过参数接收父亲和母亲的实例
self.father = father
self.mother = mother
# 自身属性
self.own_job = own_job
self.school = school
self.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] = result print(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 types
class 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_species
2.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 = x
self.y = y
# 重载 +:两个Point相加,返回新Point
def __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 = name self.age = age def __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 = x
self.y = y
# 支持向量加法:v1 + v2
def __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.id
return False
u1 = 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
# 让对象可调用:每次调用计数+1
def __call__(self):
self.count += 1
return self.count
counter = 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: 不允许设置属性:email
4.6上下文管理
| 魔法函数 | 触发时机 | 作用 |
|---|---|---|
__enter__(self) |
进入 with 代码块时 |
获取资源(如打开文件),返回操作对象 |
__exit__(self, exc_type, exc_val, exc_tb) |
离开 with 代码块时 |
释放资源(如关闭文件),处理异常 |
让该类具有文件功能
#自定义文件管理器 class FileManager: def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = None def __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 块后,文件自动关闭