Python第三弹 Python进阶 装饰器和迭代器
一、装饰器
装饰器(decorator)是 Python 中的一种高级功能,用于在不修改原函数代码的前提下,动态扩展函数或类的功能。
本质上,装饰器是一个函数:它接收一个函数作为参数,并返回一个新的函数(通常是对原函数的增强版本)。
装饰器通过 @decorator_name 语法应用在函数或方法定义之前。
Python 还提供了一些内置装饰器,例如 @staticmethod 和 @classmethod。
常见应用场景:
- 日志记录:记录函数调用信息、参数和返回值
- 性能统计:统计函数执行时间
- 权限控制:限制函数访问权限
- 缓存:缓存函数结果,提高性能
1.1 基本语法
装饰器的核心思想是:用一个函数"包装"另一个函数。
def decorator_function(original_function):
def wrapper(*args, **kwargs):
# 调用前
print("执行前")
result = original_function(*args, **kwargs)
# 调用后
print("执行后")
return result
return wrapper
@decorator_function
def target_function():
print("原函数执行")
解析:
decorator_function:装饰器函数(接收原函数)wrapper:包装函数(真正被执行)@decorator_function:等价于函数替换
等价写法:
target_function = decorator_function(target_function)
1.2 使用装饰器
装饰器通过 @ 语法糖应用在函数定义前:
@time_logger
def target_function():
pass
等价于:
def target_function():
pass
target_function = time_logger(target_function)
这种机制使我们可以在不修改原函数的情况下,统一添加功能(如日志、权限等)。
1.2.1 没有返回值的函数的装饰器
打印日志功能
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
1.2.2 带有返回值的函数的装饰器
带有返回值的函数的装饰器,需要通过 functools 返回函数包装。
# 修改方法:
import functools
def exec_log(fun):
@functools.wraps(fun) # 保留原函数的元信息(如名称、文档字符串等)
def wrapper(*args, **kwargs):
print("开始执行函数")
ret = fun()
print(f"函数执行完成 ret={ret}")
# 原函数有返回,则装饰器函数必须有返回
# 否则报错:TypeError: 'NoneType' object is not callable
return ret
return wrapper # 返回包装函数,而不是执行结果
@exec_log
def exec_me(): # 建议避免使用 exec 作为函数名,因为它是 Python 内置关键字
print("Welcome to China!")
return 200
# 调用原函数
r = exec_me()
print(r)
输出内容:
开始执行函数
Welcome to China!
函数执行完成 ret=200
200
1.2.3 带参数的装饰器
如果原函数有参数,需要在 wrapper 中使用 *args, **kwargs:
如果原函数有参数,需要在 wrapper 中使用 *args, **kwargs:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("执行前")
func(*args, **kwargs)
print("执行后")
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
输出:
执行前
Hello, Alice!
执行后
说明:使用 *args, **kwargs 可以兼容任意参数函数。
进阶:装饰器执行重复打印3次,
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
# 输出:
# Hello! 悟空
# Hello! 悟空
# Hello! 悟空
说明:这是"装饰器工厂",外层函数用于接收参数。

