|---|------------------------|
| | 知识点回顾 |
| | 1.类的装饰器 |
| | 2. 装饰器思想的进一步理解:外部修改、动态 |
| | 3. 类方法的定义:内部定义和外部定义 |
零基础 Python 学习:类的装饰器、装饰器思想、类方法定义
作为你的老师,我会从前置知识铺垫开始 ,把每个知识点拆成小模块,用 "大白话 + 分步代码" 的方式讲解,确保你能听懂。首先明确:学习这些知识点需要先掌握函数基础、类的基础、简单的函数装饰器,我会先快速回顾这些前置内容,再进入核心知识点。
前置知识快速回顾(零基础必看)
在学新内容前,先记住 3 个基础概念,用最简单的例子说明:
1. 函数的基本定义与调用
函数是 "实现特定功能的代码块",可以重复使用。
python
# 定义函数:打印问候语
def say_hello():
print("你好,我是零基础学习者!")
# 调用函数
say_hello() # 输出:你好,我是零基础学习者!
2. 类的基本定义与实例化
类是 "对象的模板",比如 "人" 这个类,可以定义人的属性(名字、年龄)和行为(说话、走路)。
python
# 定义类
class Person:
# 初始化方法:创建实例时给属性赋值
def __init__(self, name):
self.name = name # 实例属性:每个实例的名字不同
# 实例方法:类的行为(第一个参数必须是self,代表实例本身)
def say_name(self):
print(f"我的名字是{self.name}")
# 实例化:用类创建具体的对象(实例)
p = Person("小明") # 创建一个叫"小明"的人
p.say_name() # 调用实例方法,输出:我的名字是小明
3. 函数装饰器的简单理解(核心铺垫)
装饰器可以理解为 "给函数 / 类穿的'外套'":不修改原函数 / 类的代码,却能给它增加新功能。比如给函数加一个 "计时功能",看函数运行了多久:
python
# 定义装饰器函数:计算函数运行时间
def timer_decorator(func): # func是被装饰的函数
# 包装函数:实现新增功能
def wrapper():
import time
start = time.time() # 记录开始时间
func() # 执行原函数
end = time.time() # 记录结束时间
print(f"函数运行了{end - start:.6f}秒") # 新增功能:打印运行时间
return wrapper # 返回包装函数
# 使用装饰器:@+装饰器名,放在函数定义上方
@timer_decorator
def say_hello():
print("你好呀!")
# 调用函数(看似调用原函数,实际调用的是wrapper)
say_hello()
"""
输出:
你好呀!
函数运行了0.000005秒
"""
关键理解 :装饰器的本质是 "函数嵌套 + 闭包 + 语法糖(@)",核心是不修改原代码,扩展功能。
知识点 1:类的装饰器
类的装饰器分为两种情况:
- 情况 1:装饰器函数装饰类(装饰器是函数,参数是类,返回修改后的类)
- 情况 2:类作为装饰器 (装饰器本身是类,通过
__call__方法实现装饰功能)
子点 1:装饰器函数装饰类
这种用法是用函数给类添加属性或方法,就像给 "类模板" 加新的特征,所有实例都能用到。
分步讲解 + 代码
步骤 1:定义装饰器函数(参数是类,返回修改后的类)
python
def add_attr_decorator(cls): # cls是被装饰的类(比如后面的Person类)
# 给类添加一个**类属性**(所有实例共享)
cls.version = "1.0" # 给类加版本号属性
# 给类添加一个**实例方法**
def show_version(self): # self是实例
print(f"当前类的版本号:{self.version}")
cls.show_version = show_version # 把方法绑定到类上
return cls # 返回修改后的类
步骤 2:用装饰器装饰类(@+ 装饰器名,放在类定义上方)
python
@add_attr_decorator # 给Person类穿"外套",添加属性和方法
class Person:
def __init__(self, name):
self.name = name # 原有实例属性
步骤 3:创建实例,测试新增的属性和方法
python
p1 = Person("小明")
p2 = Person("小红")
# 访问新增的类属性(所有实例共享)
print(p1.version) # 输出:1.0
print(p2.version) # 输出:1.0
print(Person.version) # 输出:1.0(类也能直接访问)
# 调用新增的实例方法
p1.show_version() # 输出:当前类的版本号:1.0
p2.show_version() # 输出:当前类的版本号:1.0
通俗理解:就像给 "人" 这个类统一加了 "身份证版本号",所有的人都有这个属性,还能调用方法展示版本号,而且没改 Person 类的原有代码。
子点 2:类作为装饰器
装饰器不仅可以是函数,也可以是类 。要让类能当装饰器,必须实现__call__方法(让类的实例可以像函数一样被调用)。
分步讲解 + 代码
步骤 1 :定义装饰器类(通过__init__接收被装饰的函数,__call__实现装饰功能)
python
class TimerDecorator:
def __init__(self, func): # 初始化时接收被装饰的函数
self.func = func # 保存被装饰的函数(比如后面的say_hi)
def __call__(self): # 调用实例时执行的方法(核心)
import time
start = time.time()
self.func() # 执行原函数
end = time.time()
print(f"函数运行了{end - start:.6f}秒")
步骤 2:用类装饰器装饰函数
python
@TimerDecorator # 等价于:say_hi = TimerDecorator(say_hi)
def say_hi():
print("Hi!我是类装饰器测试")
步骤 3:调用函数(实际是调用类的实例)
python
say_hi()
"""
输出:
Hi!我是类装饰器测试
函数运行了0.000004秒
"""
关键理解:
@TimerDecorator执行后,say_hi不再是原函数,而是TimerDecorator类的一个实例。- 调用
say_hi()时,实际调用的是实例的__call__方法,从而实现装饰功能。
知识点 2:装饰器思想的进一步理解:外部修改、动态
装饰器的核心思想是 **"开放封闭原则"**:对扩展功能开放,对修改原代码封闭。我们从 "外部修改" 和 "动态" 两个角度拆解。
子点 1:外部修改 ------ 不碰原代码,在外部加功能
通俗解释:就像你有一个手机,不想拆开机身(修改原代码),但想加保护功能,就给手机套上手机壳(装饰器),手机本身没改,却多了保护功能。
例子验证
python
# 原函数:只有打印功能(绝不修改这个函数)
def original_func():
print("我是原函数,代码从未被修改!")
# 装饰器1:添加"日志功能"(外部扩展)
def log_decorator(func):
def wrapper():
print("【日志】函数开始执行") # 新增功能
func()
print("【日志】函数执行结束") # 新增功能
return wrapper
# 装饰器2:添加"计时功能"(外部扩展)
def timer_decorator(func):
def wrapper():
import time
start = time.time()
func()
end = time.time()
print(f"【计时】函数运行了{end - start:.6f}秒")
return wrapper
# 用装饰器扩展原函数(不碰原函数代码)
original_func = log_decorator(original_func)
original_func()
"""
输出:
【日志】函数开始执行
我是原函数,代码从未被修改!
【日志】函数执行结束
"""
结论 :原函数的代码一行没改,却通过外部的装饰器增加了新功能,这就是外部修改的核心。
子点 2:动态 ------ 随时加 / 减 / 换装饰器
通俗解释:手机壳可以随时换(换装饰器)、随时拆(移除装饰器)、随时加多个(多个装饰器叠加),非常灵活。
例子验证
python
# 原函数
def say_hi():
print("Hi!")
# 定义两个装饰器
def log_decorator(func):
def wrapper():
print("【日志】开始执行")
func()
print("【日志】执行结束")
return wrapper
def timer_decorator(func):
def wrapper():
import time
start = time.time()
func()
end = time.time()
print(f"【计时】运行了{end - start:.6f}秒")
return wrapper
# 动态操作1:添加计时装饰器
say_hi = timer_decorator(say_hi)
say_hi()
"""
输出:
Hi!
【计时】运行了0.000003秒
"""
# 动态操作2:换成日志装饰器(先拿到原函数,再装饰)
say_hi = log_decorator(say_hi.__wrapped__) # __wrapped__是Python内置属性,获取原函数
say_hi()
"""
输出:
【日志】开始执行
Hi!
【日志】执行结束
"""
# 动态操作3:移除所有装饰器,恢复原函数
say_hi = say_hi.__wrapped__
say_hi() # 输出:Hi!
# 动态操作4:叠加多个装饰器
say_hi = log_decorator(timer_decorator(say_hi))
say_hi()
"""
输出:
【日志】开始执行
Hi!
【计时】运行了0.000002秒
【日志】执行结束
"""
结论 :装饰器可以动态添加、更换、移除、叠加 ,不用修改原函数代码,这就是动态的核心。
知识点 3:类方法的定义:内部定义和外部定义
在 Python 中,类的方法主要有 3 种:实例方法、类方法、静态方法 。我们重点讲类方法的定义(内部 + 外部),先明确类方法的基本特征:
- 类方法用
@classmethod装饰,第一个参数是cls(代表类本身,不是实例)。 - 类方法可以通过类直接调用 ,也可以通过实例调用 ,主要用来操作类属性(所有实例共享的属性)。
子点 1:类内部定义类方法(最常用)
分步讲解 + 代码
步骤 1:定义类,在内部定义类方法、实例方法、静态方法(对比学习)
python
class Student:
# 类属性:所有学生共享的学校名称
school = "北京大学"
# 初始化方法:实例属性(每个学生的名字不同)
def __init__(self, name):
self.name = name
# 1. 实例方法:操作实例属性(第一个参数self)
def show_name(self):
print(f"我的名字是{self.name}")
# 2. 类方法:操作类属性(@classmethod装饰,第一个参数cls)
@classmethod
def change_school(cls, new_school):
cls.school = new_school # 修改类属性
print(f"学校已改为:{cls.school}")
# 3. 静态方法:和类、实例无关(@staticmethod装饰,无默认参数)
@staticmethod
def show_rule():
print("学生必须遵守校规!")
步骤 2:调用各类方法(重点看类方法的调用方式)
python
# 创建实例
s1 = Student("张三")
s2 = Student("李四")
# 调用实例方法(只能通过实例调用)
s1.show_name() # 输出:我的名字是张三
# 调用类方法(两种方式:类调用/实例调用,推荐类调用)
Student.change_school("清华大学") # 输出:学校已改为:清华大学
s1.change_school("复旦大学") # 输出:学校已改为:复旦大学
# 验证类属性的修改(所有实例共享)
print(Student.school) # 输出:复旦大学
print(s1.school) # 输出:复旦大学
print(s2.school) # 输出:复旦大学
# 调用静态方法(类/实例都能调用)
Student.show_rule() # 输出:学生必须遵守校规!
s1.show_rule() # 输出:学生必须遵守校规!
关键总结:
- 类方法的核心是
@classmethod和cls参数,用来操作类属性。 - 内部定义是最直接的方式,也是日常开发中最常用的。
子点 2:类外部定义类方法(动态扩展类)
有时候我们需要给已经定义好的类添加方法(比如第三方库的类,不能修改源码),这时候就可以在外部定义并绑定到类上。
分 3 种情况讲解(实例方法、类方法、静态方法)
步骤 1:先定义一个基础类(无任何方法,除了初始化)
python
class Teacher:
# 类属性
subject = "数学"
# 初始化方法
def __init__(self, name):
self.name = name
步骤 2 :外部定义实例方法,并绑定到类
python
# 外部定义实例方法(第一个参数必须是self)
def show_name(self):
print(f"老师的名字是{self.name}")
# 绑定到Teacher类
Teacher.show_name = show_name
# 测试
t1 = Teacher("王老师")
t1.show_name() # 输出:老师的名字是王老师
步骤 3 :外部定义类方法,并绑定到类
python
# 外部定义类方法(必须用@classmethod装饰,第一个参数cls)
@classmethod
def change_subject(cls, new_subject):
cls.subject = new_subject
print(f"授课科目已改为:{cls.subject}")
# 绑定到Teacher类
Teacher.change_subject = change_subject
# 测试(类调用/实例调用都可以)
Teacher.change_subject("语文") # 输出:授课科目已改为:语文
print(Teacher.subject) # 输出:语文
步骤 4 :外部定义静态方法,并绑定到类
python
# 外部定义静态方法(必须用@staticmethod装饰,无默认参数)
@staticmethod
def show_tip():
print("老师要认真备课!")
# 绑定到Teacher类
Teacher.show_tip = show_tip
# 测试
Teacher.show_tip() # 输出:老师要认真备课!
t1.show_tip() # 输出:老师要认真备课!
通俗理解:就像给一个已经做好的玩具(类),后期贴上新的贴纸(方法),玩具本身没重做,却多了新功能。
总结:核心知识点梳理
为了方便你记忆,我把所有核心点整理成表格:
| 知识点 | 核心内容 | 通俗比喻 |
|---|---|---|
| 类的装饰器(函数装饰类) | 用函数给类添加属性 / 方法,不修改类的源码 | 给类模板加新特征 |
| 类的装饰器(类作为装饰器) | 用类实现装饰器,通过__call__方法执行装饰功能 |
用 "类外壳" 包装函数 |
| 装饰器思想 - 外部修改 | 不修改原代码,在外部通过装饰器扩展功能 | 手机套手机壳,不拆机加保护 |
| 装饰器思想 - 动态 | 随时添加、更换、移除装饰器 | 随时换手机壳 |
| 类方法(内部定义) | 用@classmethod装饰,第一个参数cls,操作类属性 |
类的 "专属工具" |
| 类方法(外部定义) | 给已有的类动态添加方法(实例 / 类 / 静态方法) | 给玩具后期贴贴纸 |

