【Python零基础到进阶】装饰器与生成器
【Python零基础到进阶】装饰器与生成器
✅ 包含编程资料、学习路线图、源代码、软件安装包等!【[点击这里]】!
属性控制
一、基础方法属性化
-
- 传统方法控制
1 # 1. 传统方法控制
2 class Test:
3 def __init__(self):
4 self._text = '我不想让你看见'
5
6 # 显式操作接口
7 def get_t(self):
8 return self._text
9
10 def set_t(self, value):
11 self._text = value
12
13 def del_t(self):
14 del self._text
15
16
17 # 使用示例
18 a = Test()
19 print(a.get_t()) # 调用并查看属性
20 print(a._text) # 也可以直接调用属性
21 a.set_t('新值') # 显式调用方法修改属性值
22 print(a.get_t()) # 输出:新值
23 a.del_t() # 删除属性
24 print(a.get_t()) # 删除后调用,报错:AttributeError: 'Test' object has no attribute '_text'

传统方法的问题;
✔️ 每次操作都要记方法名
✔️ 可以直接访问a._text不安全
✔️ 代码不够简洁直观
- 为了能简化这些操作步骤,我们可以选择 property()函数来替代那些繁琐的步骤。
二、property()函数进阶
-
- 属性绑定方法
1 class Test:
2 """
3 保持原有方法不变
4 """
5 def __init__(self):
6 self._text = '我不想让你看见'
7
8 def get_t(self):
9 return self._text
10
11 def set_t(self, value):
12 self._text = value
13
14 def del_t(self):
15 del self._text
16
17 # 把三个方法打包成属性
18 x = property(get_t, set_t, del_t)
19
20
21 # 实例化对象
22 a = Test()
23 """
24 a.x 将触发 getter方法,
25 a.x = value 将触发 setter方法,
26 del a.x 触发 deleter方法.
27 """
28 print(a.x) # 隐式调用get_t → 输出:我不想让你看见
29 a.x = '马甲属性' # 隐式调用set_t(value),设置属性
30 print(a.x) # 隐式调用get_t → 输出:马甲属性
31 del a.x # 隐式调用del_t 删除属性
32 print(a.x) # 调用已删除的属性

解决的问题:
✔️ 用属性语法替代方法调用
✔️ 隐藏真实属性名_text
✔️ 保持数据验证能力(可在方法中添加检查)
- 2.加一层异常捕获:健壮性改造
1 class Test:
2 def __init__(self):
3 self._text = '我不想让你看见'
4
5 # 带异常处理的访问器
6 def get_t(self):
7 try:
8 return self._text
9 except:
10 return '属性不存在'
11
12 def set_t(self, value):
13 self._text = value
14
15 def del_t(self):
16 del self._text
17
18 x = property(get_t, set_t, del_t)
19
20
21 a = Test()
22 print(a.x)
23 a.x = '我穿上了马甲'
24 print(a.x)
25 del a.x
26 print(a.x) # 输出:属性不存在

解决的问题:
✔️ 增加了异常捕获,在访问不存在的属性时可以不报AttributeError的错误。
三、property()函数的装饰器使用方法
-
- 实现
1 class Test:
2 def __init__(self):
3 self._text = '我不想让你看见'
4
5 # @property 修饰了text()方法,这样就使得该方法变成了text属性.
6 # 需要注意的是,如果类中只包含该方法,那么text属性将是一个只读属性
7 @property # 替代get_t
8 def text(self):
9 try:
10 return self._text
11 except:
12 return '访问属性不存在'
13
14 # 而要想实现修改text属性的值,还需要为text属性添加 setter 方法,
15 # 就需要用到 setter 装饰器,它的语法格式如下:
16 @text.setter # # 替代set_t
17 def text(self, value):
18 self._text = value
19
20 # 除此之外,还可以使用 deleter 装饰器来删除指定属性,其语法格式为:
21 @text.deleter # 替代del_t
22 def text(self):
23 del self._text
24
25 a = Test()
26 print(a.text) # 无需括号,和属性完全一致
27
28 a.text = '我又换马甲了' # 直接赋值
29 print(a.text)
30
31 del a.text # 直接删除
32 print(a.text)

