python--生成器generator

参考文章:csdn:(116条消息) Python函数学习-生成器函数_火星有星火的博客-CSDN博客

1.生成器是什么?

在Python中,生成器(generator)是一种特殊的迭代器,它是通过函数来实现的。生成器函数在执行过程中可以暂停和继续执行,可以动态地生成一系列值,而不需要一次性生成所有值,从而节省了内存空间和计算资源。

  • 可以由生成器表达式得到。
  • 可以使用yield关键字得到一个生成器函数,再通过调用这个函数得到一个生成器对象,包含yield语句的生成器函数调用生成生成器对象的时候,生成器函数的函数体不会立即执行。
  • 生成器(对象)可以使用next方法,next(generator) 会从函数的当前位置向后执行到之后碰到的第一个yield语句,会中断函数执行,并返回yield表达式的值,再次调用next函数,从上一次的中断点恢复执行,继续调用next函数,生成器函数如果结束执行了(显式或隐式调用了return语句),会抛出StopIteration异常。
  • next()方法超限会直接报错,如果不是有特别需求,一般推荐使用for循环遍历。
  • 生成器对象只能逐次遍历一遍,不能再回头读取。

2.生成器函数和普通函数的区别

生成器函数与普通函数的区别在于,生成器函数中使用了yield语句来产生值,而不是使用return语句返回值。当生成器函数被调用时,它会返回一个生成器对象,而不是直接执行函数体中的代码。当生成器对象被迭代时,生成器函数中的代码会逐行执行,直到遇到yield语句,此时会暂停执行并将yield后面的值作为迭代器的下一个值返回。下一次迭代时,生成器函数会从yield语句后面的代码继续执行,直到再次遇到yield语句,以此类推。

3.以下是一个简单的生成器函数的示例,它生成了从0开始的自然数序列:

return

numbers = natural_numbers()

难到numbers不应该等于函数natural_numbers的返回值None吗?

虽然有显式return语句(可以没有return),但是当python编译是执行到yield语句时,就会给函数natural_numbers打上生成器标签,于是调用该生成器函数natural_numbers可以返回一个生成器对象存到numbers。

def natural_numbers():
    n = 0
    while True:
        yield n
        n += 1
    return
numbers = natural_numbers()

通过迭代器的方式---next()方法依次获取序列中的值:

yield、return都不是生成器函数的返回值,只有当我对生成器对象使用nexth函数的时候,才会开始运行生成器函数本体

print(next(numbers))  # 输出:0
print(next(numbers))  # 输出:1
print(next(numbers))  # 输出:2

由于生成器对象是一种迭代器,因此可以在for循环中使用,例如:

for n in natural_numbers():
    if n > 10:
        break
    print(n)

4.生成器函数执行逻辑

  • 同一个生成器函数中yield语句可以有多个。
for i in range(5):
    yield i  #执行了5次yield语句。
  • 遍历生成器函数对象时,函数执行到yield语句会中断执行,并返回yield表达式的值。
  • 再次遍历该对象时,函数从上一个yield断点位置继续执行,并执行到下一个yield语句。
  • 重复上一步,直到函数正常结束,或者遇到return语句。
  • return语句依然可以终止函数运行,但return语句的返回值不能被获取到。
  • return会导致当前函数返回,无法继续执行。生成器对象至此已经没有可迭代深度。
  • 继续使用next()方法,抛出StopIteration异常。
  • 如果函数没有显式的return语句,如果生成器函数执行到结尾(相当于执行了return None),一样会抛出StopIteration异常。
    def gen():
        print('line 1')
        yield 1
        print('line 2')
        yield 2
        print('line 3')
        return 3
        yield 4
    

5.next()

  • 如果直接用next()方法调用生成器函数本身,那么每次都会重新生成一个生成器对象,也就无法对某一个生成器对象持续向下遍历。
    next(gen()) # line 1
    next(gen()) # line 1
    
    line 1
    1
    line 1
    1   
    

  • 因此需要使用一个变量接收gen()返回的生成器对象。
    g = gen()
    print(g)
    
    
    

  • 用next方法,逐次遍历该生成器对象,可以正常输出。
    print(next(g)) # line 1
    print(next(g)) # line 2
    

    line 1
    1
    line 2
    2
    
  • 但是如果再继续用next()遍历,解释器会报错,StopIteration ,说明遍历超限了,函数已经在 return 3 处返回,不会执行到之后的yield
    print(next(g)) # StopIteration
    
    line 3
    
    ---------------------------------------------------------------------------
    
        StopIteration                             Traceback (most recent call last)
         in 
        ----> 1 print(next(g)) # StopIteration
        
        StopIteration: 3
        
        StopIteration: 3
    

6.作用

  • 生成器可以用于处理大数据集,减少了内存的占用。此外,生成器还可以用于惰性计算,即仅在需要时才计算,从而提高程序的效率。
  • 生成器函数可以作为中断循环体的执行的隔断,每次调用生成器对象时,函数循环体才会继续运行一次。
  • 无限循环:
def counter():
	i = 0
	while True:
		i += 1
		yield i
def inc(c):
	return next(c)
c = counter()
print(inc(c))
  • 计数器:
def inc():
	def counter():
		i = 0
		while True:
			i += 1
			yield i
	c = counter()
	return lambda : next(c)
foo = inc()
print(foo()) # 1
print(foo()) # 2
print(foo()) # 3
  • 处理递归问题:原本的死循环递归,会引起解释器超过最大递归深度报错,但是插入yield语句之后,循环体被卡主了,被生成器对象调用一次才函数循环体会执行一次。
def fib():
	x = 0
	y = 1
	while True:
		yield y
		x, y = y, x+y
foo = fib()
for _ in range(5):
	print(next(foo))	

7.生成器函数语法糖-yield from

  • yield from是Python 3.3出现的新的语法
  • yield from iterable 是 for item in iterable: yield item 形式的语法糖
def inc():
	for x in range(1000):
		yield x

def inc(): 
	yield from range(1000)  ## 解释器优化的语法糖

你可能感兴趣的:(python)