课后小练习(巩固所学)
- 用装饰器函数装饰类 ,给
Dog类添加color属性和show_color方法。 - 用类作为装饰器,给函数添加 "计数功能"(统计函数被调用的次数)。
- 给已有的
Cat类,在外部定义一个类方法 ,修改类属性breed(品种)。
通过这样的拆解和举例,相信你已经能理解这些知识点了。学习 Python 的关键是多敲代码,把每个例子自己手敲一遍,运行看结果,就能慢慢掌握啦!
练习 1:用装饰器函数装饰类,给 Dog 类添加 color 属性和 show_color 方法
练习目标
通过装饰器函数 给Dog类动态添加color类属性(所有狗共享)和show_color实例方法(展示颜色),不修改Dog类的原有代码。
步骤流程
- 定义装饰器函数 :接收被装饰的类,给类添加
color属性和show_color方法; - 用
@装饰器名装饰Dog类; - 创建
Dog类的实例,验证新增的属性和方法。
代码实现(写入practice.py)
python
# --------------- 练习1:装饰器函数装饰类 ---------------
# 步骤1:定义装饰器函数(给类添加属性和方法)
def add_dog_attrs(cls):
# 给类添加类属性color(所有Dog实例共享)
cls.color = "黄色" # 统一设置默认颜色为黄色
# 定义show_color实例方法(展示颜色)
def show_color(self):
print(f"我是{self.name},我的毛色是:{self.color}")
# 把方法绑定到类上
cls.show_color = show_color
# 返回修改后的类
return cls
# 步骤2:用装饰器装饰Dog类
@add_dog_attrs
class Dog:
# Dog类原有代码(只定义名字属性,无color和show_color)
def __init__(self, name):
self.name = name # 实例属性:狗的名字
# 步骤3:创建实例,验证效果
if __name__ == "__main__":
# 测试练习1
print("===== 练习1测试 =====")
wangcai = Dog("旺财")
huanghuang = Dog("黄黄")
# 访问新增的类属性color
print(f"Dog类的默认颜色:{Dog.color}") # 输出:黄色
print(f"旺财的颜色:{wangcai.color}") # 输出:黄色
# 调用新增的show_color方法
wangcai.show_color() # 输出:我是旺财,我的毛色是:黄色
huanghuang.show_color() # 输出:我是黄黄,我的毛色是:黄色
# 也可以修改类属性,所有实例都会同步
Dog.color = "黑色"
wangcai.show_color() # 输出:我是旺财,我的毛色是:黑色
关键说明
add_dog_attrs是装饰器函数,参数cls代表被装饰的Dog类;cls.color = "黄色"是给类加类属性,所有实例共享;show_color是实例方法,通过self访问属性。
练习 2:用类作为装饰器,给函数添加 "计数功能"(统计调用次数)
练习目标
通过类装饰器给函数动态添加计数功能,每次调用函数时,自动统计调用次数并展示。
步骤流程
- 定义类装饰器 :通过
__init__接收被装饰的函数,用实例属性保存调用次数; - 实现
__call__方法:在方法内完成 "计数 + 执行原函数" 的逻辑; - 用
@类装饰器名装饰目标函数; - 多次调用函数,验证计数功能。
代码实现(接在练习 1 代码后)
python
# --------------- 练习2:类作为装饰器实现计数功能 ---------------
# 步骤1:定义类装饰器
class CountDecorator:
def __init__(self, func):
# 保存被装饰的函数
self.func = func
# 初始化调用次数为0(实例属性,记录次数)
self.count = 0
def __call__(self, *args, **kwargs):
# 每次调用函数时,计数+1
self.count += 1
# 执行原函数
result = self.func(*args, **kwargs)
# 打印调用次数
print(f"函数{self.func.__name__}已被调用{self.count}次")
# 返回原函数的执行结果
return result
# 步骤2:用类装饰器装饰函数
@CountDecorator
def say_hi(name):
"""测试函数:打印问候语"""
print(f"你好,我是{name}!")
@CountDecorator
def add(a, b):
"""测试函数:计算两数之和"""
return a + b
# 步骤3:多次调用函数,验证计数
if __name__ == "__main__":
# 测试练习2
print("\n===== 练习2测试 =====")
say_hi("旺财") # 第1次调用
say_hi("小白") # 第2次调用
add(1, 2) # 第1次调用
add(3, 4) # 第2次调用
add(5, 6) # 第3次调用
关键说明
- 类装饰器必须实现
__call__方法,这样类的实例才能像函数一样被调用; self.count是实例属性,用来保存函数的调用次数;*args, **kwargs是为了让装饰器适配任意参数的函数(比如say_hi有一个参数,add有两个参数)。
练习 3:给已有的 Cat 类,在外部定义类方法修改类属性 breed
练习目标
给预先定义的Cat类(无法修改源码的情况),在外部 定义类方法,实现修改类属性breed(品种)的功能。
步骤流程
- 定义基础的
Cat类(包含类属性breed); - 在外部定义类方法 (用
@classmethod装饰,参数为cls); - 将类方法绑定到
Cat类上; - 调用类方法修改类属性,验证效果。
代码实现(接在练习 2 代码后)
python
# --------------- 练习3:外部定义类方法修改类属性 ---------------
# 步骤1:定义已有的Cat类(假设无法修改此类的源码)
class Cat:
# 类属性:默认品种为中华田园猫
breed = "中华田园猫"
def __init__(self, name):
self.name = name # 实例属性:猫的名字
# 步骤2:在外部定义类方法(修改类属性breed)
@classmethod
def change_breed(cls, new_breed):
# 修改类属性breed
cls.breed = new_breed
print(f"猫的品种已修改为:{cls.breed}")
# 步骤3:将类方法绑定到Cat类上
Cat.change_breed = change_breed
# 步骤4:调用类方法,验证效果
if __name__ == "__main__":
# 测试练习3
print("\n===== 练习3测试 =====")
# 查看初始品种
print(f"初始品种:{Cat.breed}") # 输出:中华田园猫
# 调用外部定义的类方法修改品种
Cat.change_breed("英短")
# 查看修改后的品种(所有实例共享)
mimi = Cat("咪咪")
huahua = Cat("花花")
print(f"咪咪的品种:{mimi.breed}") # 输出:英短
print(f"花花的品种:{huahua.breed}") # 输出:英短
关键说明
- 类方法的第一个参数必须是
cls(代表类本身),用来操作类属性; - 用
Cat.change_breed = change_breed将外部方法绑定到类上,实现动态扩展; - 类属性修改后,所有实例的该属性都会同步更新。