1.3 类装饰器
单例模式
class SingletonDecorator:
def __init__(self, cls):
self.cls = cls
self.instance = None
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
@SingletonDecorator
class Database:
def __init__(self):
print("初始化")
db1 = Database()
db2 = Database()
print(db1 is db2)
输出:
初始化
True
1.4 内置装饰器
常用内置装饰器:
- @staticmethod:定义静态方法
- @classmethod:定义类方法
- @property:将方法变为属性
class MyClass:
@staticmethod
def static_method():
print("静态方法")
@classmethod
def class_method(cls):
print(cls.__name__)
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
1.5 多个装饰器
函数采用多个装饰器的话,从下到上依次执行。
# 1.4 多个装饰器的堆叠
# 多个装饰器从下到上依次执行:
def decorator1(func):
def wrapper():
print("Decorator 1")
func()
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2")
func()
return wrapper
# 两个装饰器,先执行第一个装饰器,打印:"Decorator 1"
@decorator1
# 再执行第二个装饰器,打印:"Decorator 2";然后执行原函数,打印:"Hello!"
@decorator2
def say_hello():
print("Hello!")
# 输出:
# Decorator 1
# Decorator 2
# Hello!
say_hello()
核心总结
装饰器 = 函数包装函数 + 不修改原代码扩展功能
- @ 语法本质是函数替换
- wrapper 才是真正执行的函数
- 推荐使用 *args, **kwargs 提高通用性
- 支持函数、类、甚至带参数的装饰器
二、迭代器
迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。
字符串,列表或元组对象都可用于创建迭代器:
2.1 迭代器
语法:
for 变量 in 可迭代:
pass
iterable: 可迭代的东西,例如:字符串、数组、元组、集合Set、列表、字典、File
for循环迭代:
# 字符串的迭代
for c in "我是中国人,我爱中国":
print(c)
2.2 创建迭代器
迭代器的创建有两种方式。
方式一:从可迭代对象获取迭代器 __iter__() 方法
list = [1,2,3,4,5]
# 1)从可迭代对象获取迭代器 __iter__() 方法
it = list.__iter__()
print(it)
# 输出:1
print(it.__next__())
# 输出:2
print(it.__next__())
# 输出:3
print(it.__next__())
方式二:iter(可迭代)创建迭代器
采用iter函数创建迭代器。迭代器是可迭代的,可以使用for遍历。
# 2)通过 iter(可迭代) 创建迭代器
print("通过iter创建迭代器")
iter = iter(list)
for item in iter:
print(item)
print("迭代器遍历完成")
总结:
两种获取迭代器的方法:
1.iter内置函数可以获取迭代器
2.可迭代对象的__iter__()方法可以获取迭代器。特殊方法。
for里面一定可以拿到迭代器的。所以所有不可迭代的东西不能使用for循环遍历。
迭代器统一了所有不同数据类型的遍历工作。
2.3 采用next获取迭代器元素
list = [1,2,3,4,5]
it2 = iter(list)
while True:
print (next(it2))
next遍历迭代器后,遍历完成,会报错StopIteration。
因为迭代器没有提供是否遍历完成的函数,next怎么安全遍历呢?
可以通过 try-catch捕捉StopIteration异常
list = [1,2,3,4,5]
it2 = iter(list)
while True:
try:
print (next(it2))
except StopIteration:
print("next遍历完成")
2.3.1 StopIteration
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
在 20 次迭代后停止执行:
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
迭代器本身的特性:
1.只能往前不能反复
2.特别节省内存
3.惰性机制
三、生成器
生成器本质就是迭代器。
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
yield 是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。
调用一个生成器函数,返回的是一个迭代器对象。
def countdown(n):
while n > 0:
yield n
n -= 1
# 创建生成器对象
generator = countdown(5)
# 生成器不会直接返回结果
# 输出:<generator object countdown at 0x10450bf40>
print(generator)
# 通过迭代生成器获取值
print(next(generator)) # 输出: 5
print(next(generator)) # 输出: 4
print(next(generator)) # 输出: 3
# 使用 for 循环迭代生成器
for value in generator:
print(value) # 输出: 2 1
3.1.创建生成器
创建生成器有两种方法:
1)生成器函数
2)生成器表达式
3.1.1 生成器函数
生成器函数:生成器函数中有一个关键字 yield.
# 1.1 生成器函数
def fun():
print(1234567)
# yield返回结果
yield 999
# 生成器函数返回的是迭代器
ret = fun()
# 可以采用迭代器next获取结果
# 只有执行next时候,生成器的函数体才会执行。
print(ret.__next__())
3.1.2 yield可以分段返回
例如:fun2分2段返回结果,第一段返回999,第二段返回888
# yield可以分段返回
def fun2():
print(1234567)
# yield返回结果
yield 999 # yield也有返回的意思。return立即返回结果,yield只有执行到next的时候才会返回。
# 第二段返回
print(45678)
yield 888
ret = fun2()
print("第一段返回结果")
# 第一段返回结果:
# 输出:
# 1234567
# 999
print(ret.__next__())
print()
print("第二段返回结果")
# 第二段返回结果:
# 输出:
# 45678
# 888
print(ret.__next__())
3.2 生成器的读取
生成器的遍历和读取,和迭代器类似。
3.2.1 生成器分段返回
生成器结果分段返回,有什么使用场景呢?
例如:制作10000件衣服,分批进行制作,制作完一批,返回一批。继续制作下一批。
四、推导式
推导式目的主要是简化代码。
4.1 列表推导式
语法:
列表推导式:[数据 for循环 if判断]
# 1.列表推导式
# 1)将1~10添加到列表中
list = [i for i in range(1,10)]
# 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9]
print(list)
# 2)添加20件衣服,衣服名称命名为”衣服i“
list = [f"衣服{i}" for i in range(1,10)]
# 输出:['衣服1', '衣服2', '衣服3', '衣服4', '衣服5', '衣服6', '衣服7', '衣服8', '衣服9']
print(list)
4.2 集合推导式
语法:
集合推导式:{数据 for循环 if判断}
# 2.集合推导式
# 将1~10添加到集合中
set = {i for i in range(1,10,2)}
# 输出:{1, 3, 5, 7, 9}
print(set)
4.3 字典推导式
语法:
字典推导式:{key:value for循环 if判断}
# 3.字典推导式
# 将列表中的索引作为键,元素作为值,生成字典
list = ["白龙马","唐僧", "白晶晶", "铁扇公主"]
# 字典推导式
dict = {i:list[i] for i in range(len(list))}
# 输出:{0: '白龙马', 1: '唐僧', 2: '白晶晶', 3: '铁扇公主'}
print(dict)
五、生成器表达式
4.生成器表达式
语法:
(数据 for循环 if判断)
# 生成1~10的平方的列表
# 创建生成器表达式
gen = (i**2 for i in range(1,10,2))
# 生成器列表的数据打印
for item in gen:
print(item)
# 输出:
# 1
# 9
# 25
# 49
# 81
# 生成1~10的平方的列表生成的结果作为列表
lst = list(gen)
# 输出:[]
print(lst)
# 为什么是空列表呢?
# 因为生成器表达式是一次性的,在for item in gen:已经遍历完成,因此剩余的内容遍历是一个空列表
因为生成器表达式是一次性的,在for item in gen:已经遍历完成,因此剩余的内容遍历是一个空列表。
更多推荐
所有评论(0)