Python-匿名函数、生成器函数、协程原理、yield from语法

1、匿名函数

1.1、什么是匿名函数

匿名:隐藏名字,即没有名称
匿名函数:没有名字的函数。
函数没有名字该如何定义?函数没有名字如何调用?

1.2、Lambda表达式

1.2.1、语法

使用lambda关键字定义匿名函数,格式为 lambda [参数列表]: 表达式
参数列表不需要小括号。无参就不写参数
冒号用来分割参数列表和表达式部分
不需要使用return。表达式的值,就是匿名函数的返回值。表达式中不能出现等号
lambda表达式(匿名函数)只能写在一行上,也称为单行函数

1.2.2、示例

lambda x: x ** 2 # 定义
(lambda x: x ** 2)(4) # 调用
foo = lambda x,y: (x+y) ** 2 # 定义函数
foo(1, 2)
# 等价于
def foo(x,y):
    return (x+y) ** 2

1.3、使用lambda表达式简化代码示例

1.3.1、返回常量的函数

print((lambda :0)())
print((lambda x:100)(1))

1.3.2、加法匿名函数,带缺省值

print((lambda x, y=3: x + y)(5))
print((lambda x, y=3: x + y)(5, 6))

1.3.3、keyword-only参数

print((lambda x, *, y=30: x + y)(5))
print((lambda x, *, y=30: x + y)(5, y=10))

1.3.4、可变参数

print((lambda *args: (x for x in args))(*range(5)))
print((lambda *args: [x+1 for x in args])(*range(5)))
print((lambda *args: {x%2 for x in args})(*range(5)))

1.4、应用案例

1.4.1、构建一个字典,所有key对应的值是一个列表,创建新的kv对的值也是空列表

d={c:[] for c in 'abcde'}
d['a'].append(10)
d['f']=[]
d['f'].append(20)
print(d)

1.4.2、defaultdict

from collections import defaultdict
d = defaultdict(list) # lambda : list()
d['a'].append('10') # d['a'] = list()
d['f'].append('20')
print(d)

1.4.3、sorted

x = ['a', 1, 'b', 20, 'c', 32]
print(sorted(x, key=str))
# 如果按照数字排序怎么做?
x = ['a', 1, 'b', 20, 'c', 32]
print(sorted(x, key=lambda x: x if isinstance(x, int) else int(x, 16)))

2、生成器函数

2.1、创建生成器方式

1、生成器表达式
2、生成器函数
函数体代码中包含yield语句的函数
与普通函数调用不同,生成器函数调用返回的是生成器对象

2.2、生成器表达式示例

m = (i for i in range(5))
print(type(m))
print(next(m))
print(next(m))

2.3、生成器函数示例

def inc():
    for i in range(5):
        yield i
print(type(inc))
print(type(inc())) # 生成器函数一定要调用,返回生成器对象
g = inc() # 返回新的生成器对象
print(next(g))
for x in g:
    print(x)
print('-------------------')
for x in g: # 还能迭代出元素吗?
    print(x)

2.4、生成器执行流程

2.4.1、流程说明

普通函数调用,函数会立即执行直到执行完毕。
生成器函数调用,并不会立即执行函数体,而是返回一个生成器对象,需要使用next函数来驱动这个生成器对象,或者使用循环来驱动。
生成器表达式和生成器函数都可以得到生成器对象,只不过生成器函数可以写更加复杂的逻辑。

2.4.2、代码

def gen():
    print(1)
    yield 2
    print(3)
    yield 4
    print(5)
    return 6
    yield 7
#print(next(gen())) # 1 2
# print(next(gen())) # 1 2
g = gen()
print(next(g))
print(next(g))
# print(next(g)) # return的值可以拿到吗? 报错StopIteration: 6
print(next(g, 'End')) # 没有元素不想抛异常,给个缺省值

2.4.3、代码分析

在生成器函数中,可以多次yield,每执行一次yield后会暂停执行,把yield表达式的值返回再次执行会执行到下一个yield语句又会暂停执行函数返回
return语句依然可以终止函数运行,但return语句的返回值不能被获取到
return会导致当前函数返回,无法继续执行,也无法继续获取下一个值,抛出StopIteration异常
如果函数没有显式的return语句,如果生成器函数执行到结尾(相当于执行了return None),一样会抛出StopIteration异常

2.5、生成器函数

包含yield语句的生成器函数调用后,生成生成器对象的时候,生成器函数的函数体不会立即执行
next(generator) 会从函数的当前位置向后执行到之后碰到的第一个yield语句,会弹出值,并暂停函数执行
再次调用next函数,和上一条一样的处理过程
继续调用next函数,生成器函数如果结束执行了(显式或隐式调用了return语句),会抛出StopIteration异常

2.6、应用案例

2.6.1、无限循环

def counter():
    i = 0
    while True:
        i += 1
        yield i
c = counter()
print(next(c)) # 1
print(next(c)) # 2
print(next(c)) # 3

2.6.2、计数器-非正常

def inc():
    def counter():
        i = 0
        while True:
            i += 1
            yield i
    
    c = counter()
    return next(c)
print(inc()) # 1
print(inc()) # 1
print(inc()) # 1

2.6.3、计数器-正常

def inc():
    def counter():
        i = 0
        while True:
            i += 1
            yield i
    c = counter()
    def inner():
        return next(c)
    return inner
    # return lambda : next(c)

foo = inc()
print(foo()) # 1
print(foo()) # 2
print(foo()) # 3

2.6.4、斐波那契数列

def fib():
    a=0
    b=1
    while True:
        yield b
        a,b=b,a+b
f = fib()
for i in range(1,102):
    print(i,next(f))

4、协程Coroutine

生成器的高级用法
它比进程、线程轻量级,是在用户空间调度函数的一种实现
Python3 asyncio就是协程实现,已经加入到标准库
Python3.5 使用async、await关键字直接原生支持协程
协程调度器实现思路
有2个生成器A、B
next(A)后,A执行到了yield语句暂停,然后去执行next(B),B执行到yield语句也暂停,然后再次调
用next(A),再调用next(B)在,周而复始,就实现了调度的效果
可以引入调度的策略来实现切换的方式
协程是一种非抢占式调度

5、yield from语法

5.1、简介

从Python 3.3开始增加了yield from语法,
使得 yield from iterable 等价于 for item in iterable: yield item 。

5.2、作用

yield from就是一种简化语法的语法糖。

5.3、示例

# 方式一
def inc():
    for x in range(1000):
        yield x

# 方式二:简化
def inc_m():
    yield from range(1000)# 注意这个函数出现了yield,也是生成器函数
foo = inc_m()
print(next(foo))
print(next(foo))
print(next(foo))

5.4、小结

本质上yield from的意思就是,从from后面的可迭代对象中拿元素一个个yield出去。

 

posted @ 2023-06-28 18:25  小粉优化大师  阅读(65)  评论(0)    收藏  举报