类的装饰器的执行顺序是怎样的?
作为零基础的学习者,你肯定会对装饰器的执行顺序感到困惑 ------ 尤其是多个装饰器叠加时,到底先执行哪个?
我会先把执行过程拆成两个核心阶段 (装饰阶段 + 调用阶段),再用大白话 + 带打印的代码例子(每一步都标清执行顺序),分情况讲解:单个装饰器、多个装饰器叠加、类作为装饰器的执行顺序,最后总结成易记的规律,确保你一看就懂。
先明确两个核心阶段(关键!避免混淆)
类的装饰器的执行过程分为两个完全不同的阶段,这是理解执行顺序的根本:
- 装饰阶段 :Python 解释器加载代码时 (比如运行脚本、导入模块),遇到
@装饰器名就会立即执行装饰器函数,完成对类的 "包装"。
通俗说:这是 "给类穿外套" 的阶段 ,代码一加载就执行,不用等创建实例。
- 调用阶段 :当你创建类的实例 (比如
a = A())、调用类的方法时,才会执行装饰器里的包装函数(wrapper)或类装饰器的__call__方法。
通俗说:这是"穿好外套后,使用类" 的阶段。
情况 1:单个函数装饰器装饰类(最基础)
先从最简单的单个装饰器入手,通过打印语句直观看到执行顺序。
代码例子(加大量打印,标清步骤)
python
# 定义装饰器函数
def add_attr_decorator(cls):
print("【装饰阶段】1. 执行装饰器函数 add_attr_decorator,接收的类:", cls.__name__)
# 给类添加属性
cls.version = "v1.0"
# 定义包装函数(只有调用阶段才会执行)
def wrapper(*args, **kwargs):
print("【调用阶段】1. 执行装饰器的 wrapper 函数")
# 创建原类的实例
instance = cls(*args, **kwargs)
print("【调用阶段】3. 原类实例创建完成,返回实例")
return instance
print("【装饰阶段】2. 装饰器函数执行完毕,返回 wrapper 函数")
return wrapper
# 用装饰器装饰类(触发装饰阶段)
@add_attr_decorator
class A:
def __init__(self):
print("【调用阶段】2. 执行类 A 的 __init__ 方法")
# --------------- 分割线:以上是装饰阶段,以下是调用阶段 ---------------
print("------------------开始创建实例------------------")
a = A() # 创建实例,触发调用阶段

