Python函数之三:迭代器和生成器
一、函数名的运用
函数名是一个特殊的变量,他除了具有变量的功能,还有最主要一个特点就是加上() 就执行,其实他还有一个学名叫第一类对象。
1、指向内存地址
def func():
print('这是一个函数')
print(func)#<function func at 0x000002B9AC36D1E0>
函数名指向了内存地址:0x000002B9AC36D1E0
2、当做变量传递
def func():
print('这是一个函数')
a = func
print(a is func)#True
函数名当做变量赋值给了a变量,他们指向同一个内存地址
3、当做容器类的元素
用容器类元素:
a = "in func1: 嘻嘻"
b = "in func2: 哈哈"
c = "in func3: 咯咯"
d = "in func4: 吱吱"
lst = [a, b, c, d]
for i in lst:
print(i)
用函数名:
def func1():
print("in func1: 嘻嘻")
def func2():
print("in func2: 哈哈")
def func3():
print("in func3: 咯咯")
def func4():
print("in func4: 吱吱")
lst = [func1, func2, func3, func4]
for i in lst:
i()
4、当做函数的参数和返回值
前文的文章总结过,即:高阶函数
二、f-strings格式化输出
f-strings 是python3.6开始加入标准库的格式化输出新的写法,这个格式化输出比之前的%s 或者 format 效率高并且更加简化
1、结构
他的结构就是F(f)+ str的形式,在字符串中想替换的位置用{}展位,与format类似,但是用在字符串后面写入替换的内容,而他可以直接识别。
name = '晴朗'
age = 18
sex = '男'
print(f'我叫{name},今年{age}岁,我是{sex}生!')
# 我叫晴朗,今年18岁,我是男生!
2、使用方法
大括号{}内可以插入任意表达式:
#整数
print(f'{3 * 8}') # 24
#字符串
name = 'abcde'
print(f"全部大写:{name.upper()}") # 全部大写:ABCDE
# 字典
person = {'name': '晴朗', 'age': 18}
msg = f"I am {person['name']}, age {person['age']}"
print(msg) # I am 晴朗, age 18
# 列表
l1 = ['晴朗', 18]
msg = f'姓名:{l1[0]},年龄:{l1[1]}.'
print(msg) # 姓名:晴朗,年龄:18.
#函数
def sum(a, b):
return a + b
msg = f'3和5的和是:{sum(3, 5)}'
print(msg)#3和5的和是:8
三、迭代器
1、可迭代对象
在python中,但凡内部含有__iter__方法的对象,都是可迭代对象。
例如:
st = 'abcde'
li = [1, 2, 3]
tu = (1, 5, 9)
m = 55
print('__iter__' in dir(st))#True
print('__iter__' in dir(li))#True
print('__iter__' in dir(tu))#True
print('__iter__' in dir(m))#False
结论:字符串、列表、元组等都是可迭代对象,而整数不是可迭代对象
2、迭代器的简介
1、迭代是Python最强大的功能之一,是访问集合元素的一种方式。
2、迭代器是一个可以记住遍历的位置的对象。
3、迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
4、迭代器有两个基本的方法:iter() 和 next()。
li = [1, 2, 3, 8, 12]
it = iter(li)#创建迭代器对象
for i in it:
print(i)
2、判断对象是否迭代器
通过判断iter() 和 next()方法是否属于被判断对象方法列表中,从而判断对象是否迭代器
if ('__iter__' in dir(判断对象)) and ('__next__' in dir(判断对象)):
print('判断对象是迭代器')
else:
print('判断对象不是迭代器')
li = [1, 2, 3, 8, 12]
print('__iter__' in dir(li))#True
print('__next__' in dir(li))#False
结论:li不是迭代器,是迭代对象
3、迭代对象转换成迭代对象
使用iter() 方法直接转换:
l1 = [1, 2, 3, 4, 5, 6]
obj = iter(l1)
if ('__iter__' in dir(obj)) and ('__next__' in dir(obj)):
print('判断对象是迭代器')
else:
print('判断对象不是迭代器')
#判断对象是迭代器
4、迭代器的取值
迭代器是利用__next__()进行取值:
l1 = [1, 2, 3, 4, 5, 6]
obj = iter(l1)
print(obj.__next__())#1
print(obj.__next__())#2
print(obj.__next__())#3
print(obj.__next__())#4
print(obj.__next__())#5
5、 while模拟for的循环原理
循环原理:
1.先调用in后对象的__iter__方法,将其变成一个迭代器对象
2.调用next(迭代器),将得到的返回值赋值给变量名
3.循环往复直到next(迭代器)抛出异常,for会自动捕捉异常然后结束循环
6、迭代器的优缺点
1、优点
1、提供了一种通用不依赖索引的迭代取值方式
2、同一时刻在内存中只存在一个值,更节省内存
2、缺点
1、取值不如按照索引的方式灵活,不能取指定的某一个值,只能往后取,不能往前去
2、无法预测迭代器的长度
三、生成器
1、生成器简介
1、生成器就是一种自定义的迭代器,本质为迭代器
2、在 Python 中,使用了 yield 的函数被称为生成器(generator)
3、跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器
4、在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行
5、调用一个生成器函数,返回的是一个迭代器对象
def func():
print(1)
yield 2
res = func()
print(res)#<generator object func at 0x000002597F606B10>
print(res.__next__())#1 2
以上代码中,func就是一个生成器,调用func()时,获取的生成器对象(其实就是一个迭代器),通过next方法取值
2、创建生成器
2.1生成器的结构
def func():
...
yield ...
...
yield ...
...
2.2 yield与return的区别
1、yield 的作用:
1.1、yield提供了一种自定义迭代器的解决方案
1.2、yield可以保存函数的暂停的状态
2、两者区别:
相同点:都可以返回值,值得类型与个数没有限制
不同点:yield可以返回多次值,而return只能返回一次值函数就会结束;yield可以有多个,而return一般只有一个
2.3 生成器的优点
例:假设要小明向自行车厂定制了一千辆自行车给员工发放福利,自行车厂boss一口气全部生产出来了,后来小明只发放了200辆
一般代码实现发放:
for i in range(1, 1001):
print(i)
迭代器实现发放:
def func():
for i in range(1, 1001):
yield '自行车'+str(i)
by = func()
for i in range(1, 201):
print(next(by))
通过以上两种方式得出结论:
1、第一种是直接把自行车全部生产出来,占用内存。
2、第二种是发放一辆,生产一辆,非常的节省内存,而且还可以保留上次的发放位置。
2.4 send方法:主要是用于外部与生成器对象的交互
def func1(): # 生成器函数
x = yield 1
yield x
yield x
res = func1()
print(next(res))#1
m = res.send('888')
print(m)#888
print(next(res))#888
send与next的异同:
相同点:send 和 next()都可以让生成器对应的yield向下执行一次,都可以获取到yield生成的值。
不同点: 第一次获取yield值只能用next不能用send(可以用send(None)), send可以给上一个yield置传递值。
2.5 yield from
yield会将可迭代对象当做一个整体传出来,而yield from则是将可迭代对象中的每一个元素传出来
li = [1, 2, 5, 6, 4, 3, 9]
def func1():
yield li
def func2():
yield from li
res1 = func1()
print(next(res1))#[1, 2, 5, 6, 4, 3, 9]
res2 = func2()
print(next(res2))#1
print(next(res2))#2
print(next(res2))#5
print(next(res2))#6
print(next(res2))#4
注意:如果要访问两个可迭代对象的数据,则可以使用两个yield from
li = [1, 2, 5, 6, 4, 3, 9]
tu = ('a', 'b', 'm')
def func2():
yield from li
yield from tu
res2 = func2()
for i in res2:
print(i)
#1, 2, 5, 6, 4, 3, 9, a, b, m