当前位置: 首页 > news >正文

人工智能学习中深度学习之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 result

4.验证效果(实例化并调用)

# 创建增强版处理器实例
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_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 = 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: 不允许设置属性:email

4.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 块后,文件自动关闭

http://www.dtcms.com/a/573625.html

相关文章:

  • K8s/Kubernetes(v1.23.17)三节点集群部署全过程的总结与问题回顾
  • 调试oracle函数性能(嵌入存储过程)
  • React 元素渲染
  • 个人开发者短信验证码接入指南-阿里云
  • 移动端优秀网站上海传媒公司名字
  • 建设一个网站的需求分析一个网站开发流程
  • PsPasswd(7.19):远程修改密码的边界与合规建议
  • 【钉钉多元表格(自动化)】钉钉群根据表格 自动推送当天值日生信息
  • LangFlow源码深度解析:Component核心机制与生态体系
  • dede织梦仿站网站建设做网站赚谁的钱
  • DropLoRA技术详解:克服大模型微调过拟合的创新方法
  • 【剑斩OFFER】算法的暴力美学——串联所有单词的字串
  • 学习Linux——进程管理
  • 在k8s中部署seaweedfs,上传文件到seaweedfs方法
  • 极氪与火山引擎深化合作,Data Agent赋能车辆数据管理效率
  • Kotlin 使用命令行编译
  • 1450dpi+93% 相似度,这款发光纳米纤维让皮肤纹理“复印”更精准
  • 匠魂(1)
  • LeetCode Hot100 自用
  • 做婚介网站可行性报告模板绵阳网站建设多少钱
  • 单位服务器网站打不开网站参考页面设计
  • 陇南建设网站大良营销网站建设信息
  • mac M系列芯片 unity 安装会遇到的错误以及解决
  • Reka UI - 一款免费开源的 Vue 无头 UI 组件库,样式定制开发项目的绝佳选择
  • 个人二级网站怎么做营销咨询服务合同
  • UDP-复用分用
  • 做网站需要什么特色网站制作入门
  • QListWidget的图标模式
  • 【大模型实战笔记 6】Prompt Engineering 提示词工程
  • 能源生态系统的架构设计:利益相关方治理与跨行业协作