[python] 三大神器—生成器、迭代器、装饰器

本文详细介绍了Python编程中的三大神器——生成器、迭代器和装饰器。生成器用于惰性计算,避免一次性存储大量数据;迭代器是可被next()调用的对象,用于按需获取数据;装饰器则允许在不修改原函数代码的情况下,为函数添加额外功能。文章通过实例解析了它们的工作原理和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

1. 生成器

squares = [i**2 for i in range(10000)]
for i in squares:
	pass

把所有元素都存在列表里,占用了大量内存,而实际上每次只用到列表中的一个元素。

生成器:
1.惰性计算
2.边执行边计算,无需一次性存入大量数据
3.实际上一直在执行next(操作),直到无值可取

1.1 生成器表达式 (…)

圆括号

squares = (i**2 for i in range(100000))
for i in squares:
    pass

【例】求0-500的和

sum((i for i in range(501)))
## 5050
1.2 生成器函数 yield

【例】 生成斐波那契数列

1.数组迭代和存储
在这里插入图片描述
2.若有中间操作,每次只用到其中一个元素,比如每次只打印一个数字(不需要一次性使用全部元素),可以把这个中间操作放在循环里面执行
在这里插入图片描述
3.这种情况也可以构造生成器函数,方便单独进行中间操作,把print换成yield即可

当我们调用这个函数fib(),它首先会顺序执行,当执行到yield时停止,yield就会返回yield后面跟着的那个元素,相当于 return 的功能,所不同的是,执行完返回之后仍然会停在这里,直到我们执行下一次的操作 next(),继续执行剩余操作,并返回到while判断语句,符合条件则执行 yield,再次返回,并停留在这里,等待下一次操作 next()…

总结:在每次调用 next() 的时候执行,遇到 yield 语句则返回(相应元素),再次执行时从上次返回的 yield 语句处继续执行

在这里插入图片描述

1.3 调用生成器 next() / send()

[操作系统] 多任务 - 协程

2. 迭代器

2.1 可迭代对象 - 可以for循环

可直接作用于for循环的对象,统称为可迭代对象: Iterable

  • 列表,元组,字典,集合,字符串,文件
  • 生成器
1.可用 isinstance( .., Iterable) 判断可迭代对象
from collections import Iterable
isinstance([1,2,3], Iterable)
# True
isinstance("python", Iterable)
# True
2.迭代器还能被 next() 函数调用

比如生成器不仅可用于 for 循环,还可以被 next() 函数调用,直到没有数据可取,抛出 StopIteration (生成器不仅是可迭代对象,还是迭代器)

squares = (i**2 for i in range(5))
isinstance(squares, Iterable)
# True

print(next(squares))
print(next(squares))
print(next(squares))
0
1
4
2.2 迭代器 - 可被 next() 调用

可以被 next() 函数调用,并且不断返回下一个值,直到没有数据可取的对象(可耗尽),称为迭代器 Iterator

  • 生成器 是 Iterable 也是 Iterator
  • 列表,元组,字典,集合,字符串 是 Iterable 不是 Iterator
  • 文件 是 Iterable 也是 Iterator
  • zip, enumerate 等 itertools 里的函数是 Iterator
  • range() 不是 Iterator
1.可用 isinstance(... , Iterator) 判断
from collections import Iterator

squares = (i**2 for i in range(5))  ## 生成器
isinstance(squares, Iterator)
# true

with open("test.txt", "r", encoding = "utf-8") as f:
    print(isinstance(f, Iterator))    ## 文件
# True

isinstance([1,2], Iterator)   ## 列表
# False
isinstance("python", Iterator)  ## 字符串
# False
isinstance(range(5), Iterator) ## range函数
# False
x = [1, 2]
y = ["a", "b"]
for i in zip(x, y):    ## zip函数
    print(i)
# (1, 'a')
# (2, 'b')
isinstance(zip(x, y), Iterator)
# True
num = [1, 2, 3]
for i in enumerate(num): # enumerate函数
    print(i)
# (0, 1)
# (1, 2)
# (2, 3)
isinatance(enumerate(num), Iterator)
# True
2.可以通过 iter(Iterable) 创建迭代器

列表,元组,字典,集合,字符串 是 Iterable 不是 Iterator,但可以用 iter 创建 Iterator

isinstance(iter([1,2,3]), Iterator)
# True

PS:Iterator表示的是一个数据流,不断被next()调用并返回下一个数据直到StopIteration异常。但,我们不能提前知道序列长度,只能通过 next() 按需计算下一个数据,(惰性计算),而列表等类型可以计算序列长度。Iterator甚至可以表示一个无限大的数据流,但列表等显然不可能做到。

3.python的for循环本质

结合上一条,for item in Iterable 实际上先通过 iter(Iterable ) 函数创建可迭代对象的迭代器 Iterator然后对迭代器不断调用 next() 方法来获取下一个值,并将其赋值给 item,当遇到 StopIteration 的异常后 结束循环。

