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. 一些讨论
- 原函数的属性被掩盖了,返回的是装饰器函数的内层函数:f1 = timer(f1),因此 f1 = inner
- 可以使用 @wraps 实现回归本源