核心总结(单个装饰器)
| 阶段 | 执行顺序 |
|---|---|
| 装饰阶段 | 解释器加载代码时,先执行@add_attr_decorator → 调用add_attr_decorator(A) → 返回wrapper函数 → 把A重命名为wrapper |
| 调用阶段 | 创建实例a = A() → 实际调用wrapper() → 执行wrapper内的代码 → 调用原类A()创建实例 → 执行A.__init__ → 返回实例 |
情况 2:多个函数装饰器叠加装饰类(重点!易混淆)
多个装饰器叠加时,装饰阶段和调用阶段的执行顺序完全相反,这是初学者最容易踩坑的点。
代码例子(两个装饰器叠加)
python
# 装饰器1:外层装饰器(离类远)
def decorator1(cls):
print("【装饰阶段】1. 执行 decorator1,接收的类:", cls.__name__)
def wrapper(*args, **kwargs):
print("【调用阶段】1. 执行 decorator1 的 wrapper 函数")
instance = cls(*args, **kwargs)
print("【调用阶段】4. 从 decorator1 的 wrapper 返回实例")
return instance
print("【装饰阶段】2. decorator1 执行完毕,返回 wrapper")
return wrapper
# 装饰器2:内层装饰器(离类近)
def decorator2(cls):
print("【装饰阶段】3. 执行 decorator2,接收的类:", cls.__name__)
def wrapper(*args, **kwargs):
print("【调用阶段】2. 执行 decorator2 的 wrapper 函数")
instance = cls(*args, **kwargs)
print("【调用阶段】3. 从 decorator2 的 wrapper 返回实例")
return instance
print("【装饰阶段】4. decorator2 执行完毕,返回 wrapper")
return wrapper
# 叠加装饰器:@decorator1 在外,@decorator2 在内
@decorator1
@decorator2
class B:
def __init__(self):
print("【调用阶段】2.5 执行类 B 的 __init__ 方法")
# --------------- 分割线:装饰阶段结束,开始调用阶段 ---------------
print("------------------开始创建实例------------------")
b = B()

通俗比喻:穿衣服和脱衣服
1. 装饰阶段(穿衣服) :先穿内层 的装饰器(离类近的@decorator2),再穿外层 的装饰器(离类远的@decorator1)。
- 就像穿秋衣(decorator2)→ 穿外套(decorator1),先穿里面的,再穿外面的。
2. 调用阶段(脱衣服) :先脱外层 的装饰器(decorator1 的 wrapper),再脱内层的装饰器(decorator2 的 wrapper),最后露出原类(B 的__init__)。
- 就像脱外套(decorator1)→ 脱秋衣(decorator2),先脱外面的,再脱里面的。
核心规律(多个装饰器叠加)
| 阶段 | 执行顺序 | 口诀 |
|---|---|---|
| 装饰阶段 | 从下到上(离类近的装饰器先执行,离类远的后执行) | 近的先穿 |
| 调用阶段 | 从上到下(离类远的装饰器的 wrapper 先执行,离类近的后执行) | 远的先脱 |
情况 3:类作为装饰器的执行顺序
类作为装饰器时,依赖两个特殊方法:__init__(初始化)和__call__(调用),它们分别对应装饰阶段 和调用阶段。
代码例子
python
# 定义类装饰器
class TimerDecorator:
def __init__(self, cls):
print("【装饰阶段】1. 执行类装饰器的 __init__ 方法,接收的类:", cls.__name__)
self.cls = cls # 保存被装饰的类
def __call__(self, *args, **kwargs):
print("【调用阶段】1. 执行类装饰器的 __call__ 方法")
instance = self.cls(*args, **kwargs)
print("【调用阶段】3. 原类实例创建完成,返回实例")
return instance
# 用类装饰器装饰类(触发装饰阶段)
@TimerDecorator
class C:
def __init__(self):
print("【调用阶段】2. 执行类 C 的 __init__ 方法")
# --------------- 分割线:装饰阶段结束,开始调用阶段 ---------------
print("------------------开始创建实例------------------")
c = C()

核心总结(类作为装饰器)
| 阶段 | 执行顺序 |
|---|---|
| 装饰阶段 | 解释器加载代码时,执行@TimerDecorator → 调用TimerDecorator(C) → 执行__init__方法 → 把C重命名为TimerDecorator的实例 |
| 调用阶段 | 创建实例c = C() → 实际调用实例的__call__方法 → 执行__call__内的代码 → 调用原类C()创建实例 → 执行C.__init__ → 返回实例 |
情况 4:装饰器装饰类的方法(扩展:容易和类的装饰器混淆)
有时候你可能会遇到 "装饰器装饰类里的方法"(比如@classmethod、@staticmethod,或自定义装饰器),这里也顺带讲一下执行顺序,避免混淆。
代码例子
python
# 定义装饰器:装饰类的方法
def method_decorator(func):
print("【装饰阶段】1. 执行 method_decorator,接收的方法:", func.__name__)
def wrapper(self):
print("【调用阶段】1. 执行 method_decorator 的 wrapper 函数")
func(self)
print("【调用阶段】3. 方法执行完毕")
print("【装饰阶段】2. method_decorator 执行完毕,返回 wrapper")
return wrapper
class D:
def __init__(self):
self.name = "D"
# 用装饰器装饰实例方法
@method_decorator
def show_name(self):
print("【调用阶段】2. 执行 show_name 方法")
# --------------- 分割线:装饰阶段结束,开始调用阶段 ---------------
print("------------------开始创建实例并调用方法------------------")
d = D()
d.show_name()