4.range()不是迭代器,xrange()是
nums = range(10)
isinstance(nums, Iterator)
# False
next(nums)        
# TypeError         # 不可被next()调用

【python 2】
输入 range(100) 则返回一个列表 [0, 1, 2, … ,99],如果输入 range(100000…000) 那么首先需要占用大量的内存空间来存储这个列表结果,并且在列表的生成过程中就卡死了
输入 xrange(100) 则返回一个对象(迭代器),占用极小的内存空间,存储的不是列表结果,而是生成列表的方式

【python 3 】已经把 range 改为了 xrange

2.3 生成器是一种特殊的迭代器

迭代:访问集合元素的一种方式(在原来的基础上,得到新的元素)

迭代器:一个对象,可以用很小的内存资源来访问集合元素(通过调用 next() 方法,迭代获取数据),比如 for temp in Iterable , [ xxx for xxx in iterable] 存储的不是列表结果,而是生成列表的方式。

生成器:一个函数,返回的是迭代器,能够记录当前迭代到的状态,并配合 next() 函数进行迭代,可以生成数据。

特殊在于,迭代器必须是一个类里有 iter 方法和 next() 方法,它的对象才能是一个迭代器,但是生成器不需要,只要一个函数里有 yield,那么这个函数就是生成器,可以通过 next() 函数来调用这个生成器,从而生成数据,每调用一次只生成一个值,然后暂停执行,直到下次调用。

[操作系统] 多任务 - 协程

3. 装饰器

3.1 实现需求!!!!!

【1】需要对已开发上线的程序添加某些功能
【2】不能对程序中函数的源代码进行修改
【3】不能改变程序中函数的调用方式

装饰器,就是对原函数进行【装饰】

3.2 相关知识
1. 函数是一个对象

可以将函数赋值给变量,调用变量就是调用函数

def square(x):
    return x**2
print(type(square))  # square 是 function 类的一个实例
# <class 'function'>

pow_2 = square   # 相当于给square函数起了别名
print(pow_2(5))
print(square(5))
# 25
# 25
2. 高阶函数(函数作为参数/返回值)

接收函数作为参数,或者返回函数作为返回值,满足之一称为高阶函数

def square(x):
    return x**2
def pow_2(fun):
    return fun
f = pow_2(square)   ### Not pow_2(square(8))
f(8)
# 64
3. 嵌套函数

在函数内部又定义一个函数

def outer():
    print("outer is running")
    
    def inner():
        print("inner is running")
    inner()

outer()
# outer is running
# inner is running
4. 闭包(延伸了作用域的函数)

闭包是延伸了作用域的函数
闭包 = 函数+外层环境的变量

如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包

在这里插入图片描述

5. 关于闭包的变量(nonlocal)

一旦在内层函数重新定义了相同名字的变量,则该变量成为局部变量,外层函数对其的定义和初始化全部失效(例子里就是 x = x +100 的 x 变成局部变量了,无初始值,报错)
在这里插入图片描述
可以使用 nonlocal 允许内层函数修改闭包变量,且不会变成内部变量
global的作用对象是全局变量(声明代码块中的变量使用外部全局的同名变量),nonlocal的作用对象是外层变量(使用在闭包中的,声明内层函数使用外层的同名变量
在这里插入图片描述

3.3 装饰器
1. 装饰器:高阶函数 + 嵌套函数 + 闭包

个人理解:
装饰器函数是个【嵌套函数】,新增的功能由内层函数实现。
装饰器函数也是个【高阶函数】,外层函数把原函数作为参数传入(内层函数执行时用到),外层函数的返回值是内层函数,注意返回值是 inner,不是 inner()
装饰器的内层函数是个【闭包】,执行时用到了外层的参数(原函数)
在这里插入图片描述

2. 语法糖 @

在这里插入图片描述

3. 高级装饰器 - 有参

【函数有参】可以在装饰器的内层函数引入参数(保险起见,两种参数都写上),把外层函数的参数传递给内层函数

def timer(func):
    def inner(*args, **kwargs):
        ...
        func(*args, **kwargs)
        ...
@timer
def f1(n):
    ...

f1(2) 

在这里插入图片描述
【函数有返回值】可以在装饰器的内层函数调用 原函数 的时候,将结果赋给一个变量比如 res,将 res 作为内存函数的返回值
在这里插入图片描述

【装饰器有参】装饰器本身需要一些参数,比如针对不同的函数进行装饰,比如针对不同的需求进行装饰,需要传递一些选择分支性质的参数

在这里插入图片描述

4. 一些讨论
  1. 原函数的属性被掩盖了,返回的是装饰器函数的内层函数:f1 = timer(f1),因此 f1 = inner
  2. 可以使用 @wraps 实现回归本源
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值