方法优势:
✔️ 每个操作对应一个装饰器
✔️ 方法名即属性名,更直观
✔️ 方便添加类型检查等逻辑
五、property()函数语法总结
-
1.property()基本语法结构
属性对象 = property(fget=None, fset=None, fdel=None, doc=None) -
2.参数说明

-
- 返回值特性
返回属性描述符对象
自动绑定到类属性名
- 返回值特性
-
4.装饰器用法
注意>定义顺序要求:
-
- 必须先声明 @property 方法
-
- setter/deleter 装饰器必须引用已存在的property方法
-
- 最终属性名由 @property 修饰的方法名决定
正确用法示例:
- 最终属性名由 @property 修饰的方法名决定
1 class Demo:
2 @property
3 def attr(self): # ① 定义getter
4 return self._value
5
6 @attr.setter # ② 定义setter(必须引用已存在的attr)
7 def attr(self, value):
8 self._value = value
9
10 @attr.deleter # ③ 定义deleter
11 def attr(self):
12 del self._value

错误用法示例:
1 class ErrorDemo:
2 @attr.setter # ❌ 未先定义@property
3 def attr(self, value):
4 self._value = value
5
6 @property
7 def attr(self):
8 return self._data
9
10 @attr.deleter
11 def attr(self):
12 pass # ❌ 方法名不一致

- 顺序不可逆:必须按
@property → @xxx.setter → @xxx.deleter顺序声明 - 方法名统一:所有装饰方法必须使用相同的名称
- 属性名锁定:最终属性名由
@property修饰的方法名决定
装饰器
- 1.装饰器的概念
装饰器是一个函数,用于增强另一个函数的功能。通过高阶函数和闭包实现。
主要功能:在不修改原函数代码的前提下增加新功能 - 2.闭包机制回顾
回顾案例:
1 def func1(a):
2 print(a) # ① 打印外层参数
3 print("函数 func1 正在执行")
4 def func2(b):
5 print(b) # ③ 打印内层参数
6 print("函数 func2 正在执行")
7 return a + b # ④ 访问外层变量
8 return func2 # ② 返回内层函数
9
10 # 调用方式一(分步执行)
11 f = func1(1) # 触发① → 返回func2
12 print(f) # 输出:<function func1.<locals>.func2 at 0x...>
13 c = f(2) # 触发③④ → 返回3
14 print(c) # 输出:3
15
16 # 调用方式二(链式调用)
17 c = func1(1)(2) # 依次触发①③④
18 print(c) # 输出:3

闭包与装饰器的关联:
- 环境保留:func2持续访问func1的变量a
- 函数工厂:func1生成不同配置的func2
- 延迟执行:返回函数对象而非立即执行
3.使用装饰器和不使用装饰器的区别
通过计算函数运行时间来对比下
非装饰器写法:
1 import time
2
3 def timer(func):
4 print('开始计时') # ① 开始计时标记
5 start = time.time() # ② 记录开始时间
6 func() # ③ 执行原函数
7 end = time.time() # ④ 记录结束时间
8 print('计时结束') # ⑤ 结束计时标记
9 return f'一共花费了{end - start}秒的时间' # ⑥ 返回计时结果
10
11 def our_func():
12 n = 0
13 for i in range(1000001):
14 n += i
15 print('装饰器的计时工具') # ⑦ 原函数执行标记
16 print(n) # ⑧ 输出计算结果
17 return n
18
19 # 调用方式
20 t = timer(our_func) # 必须显式调用timer
21 print(t) # 输出计时结果