核心总结
- 装饰阶段:加载类时,立即执行装饰器函数,包装方法。
- 调用阶段:调用方法时,执行包装函数,再执行原方法。
最终总结:类的装饰器执行顺序的核心规律
为了方便你记忆,我把所有情况的核心规律整理成三句口诀:
- 装饰阶段看 "就近":多个装饰器叠加时,离类近的先执行(从下到上)。
- 调用阶段看 "远近":多个装饰器的包装函数,离类远的先执行(从上到下)。
- 类装饰器分两法 :
__init__在装饰阶段执行,__call__在调用阶段执行。
零基础学习建议
- 写代码加打印 :遇到不懂的执行顺序,就在装饰器、类的方法里加
print,运行后看输出顺序,比死记硬背更有效。 - 先练单个,再练多个:先掌握单个装饰器的执行顺序,再尝试叠加 2 个装饰器,逐步进阶。
- 记住两个阶段:永远区分 "装饰阶段(加载代码时)" 和 "调用阶段(用类时)",这是避免混淆的关键。
类的装饰器的应用场景
结合你零基础的学习背景,我会 从 "实用价值 + 通俗场景描述 + 具体代码例子"的角度,拆解类的装饰器的常见应用场景。所有例子都会保持简单易懂,延续之前的 "大白话 + 分步代码" 风格,让你不仅知道 "能用在哪",还能知道 "怎么用"。
首先回顾下类的装饰器的核心:不修改类的源码,动态给类添加功能、修改类的行为。这一特性让它在实际开发中非常灵活,以下是最常用的 6 大场景:
场景 1:类的属性 / 方法扩展(最基础、最常用)
适用情况
当你需要给多个类统一添加通用的属性或方法,但又不想在每个类里重复写代码时,用类的装饰器是最佳选择。
通俗例子
比如你开发了一个项目,里面有User(用户类)、Product(商品类)、Order(订单类),现在需要给所有类都加上创建时间、版本号 这些通用属性,以及展示通用信息的方法。如果每个类都写一遍,会重复很多代码,用类装饰器就能一次性搞定。
代码实现
python
# 定义通用的扩展装饰器
def add_common_attrs(cls):
# 给类添加通用类属性
cls.create_time = "2025-12-11" # 创建时间
cls.version = "v2.0" # 版本号
# 给类添加通用实例方法
def show_common_info(self):
print(f"【{cls.__name__}】创建时间:{self.create_time},版本号:{self.version}")
cls.show_common_info = show_common_info
return cls
# 用装饰器装饰各个类
@add_common_attrs
class User:
def __init__(self, name):
self.name = name
@add_common_attrs
class Product:
def __init__(self, price):
self.price = price
@add_common_attrs
class Order:
def __init__(self, order_id):
self.order_id = order_id
# 测试效果
u = User("小明")
u.show_common_info() # 输出:【User】创建时间:2025-12-11,版本号:v2.0
p = Product(99.9)
p.show_common_info() # 输出:【Product】创建时间:2025-12-11,版本号:v2.0
核心价值
代码复用:避免重复写通用代码,提升开发效率,后期修改也只需要改装饰器的代码,所有类都会同步更新。
场景 2:单例模式的实现(面试高频、开发常用)
适用情况
单例模式 :让一个类永远只能创建一个实例(比如系统的配置类、数据库连接类,只需要一个实例就够了,多创建会浪费资源)。用类的装饰器实现单例,简洁又灵活。
通俗例子
比如你做一个游戏,游戏的 "全局配置类"GameConfig需要保存游戏的音量、分辨率等设置,整个游戏只需要一个配置实例,所有地方都用这个实例,就可以用类装饰器实现。
代码实现
python
# 定义单例装饰器
def singleton(cls):
# 用字典保存类的唯一实例(闭包特性,保存状态)
instances = {}
def wrapper(*args, **kwargs):
# 如果类还没有实例,就创建一个
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
# 直接返回已有的实例
return instances[cls]
return wrapper
# 用装饰器装饰类,使其成为单例
@singleton
class GameConfig:
def __init__(self, volume=50, resolution="1920x1080"):
self.volume = volume
self.resolution = resolution
# 测试单例效果
config1 = GameConfig()
config2 = GameConfig(volume=80) # 尝试传新参数
# 两个变量指向同一个实例
print(config1 is config2) # 输出:True
print(config1.volume) # 输出:50(还是第一个实例的参数,因为只创建一次)
核心价值
资源节约:避免重复创建实例,减少内存占用和初始化开销,保证全局只有一个统一的实例。
场景 3:类的实例化参数校验
适用情况
当你需要检查类实例化时传入的参数是否合法 (比如参数类型、范围、是否为空),但不想在每个类的__init__方法里写校验代码时,用类装饰器可以统一实现。
通俗例子
比如你有一个Student(学生类),要求年龄必须是 10-25 的整数 、名字不能为空字符串,用类装饰器可以给类加上参数校验功能,不符合条件就直接报错。
代码实现
python
# 定义参数校验装饰器
def validate_student(cls):
# 保存原有的__init__方法
original_init = cls.__init__
# 重写__init__方法,添加校验逻辑
def new_init(self, name, age):
# 校验名字:不能为空
if not name or isinstance(name, str) is False:
raise ValueError("名字必须是非空字符串!")
# 校验年龄:必须是10-25的整数
if not isinstance(age, int) or age < 10 or age > 25:
raise ValueError("年龄必须是10-25的整数!")
# 执行原有的__init__方法
original_init(self, name, age)
# 替换类的__init__方法
cls.__init__ = new_init
return cls
# 用装饰器装饰类
@validate_student
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
# 测试校验效果
s1 = Student("张三", 18) # 合法,正常创建
print(s1.name) # 输出:张三
# s2 = Student("", 20) # 报错:ValueError: 名字必须是非空字符串!
# s3 = Student("李四", 30) # 报错:ValueError: 年龄必须是10-25的整数!
核心价值
统一校验:把参数校验的逻辑抽离出来,多个类可以复用同一个装饰器,避免在每个类里写重复的校验代码。
场景 4:日志记录与性能监控
适用情况
当你需要记录类的实例化过程、方法调用情况 ,或者统计类的方法执行时间(性能监控),用类的装饰器可以实现无侵入式的监控。
通俗例子
比如你开发了一个Calculator(计算器类),需要记录每个方法的调用时间、调用次数,方便后期排查性能问题,用类装饰器就能实现。
代码实现
python
# 定义日志和计时装饰器
def monitor_class(cls):
# 给类添加调用次数的类属性
cls.call_count = 0
# 遍历类的所有方法,给方法添加监控
for attr_name, attr_value in cls.__dict__.items():
# 只处理方法(排除属性、初始化方法等)
if callable(attr_value) and attr_name != "__init__":
# 定义包装函数
def wrapper(func):
def inner(*args, **kwargs):
import time
# 记录开始时间
start = time.time()
# 执行原方法
result = func(*args, **kwargs)
# 记录结束时间
end = time.time()
# 增加调用次数
cls.call_count += 1
# 打印日志
print(f"【监控】方法{func.__name__}调用耗时:{end - start:.6f}秒")
print(f"【监控】累计调用次数:{cls.call_count}")
return result
return inner
# 替换原方法
setattr(cls, attr_name, wrapper(attr_value))
return cls
# 用装饰器装饰类
@monitor_class
class Calculator:
def add(self, a, b):
return a + b
def multiply(self, a, b):
return a * b
# 测试监控效果
calc = Calculator()
print(calc.add(1, 2)) # 输出:3,同时打印监控日志
print(calc.multiply(3, 4)) # 输出:12,同时打印监控日志
核心价值
无侵入监控:不用修改类的原有方法,就能实现日志记录和性能统计,方便调试和线上问题排查。
场景 5:缓存 / 记忆化(提升类方法的执行效率)
适用情况
当类的某些方法执行耗时较长 ,且输入相同参数时返回结果相同(比如计算斐波那契数、查询数据库固定数据),用类装饰器可以给方法添加缓存,避免重复计算。
通俗例子
比如你有一个MathTool(数学工具类),里面的fib方法用来计算斐波那契数,多次调用相同参数时,用缓存直接返回结果,提升效率。
代码实现
python
# 定义缓存装饰器
def cache_decorator(cls):
# 遍历类的方法,给方法添加缓存
for attr_name, attr_value in cls.__dict__.items():
if callable(attr_value) and attr_name != "__init__":
# 用字典保存缓存(参数作为键,结果作为值)
cache = {}
def wrapper(func):
def inner(*args):
# 如果参数在缓存里,直接返回结果
if args in cache:
print(f"【缓存】使用缓存结果:{cache[args]}")
return cache[args]
# 否则执行方法,并存入缓存
result = func(*args)
cache[args] = result
print(f"【缓存】存入新结果:{result}")
return result
return inner
setattr(cls, attr_name, wrapper(attr_value))
return cls
# 用装饰器装饰类
@cache_decorator
class MathTool:
def fib(self, n):
# 计算斐波那契数(递归实现,耗时较长)
if n <= 1:
return n
return self.fib(n-1) + self.fib(n-2)
# 测试缓存效果
tool = MathTool()
tool.fib(10) # 第一次计算,存入缓存
tool.fib(10) # 第二次直接用缓存,不用重复计算
核心价值
提升效率:避免重复执行耗时的方法,尤其是参数重复的场景,能显著提升程序运行速度。
场景 6:权限控制(限制类方法的调用)
适用情况
当你需要限制某些用户 / 角色调用类的方法(比如只有管理员能调用删除数据的方法),用类装饰器可以统一实现权限校验。
通俗例子
比如你有一个AdminSystem(管理系统类),里面的delete_user方法只有管理员能调用,普通用户调用会报错,用类装饰器就能实现这个权限控制。
代码实现
python
# 定义权限校验装饰器
def check_permission(cls):
# 遍历类的方法,给方法添加权限校验
for attr_name, attr_value in cls.__dict__.items():
if callable(attr_value) and attr_name.startswith("delete_"):
def wrapper(func):
def inner(self, user_role, *args):
# 校验角色:只有admin能调用
if user_role != "admin":
raise PermissionError("权限不足!只有管理员能执行此操作。")
# 否则执行方法
return func(self, user_role, *args)
return inner
setattr(cls, attr_name, wrapper(attr_value))
return cls
# 用装饰器装饰类
@check_permission
class AdminSystem:
def delete_user(self, user_role, user_id):
print(f"管理员删除了用户{user_id}")
# 测试权限控制
system = AdminSystem()
system.delete_user("admin", 1001) # 合法,输出:管理员删除了用户1001
# system.delete_user("user", 1002) # 报错:PermissionError: 权限不足!只有管理员能执行此操作。
核心价值
统一权限管理:把权限校验的逻辑抽离出来,多个类可以复用,避免在每个方法里写重复的权限判断。
总结:类的装饰器应用场景核心梳理
为了方便你记忆,我把核心场景和价值整理成表格:
| 应用场景 | 核心需求 | 通俗比喻 |
|---|---|---|
| 属性 / 方法扩展 | 给多个类统一加通用属性 / 方法 | 给所有商品统一贴价格标签 |
| 单例模式实现 | 一个类只能创建一个实例 | 世界上只有一个太阳 |
| 实例化参数校验 | 检查传入参数是否合法 | 安检机检查行李是否合规 |
| 日志记录 / 性能监控 | 记录类的使用情况、统计执行时间 | 给设备装监控摄像头 |
| 缓存 / 记忆化 | 避免重复计算,提升方法执行效率 | 笔记本记录计算结果,下次直接用 |
| 权限控制 | 限制不同角色调用类的方法 | 小区大门的门禁系统 |
学习小贴士
对于零基础的你来说,不用一开始就记住所有场景,先掌握属性扩展、单例模式这两个最常用的场景,动手敲代码实现一遍,就能理解类的装饰器的核心价值。后续遇到实际问题时,再回头看其他场景,会更容易理解。
类的装饰器的优缺点
结合你零基础的学习背景,我会继续用大白话 + 实际例子 + 核心总结的方式,拆解类的装饰器的优点和缺点。每个点都会结合之前学过的例子,让你能关联起来理解,避免抽象难懂。
首先回顾类的装饰器的核心:不修改类的源码,动态给类添加 / 修改功能。这个核心特性既是它的优点来源,也带来了一些潜在问题。
一、类的装饰器的优点
类的装饰器之所以被广泛使用,是因为它解决了开发中的很多实际问题,以下是最核心的 5 个优点:
优点 1:遵循 "开放封闭原则",不破坏原代码(最核心)
通俗解释:就像给手机套壳,不用拆手机(修改原类代码)就能加保护功能,原手机的核心功能完全不受影响。如果后期想去掉壳,手机还是原来的样子。
例子验证(回顾之前的属性扩展例子):
python
# 原类:业务逻辑固定,绝不修改
class User:
def __init__(self, name):
self.name = name
# 装饰器:外部扩展功能
def add_version(cls):
cls.version = "v1.0"
return cls
# 装饰后的类:原类代码一行没改,却多了版本号属性
@add_version
class User:
def __init__(self, name):
self.name = name
核心价值 :在实际开发中,原类可能是第三方库的类、项目中已经上线的核心类,直接修改容易引发 bug,而装饰器能做到无侵入式扩展,极大降低风险。
优点 2:代码复用性极高,避免重复 "造轮子"
通俗解释 :一个装饰器可以给多个类 "服务",比如一个 "参数校验装饰器",既能给Student类用,也能给Teacher类用,不用每个类都写一遍校验代码。
例子验证(回顾参数校验例子):
python
# 通用的参数校验装饰器(写一次,用多次)
def validate_age(cls):
original_init = cls.__init__
def new_init(self, name, age):
if not isinstance(age, int) or age < 0:
raise ValueError("年龄必须是正整数!")
original_init(self, name, age)
cls.__init__ = new_init
return cls
# 给Student类用
@validate_age
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
# 给Teacher类用
@validate_age
class Teacher:
def __init__(self, name, age):
self.name = name
self.age = age
核心价值:减少重复代码,提升开发效率,后期如果要修改校验逻辑,只需要改装饰器的代码,所有被装饰的类都会同步更新,不用逐个修改。
优点 3:功能动态可控,灵活调整
通俗解释:装饰器就像给类 "贴贴纸",可以随时贴(添加装饰器)、撕(移除装饰器)、换(替换装饰器)、叠(多个装饰器叠加),完全不影响类本身。
例子验证(动态切换装饰器):
python
# 原类
class Product:
def __init__(self, price):
self.price = price
# 装饰器1:加日志
def log_decorator(cls):
# 实现日志功能...
return cls
# 装饰器2:加缓存
def cache_decorator(cls):
# 实现缓存功能...
return cls
# 开发环境:加日志装饰器
@log_decorator
class Product:
def __init__(self, price):
self.price = price
# 生产环境:换成缓存装饰器(只需改@后的装饰器名)
@cache_decorator
class Product:
def __init__(self, price):
self.price = price
核心价值:可以根据不同的场景(开发 / 测试 / 生产)灵活调整类的功能,不用重构类的代码。
优点 4:逻辑解耦,让代码更清晰
通俗解释:把通用逻辑(比如日志、校验、缓存)和业务逻辑(比如用户管理、商品管理)分开,就像把衣服的 "装饰图案" 和 "布料本身" 分开,衣服的主体功能更纯粹。
例子对比:
| 无装饰器(耦合) | 有装饰器(解耦) |
|---|---|
| 类里既写业务逻辑,又写校验、日志 | 类里只写业务逻辑,校验 / 日志交给装饰器 |
| 代码又长又乱,分不清核心逻辑 | 代码简洁,核心逻辑一目了然 |
核心价值:后期维护时,想改业务逻辑就看类本身,想改通用逻辑就看装饰器,不用在一堆代码里找来找去。
优点 5:语法糖@让代码更简洁易读
通俗解释 :用@装饰器名的方式装饰类,比手动调用装饰器函数更直观,一眼就能看出类被添加了什么功能。
例子对比:
python
# 手动调用装饰器(繁琐)
class User:
pass
User = add_version(User)
# 用@语法糖(简洁)
@add_version
class User:
pass
核心价值:代码更简洁,可读性更高,这也是 Python 设计装饰器语法糖的初衷。
二、类的装饰器的缺点
类的装饰器并非完美,尤其是对零基础的学习者来说,它的一些特性可能会带来理解和使用上的困难,以下是最核心的 5 个缺点:
缺点 1:增加代码的理解成本(尤其是多层装饰器)
通俗解释:装饰器的本质是 "函数嵌套 + 闭包 + 语法糖",零基础的你可能会看不懂装饰器的执行流程,尤其是多个装饰器叠加时,执行顺序更难理解。
例子(多层装饰器的执行顺序问题):
python
# 装饰器1
def decorator1(cls):
print("执行decorator1")
return cls
# 装饰器2
def decorator2(cls):
print("执行decorator2")
return cls
# 多层装饰器:执行顺序是从下到上(先decorator2,再decorator1)
@decorator1
@decorator2
class User:
pass
# 输出:
# 执行decorator2
# 执行decorator1
踩坑点 :很多初学者会以为装饰器的执行顺序是从上到下,实际是就近原则(离类最近的装饰器先执行),这会增加理解难度。
缺点 2:调试难度增加,"隐藏" 了原类的信息
通俗解释 :装饰器会替换原类或原方法,调试时你看到的可能是装饰器的包装函数(比如wrapper),而不是原类的方法,很难定位问题。
例子(调试时的问题):
python
def singleton(cls):
instances = {}
def wrapper(*args):
if cls not in instances:
instances[cls] = cls(*args)
return instances[cls]
return wrapper
@singleton
class GameConfig:
def __init__(self):
self.volume = 50
# 调试时,GameConfig实际是wrapper函数,不是原类
print(GameConfig) # 输出:<function singleton.<locals>.wrapper at 0x0000021F8A7F7E20>
踩坑点 :用print()或调试工具查看类时,看到的是装饰器的包装函数,而不是原类,容易让人困惑。
缺点 3:可能产生副作用,容易踩坑
通俗解释:一些装饰器(比如单例装饰器)会改变类的原有行为,如果不了解其内部逻辑,很容易出现预期之外的结果。
例子(单例装饰器的副作用):
python
@singleton
class GameConfig:
def __init__(self, volume=50):
self.volume = volume
# 第一次创建实例:正常
config1 = GameConfig(volume=80)
print(config1.volume) # 输出:80?不,输出50!
# 原因:单例装饰器的wrapper函数里,第一次创建实例后,后续传参都无效
踩坑点:初学者可能以为第二次传参能修改实例的属性,实际因为单例的特性,参数会被忽略,这就是装饰器的副作用。
缺点 4:特殊情况下会影响类的继承和属性访问
通俗解释 :如果装饰器返回的是函数 (比如单例装饰器的wrapper函数),而不是原类的子类,那么继承这个类时可能会出现问题。
例子(继承问题):
python
@singleton
class Parent:
def show(self):
print("我是父类")
# 子类继承Parent(实际是继承wrapper函数,不是原类)
class Child(Parent):
pass
# 报错:TypeError: function() argument 'code' must be code, not str
# 因为wrapper是函数,不能被继承
解决办法:可以优化装饰器,让其返回原类的子类(比如用类装饰器代替函数装饰器),但这会增加装饰器的复杂度。
缺点 5:过度使用会导致代码分散,不利于整体阅读
通俗解释 :如果一个类被多个装饰器装饰,比如@log、@cache、@validate、@singleton,那么类的功能被分散在多个装饰器里,想了解类的完整功能,需要逐个查看装饰器的代码,反而降低了可读性。
例子(过度装饰的问题):
python
@log
@cache
@validate
@singleton
@add_version
class User:
def __init__(self, name):
self.name = name
踩坑点:初学者容易滥用装饰器,给每个类都加一堆装饰器,导致代码逻辑分散,后期维护时需要来回切换文件查看装饰器,效率低下。
三、类的装饰器的优缺点总结(表格版)
为了方便你记忆,我把核心优缺点整理成表格:
| 类别 | 具体点 | 通俗比喻 | 核心影响 |
|---|---|---|---|
| 优点 | 遵循开放封闭原则 | 手机套壳不拆机 | 无侵入扩展,降低修改风险 |
| 代码复用性高 | 一个模具做多个零件 | 避免重复代码,提升开发效率 | |
| 功能动态可控 | 随时换手机壳 | 灵活适配不同场景 | |
| 逻辑解耦 | 衣服和装饰图案分开 | 代码更清晰,易维护 | |
| 语法糖简洁易读 | 用快捷键代替长命令 | 代码更直观,可读性高 | |
| 缺点 | 增加理解成本 | 叠了多层纸的画,看不清底层 | 初学者难理解执行流程 |
| 调试难度增加 | 戴了面具的人,认不出本来面目 | 调试时难定位原类 / 方法 | |
| 可能有副作用 | 吃药治病但有副作用 | 改变类的原有行为,易踩坑 | |
| 影响继承(特殊情况) | 嫁接的树枝长不出原树的果子 | 继承时可能出现错误 | |
| 过度使用导致代码分散 | 把一篇文章拆成多个小片段 | 不利于整体阅读和维护 |
四、零基础学习建议:如何正确使用类的装饰器
针对你的情况,我给出几个实用建议,帮你避开缺点,发挥优点:
1. 先学简单的装饰器,再学复杂的
先掌握属性扩展、简单日志 这类基础装饰器,再学单例、缓存、多层装饰器,一步一个脚印,不要急于求成。
2. 避免过度使用,够用就好
一个类最多用 1-2 个装饰器,比如给User类加 "参数校验 + 日志" 就够了,不要加一堆装饰器。
3. 给装饰器写详细注释
比如:
python
def singleton(cls):
"""
单例装饰器:让类只能创建一个实例
:param cls: 被装饰的类
:return: 包装函数,返回唯一实例
"""
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
注释能帮你和其他人理解装饰器的功能,降低理解成本。
4. 调试时用__wrapped__获取原类 / 方法
Python 提供了__wrapped__属性,可以获取被装饰的原类 / 方法,比如:
python
@singleton
class GameConfig:
pass
# 获取原类
original_cls = GameConfig.__wrapped__
print(original_cls) # 输出:<class '__main__.GameConfig'>
5. 优先用函数装饰器(简单),特殊场景用类装饰器
对于零基础的你,函数装饰器更容易理解和实现,类装饰器(需要__call__方法)可以后期再深入。
最后总结
类的装饰器是 Python 中非常强大的特性,优点远大于缺点,只要你掌握正确的使用方法,就能极大提升开发效率。对于零基础的你来说,不用害怕它的缺点,只要多敲代码、多调试,慢慢就能理解其内部逻辑,做到灵活运用。
作业:复习类和函数的知识点,写下自己过去一个月的学习心得,如对函数和类的理解,对 python这门工具的理解等。
作为零基础学习者,我们不用纠结复杂的专业术语,而是用生活中的具体事物类比,从 "是什么 - 有什么用 - 怎么关联" 的逻辑,把 Python、函数、类的核心讲清楚。
一、先理解 Python:一门 "说人话" 的万能工具箱
如果把编程比作 "解决生活问题",那么 Python 就是你手里的万能工具箱------ 它不像某些工具箱(比如 C++)需要你先学复杂的工具组装规则,而是打开就能用,还自带各种现成的 "配件"。
1. Python 的核心特点:为什么零基础也能学?
(1)语法像 "聊天",不用记复杂符号
比如想让计算机打印 "你好",Python 只需要写:
python
print("你好")
就像你跟计算机说 "帮我打出来'你好'" 一样,比其他语言的System.out.println("你好")(Java)、printf("你好\n")(C 语言)简单太多。
(2)自带 "海量配件",不用自己从头造
Python 有一个超丰富的生态库(可以理解为工具箱里的 "预制配件包"):
- 想爬取网页数据?用
requests库(像现成的 "爬墙梯"); - 想做数据分析?用
pandas库(像现成的 "统计表格"); - 想做自动化脚本(比如自动发邮件)?用
smtplib库(像现成的 "邮件发射器")。
这些库就像你买工具箱时,商家直接送了你几百个专用配件,不用你自己敲铁皮做配件。
(3)"万能" 到能解决各种问题
Python 能做的事几乎覆盖了编程的所有场景:
- 小到写个自动整理文件的脚本、做个计算器;
- 大到做数据分析、人工智能、网页开发、游戏开发。
就像万能工具箱既能拧螺丝、钉钉子,也能修家电、装家具。
(4)跨平台 "通用",在哪都能用
你在 Windows 电脑上写的 Python 代码,复制到 Mac 或 Linux 电脑上,几乎不用改就能运行 ------ 就像你的工具箱在客厅、卧室、车库都能用来干活。
2. Python 的设计思想:简单就是美
Python 的作者一直强调 "用一种方法做一件事",不像有些语言有多种写法。这对零基础的你来说特别友好:不用纠结 "哪种写法更好",只要学一种最直接的方式就行。
二、再理解函数:工具箱里的 "单个专用工具"
当你用工具箱干活时,比如想打孔,你不会每次都 "找电线→接电源→拿钻头→旋转钻孔",而是直接用电钻这个专用工具 ------ 函数就是 Python 里的 "电钻"。
1. 函数的本质:封装重复的操作,一次造工具,多次用
生活例子:做蛋炒饭
如果你每天都要做蛋炒饭,步骤是:
- 打鸡蛋、搅拌;
- 热油、炒鸡蛋;
- 加米饭、翻炒;
- 加盐、出锅。
你不会每天都在脑子里重复想这 4 步,而是把它记成 "蛋炒饭流程"------ 函数就是把这 4 步封装成一个 "工具",需要时直接喊 "做蛋炒饭" 就行。
代码例子:用函数实现 "蛋炒饭"
python
# 定义函数:造"做蛋炒饭"的工具(参数是"加多少盐")
def make_fried_rice(salt_amount):
print("1. 打鸡蛋、搅拌")
print("2. 热油、炒鸡蛋")
print("3. 加米饭、翻炒")
print(f"4. 加{salt_amount}勺盐,出锅")
return "香喷喷的蛋炒饭" # 返回值:工具的"成品"
# 调用函数:用工具做蛋炒饭(传参数"1勺盐")
rice = make_fried_rice(1)
print(rice) # 输出:香喷喷的蛋炒饭
# 再调用一次:换个参数,做淡一点的蛋炒饭
rice2 = make_fried_rice(0.5)
2. 函数的核心要素:造工具的 4 个关键
| 要素 | 生活类比(电钻) | 代码对应 |
|---|---|---|
| 定义 | 工厂造电钻的过程 | def 函数名(): |
| 参数 | 电钻的钻头、打孔深度 | 函数括号里的变量 |
| 调用 | 拿起电钻按开关 | 函数名(参数) |
| 返回值 | 打好的孔 / 打孔成功的结果 | return 后的内容 |
3. 函数的价值:为什么必须学?
- 避免重复劳动 :比如你需要在 10 个地方打印 "欢迎语",写一次函数,调用 10 次就行,不用写 10 遍
print("欢迎"); - 拆分复杂任务:比如做一个电商网站,把 "用户登录""商品结算""物流查询" 拆成不同函数,代码就不会乱成一团;
- 方便修改:如果想改 "蛋炒饭" 的做法,只需要改函数里的代码,所有调用的地方都会同步更新。
三、最后理解类:工具箱里的 "物品模板 + 套装工具"
函数是 "单个工具"(比如电钻),而类是 **"物品的模板"+"套装工具"**------ 比如 "汽车" 这个类,既是造汽车的模板,也包含了 "启动、加速、刹车" 等一套行为工具。
1. 类的本质:模拟现实世界的事物,把 "特征" 和 "行为" 打包
现实世界的任何事物都有特征(属性)和行为(方法):
- 比如 "狗":特征是名字、颜色、品种;行为是叫、跑、摇尾巴。
- 比如 "手机":特征是品牌、型号、电量;行为是打电话、发微信、拍照。
类就是把这些特征和行为打包成一个模板,用模板可以造出无数个具体的 "实例"(比如一只叫 "旺财" 的金毛,一部苹果 15 手机)。
生活例子:汽车模板(类)与具体汽车(实例)
| 类(汽车模板) | 实例(你家的白色特斯拉) |
|---|---|
| 特征(类属性):4 个轮子、有方向盘 | 特征(实例属性):白色、型号 Model 3、电量 80% |
| 行为(方法):启动、加速、刹车 | 行为:你的特斯拉启动后,能开到 60 码 |
代码例子:用类实现 "狗"
python
# 定义类:造"狗"的模板
class Dog:
# 类属性:所有狗都有的特征(共享)
leg_num = 4
# 初始化方法:创建实例时,给实例加专属特征(名字、颜色)
def __init__(self, name, color):
self.name = name # 实例属性:专属名字
self.color = color # 实例属性:专属颜色
# 方法:狗的行为(叫)
def bark(self):
print(f"{self.name}({self.color})汪汪叫!")
# 用模板造实例:具体的狗
wangcai = Dog("旺财", "黄色")
xiaobai = Dog("小白", "白色")
# 访问特征
print(wangcai.leg_num) # 输出:4(类属性,所有狗共享)
print(wangcai.name) # 输出:旺财(实例属性,专属)
# 调用行为
wangcai.bark() # 输出:旺财(黄色)汪汪叫!
xiaobai.bark() # 输出:小白(白色)汪汪叫!
2. 类的核心要素:模板的 3 个关键
(1)类与实例:模板和具体物品
- 类:是抽象的模板(比如 "狗"),不占内存;
- 实例:是具体的物品(比如旺财),占内存,有专属特征。
就像建筑图纸(类)和用图纸盖的房子(实例):图纸只是设计,房子才是能住的具体建筑。
(2)属性:事物的特征(分两种)
- 类属性:所有实例共享的特征(比如所有狗都有 4 条腿);
- 实例属性:每个实例的专属特征(比如旺财是黄色,小白是白色)。
(3)方法:事物的行为(分三种)
- 实例方法 :专属实例的行为(比如旺财叫),第一个参数是
self(代表实例本身); - 类方法 :操作类属性的行为(比如修改所有狗的腿数),用
@classmethod装饰,第一个参数是cls(代表类本身); - 静态方法 :和类、实例无关的通用行为(比如狗的通用规则 "不能吃巧克力"),用
@staticmethod装饰,无默认参数。
3. 类的三大特性:让模板更灵活(用生活例子讲透)
(1)封装:把细节藏起来,只用表面的按钮
比如你用手机打电话,只需要按号码、点拨打,不用知道手机内部的信号怎么传输、芯片怎么工作 ------ 类的封装就是把 "内部的复杂逻辑" 藏起来,外部只需要调用方法就行。
(2)继承:儿子继承父亲的财产,还能自己赚新钱
比如 "金毛" 是 "狗" 的子类,它继承了狗的所有特征(4 条腿、会叫),还能加自己的特征(比如 "会游泳")。
代码例子:
python
# 父类:狗
class Dog:
def bark(self):
print("汪汪叫")
# 子类:金毛(继承狗)
class GoldenRetriever(Dog):
def swim(self):
print("会游泳")
# 实例:金毛狗
jinmao = GoldenRetriever()
jinmao.bark() # 继承的行为:汪汪叫
jinmao.swim() # 自己的行为:会游泳
(3)多态:同一行为,不同事物有不同表现
比如 "跑" 这个行为:
- 狗跑是用四条腿;
- 人跑是用两条腿;
- 汽车跑是用轮子。
在代码里,不同的类可以有同名的方法,执行时会根据实例的类型,表现出不同的效果。
4. 类的价值:为什么比函数更强大?
- 模拟现实 :能把现实世界的事物直接转化为代码,比如做一个宠物管理系统,用
Dog类、Cat类就能清晰表示不同宠物; - 组织大型项目:当代码量上万行时,用类把相关的函数和属性打包,比一堆零散的函数更易维护;
- 代码复用:通过继承,子类能复用父类的代码,不用重复写。
四、函数与类的关联:从 "单个工具" 到 "套装工具"
很多初学者会问:"有了函数,为什么还要学类?" 其实它们是互补的:
- 小任务用函数:比如写一个计算圆面积的工具,用函数就够了;
- 大任务用类:比如做一个游戏里的 "角色系统"(有战士、法师、牧师),每个角色有自己的属性和技能,用类打包会更清晰。
简单说:函数是 "单打独斗" 的工具,类是 "团队协作" 的工具包。
五、文字版思维导图:核心知识框架梳理
为了方便你整体记忆,我把所有内容整理成层级清晰的思维导图:

六、零基础学习小贴士
- 先练函数,再学类:函数是基础,先把函数的参数、返回值练熟,再学类的概念,难度会低很多;
- 用生活例子类比:遇到不懂的概念,先想生活中对应的事物(比如类 = 模板,实例 = 具体物品),再看代码;
- 多敲代码少死记 :比如学类时,自己写一个
Student类、Teacher类,运行后看效果,比背概念更有效。
总结来说,Python 是让你轻松解决问题的 "万能工具箱",函数是里面的 "单个工具",类是里面的 "套装工具包"------ 掌握了它们,你就能用 Python 解决大部分实际问题了。