引出:迭代取值和索引取值的对比
迭代取值提供了一种不依赖于索引取值的新的方式。
-
迭代取值:
- 不依赖索引取值,通过调用
__next__()
方法取值。 - 不能重复取值,只能从左往右挨个取值。若要取同一个值,只能重新初始化迭代器对象,再调用
__next__()
方法。
- 不依赖索引取值,通过调用
-
索引取值:
- 取值的对象必须是一个容器类型,例如列表、元组等
- 可以重复取值,即一个值可以取多次,随时取。
生成器与yield关键字
生成器就是自定义迭代器,本质上就是迭代器。如果函数体内包含yield关键字,那么这个函数在调用的时候并不会执行函数体代码,而返回的结果是一个生成器对象。生成器内置有__iter__()
方法和__next__()
方法,通过生成器对象调用__next__()
方法,就会触发函数体代码的执行。
yield关键字可以用于返回值,但不同于return关键字,函数遇到return就直接结束,而yield可以保持函数运行状态“暂停”函数,用来返回多次值。
代码演示:
def index():
print('from index')
yield
index()
print(index())
index().__next__()
print(index().__next__())
执行结果:
结论:函数体包含yield关键字,调用时不执行代码,返回结果为生成器对象;生成器对象调用__next__()
方法,执行函数体,并将yield后面的值返回,若不写,默认返回None。
def index():
print('from index')
yield 123,234,345
res = index()
print(res.__next__())
# print(next(res)) # 与上一句代码作用一致
执行结果:
总结:yield后面可以返回值,并且可以同时返回多个,以元组形式返回。
def index():
print('第一次next')
yield 123,234,345
print('第二次next')
yield 'aaaa'
print('第三次next')
yield 'xxxx'
res=index() # 一旦是生成器,就可以__next__取值了
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(next(res)) # StopIteration
总结:生成器对象调用一次__next__()
方法,走一个yield,光标就停在了yield,如果再调用__next__()
,会从上一次停留的yield位置继续往下走,如果后面有,则执行,如果没有,则抛出异常。
注意:函数里面有yield关键字之后,函数调用之前还是函数,一旦调用函数变成了生成器
生成器应用实例:生成器实现range功能
-
range功能:
1. 功能一: range(10) # 0-9的数字 2. 功能二: range(3,10) # 3-9的数字 3. 功能三: range(1, 10, 2) # 1,3,5,7,9
-
利用生成器实现range功能:
def my_range(start, end=None, step=1): if not end: end = start start = 0 while start < end: yield start start += step # res = my_range(2, 10, 2) res = my_range(10) for i in res: print(i)
yield传值
有了yield关键字,可以拿到函数的生成器对象持续为函数体send值。
示例:
def index(name):
print('%s 准备干饭' % name)
while True:
food = yield
print('%s 开干 %s' % (name, food))
res=index('靓仔')
res.__next__()
res.__next__()
res.send('手扒饭')
res.send('盖浇饭')
执行结果:
yield和return的对比
-
yield:
- 函数内遇到yield关键字,在调用时把函数变成了生成器
- yield 也可以返回值,并且支持返回多个(元组形式)
- 遇到yield,函数不会结束,而是"暂停"
- yield 也支持传值(利用send方法)
-
return:
- return后面可以跟返回值,也支持返回多个(元组形式)
- 函数遇到return,会立即结束
生成器表达式
我们从列表生成式,来引出生成表达式,如下代码演示:
-
列表生成式:
l = [1,2,3,4] new_l = [i + 1 for i in l] print(new_l)
执行结果:[2, 3, 4, 5]
总结:列表生成式的返回值是列表
-
生成器表达式:
l = [1,2,3,4] new_l = (i + 1 for i in l) print(new_l) print(next(new_l))
执行结果:
总结:生成器表达式返回一个生成器对象,若要取值,需要通过
next()
来产生,生成器表达式就类似于一个“工厂”,当有需求时,"工厂"才会为你加工“产品”。
生成器笔试题
题目:写出下列程序的执行结果。
def add(n, i):
return n + i
def test():
for i in range(4):
yield i
g = test()
for n in [1, 10]:
g = (add(n, i) for i in g)
res = list(g)
print(res)
执行结果:[20, 21, 22, 23]
过程分析:
# 求和
def add(n, i):
return n + i
# 调用之前是函数 调用之后是生成器
def test():
for i in range(4):
yield i
g = test() # 初始化生成器对象
for n in [1, 10]:
g = (add(n, i) for i in g)
'''
第一次循环:
g = (add(1, i) for i in g)
第二次循环:
g = (add(10, i) for i in (add(10, i) for i in g))
'''
res = list(g)
print(res)
常见内置函数补充
abs()
:求绝对值。
print(abs(123))
print(abs(-123))
# 执行结果:123 123
all()
:只有列表中元素全为真,才返回True,否则返回False。
l1 = [1,2,3,4,5,6]
l2 = [1,2,3,0]
print(all(l1))
print(all(l2))
# 执行结果:True False
any()
:只要列表中元素有一个为真,就返回True。
l1 = [1,2,3,4,5,6]
l2 = [1,2,3,0]
l3 = [0, None,[]]
print(any(l1))
print(any(l2))
print(any(l3))
# 执行结果:True True False
sum()
:求和,注意传入的对象必须是可迭代对象。
l = [1,2,3,4,5,6]
print(sum(l))
# 执行结果:21
divmod()
:除余,第一个参数为被除数,第二个参数为除数,返回结果为(商值,余数)。
print(divmod(100,10))
print(divmod(101,10))
print(divmod(99,10))
# 执行结果:(10, 0) (10, 1) (9, 9)
应用:分页器的设计。
eval()
、exec()
:使字符串中的代码执行,注意字符串中的代码不能缩进,否则报错。
string1 = """
print("hello world")
"""
eval(string1)
eval(string1)
# 执行结果:
hello world
hello world
isinstance()
:判断某个数据是否为某个实例。
s = "hello world"
print(isinstance(s, int))
print(isinstance(s, str))
# 执行结果:
False
True
chr()
:传入十进制数,返回十进制数对应的ASCII码字符。
print(chr(65))
# 执行结果:
A
ord()
:传入ASCII码字符,返回对应的十进制数字。
print(ord('A'))
# 执行结果:
65
pow()
:参数一为底数,参数二位幂次,返回其结果。
print(pow(2,3))
print(pow(3,3))
# 执行结果:
8
27
round()
:小数点后到整数五舍六入。
print(round(4.3))
print(round(4.5))
print(round(4.8))
# 执行结果:
4
4
5
bytes()
:转二进制。
s = 'hello world'
print(bytes(s, 'utf8'))
# 执行结果:
b'hello world'