装饰器用法:
装饰器的本质是一个闭包函数 🔻
1 import time
2
3 def timer(func): # ① 接受被装饰函数
4 def call(): # ② 定义包装函数
5 print('开始计时') # 1
6 start = time.time()
7 n = func() # ③ 执行原函数
8 end = time.time()
9 print('计时结束') # 3
10 print(f'一共花费了{end - start}秒的时间')
11 return n # ④ 返回原函数结果
12 return call # ⑤ 返回包装函数
13
14 @timer # ⑥ 应用装饰器
15 def our_func():
16 n = 0
17 for i in range(1000001):
18 n += i
19 print('装饰器的计时工具') # 2
20 return n
21
22
23 # 使用装饰器:相当于省略了这步>our_func = timer(our_func)
24 t = our_func()
25 print(t)
运行结果:
区别总结:
- 调用方式
- 非装饰器:必须显式调用 timer(our_func),改变原函数调用方式
- 装饰器:直接调用 our_func(),保持原函数调用方式
- 代码侵入性
- 非装饰器:所有调用处需修改为 timer(our_func),牵一发而动全身
- 装饰器:只需在函数定义处添加 @timer,无需修改调用代码
- 功能复用性
- 非装饰器:需重复包裹逻辑,代码冗余
- 装饰器:一次定义多处复用,代码简洁
- 设计原则
- 非装饰器:违反开放封闭原则(修改原调用方式)
- 装饰器:符合开放封闭原则(扩展而非修改)
4.语法糖
什么是语法糖?
- 定义:通过语法特性简化代码,提升可读性和开发效率
- 作用:隐藏底层实现细节,让代码更加简洁直观
- 比如:@decorator 是 func = decorator(func)语法糖
无语法糖写法:
1 def decor(func):
2 def name():
3 print("被装饰的函数 add 即将执行")
4 func()
5 print("被装饰的函数 add 已执行完毕")
6 return name
7
8 def add():
9 print("函数 add 正在执行 ")
10
11 # 手动包装函数
12 add = decor(add) # 等价于 @decor
13 add() # 调用包装后的函数

语法糖写法
1 def decor(func):
2 def name():
3 print("被装饰的函数 add 即将执行")
4 func()
5 print("被装饰的函数 add 已执行完毕")
6 return name
7
8 @decor # 语法糖应用
9 def add():
10 print("函数 add 正在执行 ")
11
12 # 直接调用
13 add() # 自动触发装饰器逻辑

区别总结:
1.装饰器调用方式
- 无语法糖:需要手动调用装饰器函数,显式替换原函数
也就是:add = decor(add) - 语法糖:通过 @decor 自动调用装饰器函数,隐式替换原函数
也就是:
1 @decor
def add(): ...
2.代码简洁性
- 无语法糖:代码分散,装饰器调用和函数定义分离
- 语法糖:代码集中,装饰器声明与函数定义在一起
3.可读性 - 无语法糖:需要额外理解 add = decor(add) 的替换逻辑
- 语法糖:通过 @ 符号直观标识装饰器,逻辑更清晰
4.维护性 - 无语法糖:修改装饰器时需调整调用代码
- 语法糖:修改装饰器时无需调整调用代码
5.为什么需要装饰器?
- 传统实现案例:
- 代码主体:
1 # 累加操作
2 def add(a):
3 start_time = time.time() # 计时逻辑
4 total = 0
5 for i in range(0, a):
6 total += i
7 end_time = time.time()
8 exe_time = end_time - start_time
9 print(f'累加操作,花费的时间是{exe_time}') # 计时逻辑
10 return total
11
12 # 累乘操作
13 def mul(a):
14 start_time = time.time() # 计时逻辑
15 product = 1
16 for i in range(1, a):
17 product *= i
18 end_time = time.time()
19 exe_time = end_time - start_time
20 print(f'累乘操作,花费的时间是{exe_time}') # 计时逻辑
21 return product
代码调用:
1 # 累加的调用
2 total = add(100001)
3 print(total) # 输出:5000050000
4
5 # 累乘的调用
6 product = mul(100001)
7 print(product) # 输出:累乘结果(非常大)
装饰器实现方法:
定义装饰器
1 import time
2
3 def decorat(func):
4 def wrapper(a): # 定义包装函数
5 start_time = time.time()
6 result = func(a) # 执行原函数
7 end_time = time.time()
8 exe_time = end_time - start_time
9 print(f'该操作,花费的时间是{exe_time}')
10 return result # 返回原函数结果
11 return wrapper
使用装饰器
1 @decorat
2 def add(a):
3 print('开始执行累加操作:')
4 total = 0
5 for i in range(0, a):
6 total += i
7 return total
8
9 @decorat
10 def mul(a):
11 print('开始执行累乘操作:')
12 product = 1
13 for i in range(1, a):
14 product *= i
15 return product
代码调用
1 # 累加操作
2 f = add(100001)
3 print(f) # 输出:5000050000
4
5 # 累乘操作
6 g = mul(100001)
7 print(g) # 输出:累乘结果(非常大)
生成器
1.什么是生成器?
- 生成器是迭代器的一种实现方式。它通过
yield关键字生成数据,每次调用生成器时,会返回一个值并暂停执行,直到下一次调用。
2.案例说明:依次打印 0-1000 的数字
- 方式 1:使用函数生成(一次性保存到列表中)
1 def abc():
2 lyst = []
3 for i in range(1001):
4 lyst.append(i)
5 return lyst
6
7 for i in abc():
8 print(i)

- 方式 2:使用生成器(每次调用生成一个数据)
1 def gen():
2 for i in range(1001):
3 yield i
4
5 for i in gen():
6 print(i)

- 方式 3:使用生成器推导式
1 a = (i for i in range(1001))
2 for i in a:
3 print(i)

3.生成器的优势
- 1.消耗内存少:处理大量数据时,只需要加载当前使用的数据,不需要一次性加载所有数据。
- 2.语法简单:通过 yield 或推导式定义生成器,代码简洁易读。
4.生成器的第一种定义方式(函数式生成器)
生成器的第一种定义方式是通过 函数加 yield 关键字 来定义。
普通函数 vs 生成器
普通函数:
1 def intNum(n):
2 print("开始执行")
3 for i in range(n):
4 return i
5 print("继续执行")
6
7 num = intNum(5)
8 print(num) # 输出:0
9 num = intNum(5)
10 print(num) # 输出:0
11 num = intNum(5)
12 print(num) # 输出:0
问题:
- 普通函数使用 return 后立即退出,无法生成多个值。
- 每次调用函数都会重新执行,无法保留状态。
生成器:
1 def intNum(n):
2 print("开始执行")
3 for i in range(n):
4 yield i
5 print("继续执行")
6
7 num = intNum(5)
8 print(next(num)) # 输出:开始执行 0
9 print(next(num)) # 输出:继续执行 1
10 print(next(num)) # 输出:继续执行 2
11 print(next(num)) # 输出:继续执行 3
12 print(next(num)) # 输出:继续执行 4
13 print(next(num)) # 抛出 StopIteration 异常
优势:
- 生成器使用 yield,每次调用 next() 时生成一个值并暂停执行,保留函数状态。
- 不需要一次性生成所有数据,节省内存。
错误示例:
1 print(next(intNum(5))) # 输出:开始执行 0
2 print(next(intNum(5))) # 输出:开始执行 0
3 print(next(intNum(5))) # 输出:开始执行 0
错误原因>>>每次调用 intNum(5) 都会创建一个新的生成器对象,无法保留状态。
while 循环定义生成器
1 def fib(max):
2 n = 0
3 while n < max:
4 yield n
5 n += 1
6
7 a = fib(9)
8 print(next(a)) # 输出:0
9 print(next(a)) # 输出:1
10 print(next(a)) # 输出:2
11 print(next(a)) # 输出:3
12 print(next(a)) # 输出:4
13 print(next(a)) # 输出:5
14 print(next(a)) # 输出:6
15 print(next(a)) # 输出:7
16 print(next(a)) # 输出:8
17 print(next(a)) # 抛出 StopIteration 异常

说明:
- 使用 while 循环定义生成器,可以更灵活地控制生成逻辑。
- 每次调用 next() 时,生成器会执行到 yield 并返回当前值,然后暂停执行。
案例:斐波那契数列
通过while循环迭代器,实现斐波那契数列
生成器主体
1 def fib(max):
2 a, b = 0, 1
3 while a < max:
4 yield a
5 print('---')
6 a, b = b, a + b
- 使用生成器输出斐波那契数列
1 # 使用生成器输出斐波那契数列
2 a = fib(15)
3 print(next(a)) # 输出:0
4 print(next(a)) # 输出:1
5 print(next(a)) # 输出:1
6 print(next(a)) # 输出:2
7 print(next(a)) # 输出:3
8 print(next(a)) # 输出:5
9 print(next(a)) # 输出:8
10 print(next(a)) # 输出:13
11 print(next(a)) # 抛出 StopIteration 异常

说明:
- 生成器 fib(max) 会生成斐波那契数列,直到值大于或等于 max。
- 每次调用 next() 时,生成器会执行到 yield 并返回当前值,然后暂停执行。
- 通过 a, b = b, a + b 更新数列的下一个值。
遍历生成器输出斐波那契数列
1 for n in fib(15):
2 print(n)

5.生成器的第二种定义方式(生成器表达式)
- 这种生成器定义方式是 使用推导式。与列表推导式类似,生成器推导式使用圆括号 () 而不是方括号 [],而且它返回的是一个生成器对象,不是列表。
1.基本语法:
- g = (expression for item in iterable)
- expression:生成器每次生成的值的表达式。
- item:迭代变量,从 iterable 中取值。
- iterable:可迭代对象(如列表、范围等)。
2.基本示例:
1 g = (i for i in range(100))
2 print(g) # 输出:<generator object <genexpr> at 0x...>
3
4 # 遍历生成器
5 for i in g:
6 print(i)
- g 是一个生成器对象
- 需要 遍历 g(或使用next(g)),查看生成器的值。
3.加条件筛选:
- 生成器推导式可以添加条件筛选,只生成符合条件的值。
1 # 只生成能被 3 整除的数
2 g = (i for i in range(100) if i % 3 == 0)
3 print(g) # 输出:<generator object <genexpr> at 0x...>
4
5 # 遍历生成器
6 for i in g:
7 print(i)
4.带条件判断的生成器推导式
- 生成器推导式可以结合条件判断,生成不同的值。
1 # 小于 10 的数直接输出,否则输出 '哈哈'
2 g = (i if i < 10 else '哈哈' for i in range(100))
3 print(g) # 输出:<generator object <genexpr> at 0x...>
4
5 # 遍历生成器
6 for i in g:
7 print(i)
5.对比列表推导式
生成器推导式与列表推导式的语法非常相似,但它们的返回值不同:
- 生成器推导式返回生成器对象。
- 列表推导式返回列表对象。
例如:
1 # 生成器推导式
2 g = (i if i < 10 else '哈哈' for i in range(100))
3 print("返回生成器对象:", g) # 输出:<generator object <genexpr> at 0x...>
4
5 # 列表推导式
6 g_list = [i if i < 10 else '哈哈' for i in range(100)]
7 print("返回列表对象:", g_list) # 输出:[0, 1, 2, ..., 9, '哈哈', '哈哈', ...]


🟢 总结
- 最后希望你编程学习上不急不躁,按照计划有条不紊推进,把任何一件事做到极致,都是不容易的,加油,努力!相信自己!
🟡 文末福利
- 最后这里免费分享给大家一份Python全套学习资料,希望能帮到那些不满现状,想提升自己却又没有方向的朋友,也可以和我一起来学习交流呀。
🔴包含编程资料、学习路线图、源代码、软件安装包等!【点击这里】领取!
- ① Python所有方向的学习路线图,清楚各个方向要学什么东西
- ② 100多节Python课程视频,涵盖必备基础、爬虫和数据分析
- ③ 100多个Python实战案例,学习不再是只会理论
- ④ 华为出品独家Python漫画教程,手机也能学习
✅ 可以扫描下方二维码领取【保证100%免费】

更多推荐



所有评论(0)