PEP 289 – Generator Expressions,生成器表达式

与列表推导式类似,但使用圆括号而非方括号:

# 列表推导式(立即生成列表)

squares_list = [x**2 for x in range(5)]  # [0, 1, 4, 9, 16]

for x in squares_list:
    print(x)

# 生成器表达式(返回迭代器)
squares_gen = (x**2 for x in range(5))    # <generator object ...>

for x in squares_gen:
    print(x)

如果此时执行上述代码,你会发现没什么区别。事实上生成器表达式是惰性的,也就是说,直到for循环执行最后一次,才能生成所有数字。而前面的是先生成所有数字,再执行for循环。

这种设计对异步编程来说十分方便。

注意,和迭代器一样,生成器是一次性消耗的:迭代结束后无法重复使用,需重新创建。

PEP 318 – Decorators for Functions and Methods,装饰器

如果你使用过JavaSpring框架,或者Python的一些Web框架,你应该对装饰器这种语法非常熟悉。

装饰器

菜鸟的这篇文章讲得非常好,但是有很浓的机翻味。。。https://www.runoob.com/w3cnote/python-func-decorators.html

总的来说,在Python中函数可以被赋值给变量:

def hi(name="yasoob"):
    return "hi " + name
 
print(hi())
# output: 'hi yasoob'
 
# 我们甚至可以将一个函数赋值给一个变量,比如
greet = hi
# 我们这里没有在使用小括号,因为我们并不是在调用hi函数
# 而是在将它放在greet变量里头。我们尝试运行下这个
 
print(greet())
# output: 'hi yasoob'

可以作为参数:

def hi():
    return "hi yasoob!"
 
def doSomethingBeforeHi(func):
    print("I am doing some boring work before executing hi()")
    print(func())

可以作为返回值:

def hi(name="yasoob"):
    def greet():
        return "now you are in the greet() function"
 
    def welcome():
        return "now you are in the welcome() function"
 
    if name == "yasoob":
        return greet
    else:
        return welcome
 
a = hi()
print(a)
#outputs: <function greet at 0x7f2143c01500>
 
#上面清晰地展示了`a`现在指向到hi()函数中的greet()函数
#现在试试这个
 
print(a())
#outputs: now you are in the greet() function

甚至可以在一个函数中定义另一个函数(当你需要一些临时的封装时,这样做非常有效,你不需要传递参数也可以用外部的变量):

def hi(name="yasoob"):
    print("now you are inside the hi() function")
 
    def greet():
        return "now you are in the greet() function"
 
    def welcome():
        return "now you are in the welcome() function"
 
    print(greet())
    print(welcome())
    print("now you are back in the hi() function")

装饰器的本质就是将目标函数包裹在装饰器函数里,然后在本该执行目标函数的地方,执行装饰器函数。

下面的函数,在每次执行时,将目标函数的函数名字打印出来:

def wrap_func(func):
    def wrapped():
        print(f"{func.__name__} is called")
        func()

    return wrapped


def print_func():
    print("hello world")

wrapped_func = wrap_func(print_func)
wrapped_func()

注意下面的代码不要写成wrap_func(print_func()),这样wrap_func的参数实际上是print_func的返回值,也就是None

接下来给func加入参数:

def wrap_func(func):
    def wrapped(msg):
        print(f"{func.__name__} is called")
        func(msg)

    return wrapped


def print_func(msg):
    print(f"hello world {msg}")

wrapped_func = wrap_func(print_func)
wrapped_func("msg")

使用可变参数来代替每次手动维护参数:

def wrap_func(func):
    def wrapped(*args, **kwargs):
        print(f"{func.__name__} is called")
        func(*args, **kwargs)

    return wrapped


def print_func(msg):
    print(f"hello world {msg}")

wrapped_func = wrap_func(print_func)
wrapped_func("msg")

这一步就已经几乎是一个完整的装饰器了,真正的装饰器的语法如下:

def wrap_func(func):
    def wrapped(*args, **kwargs):
        print(f"{func.__name__} is called")
        return func(*args, **kwargs)

    return wrapped


@wrap_func
def print_func(msg):
    print(f"hello world {msg}")


print_func("msg")

使用@注解,使得装饰器函数每次都会被运行。将func的返回值return回来。

带参数的装饰器实际上是一个装饰器工厂:它接受参数并返回一个装饰器函数。完整结构包含三层函数嵌套:

  1. 最外层函数:接收装饰器的参数,并返回中间层函数。
  2. 中间层函数:作为真正的装饰器,接收被装饰的函数,并返回内层函数。
  3. 内层函数:包装原函数的执行,处理参数并调用原函数。

我们继续改造之前的装饰器,让它能接收参数:

def wrap_func(msg):
    def decorator(func):
        def wrapped(*args, **kwargs):
            print(f"{func.__name__} is called. Because of {msg}")
            return func(*args, **kwargs)
        return wrapped
    return decorator


@wrap_func(msg="msg")
def print_func(msg):
    print(f"hello world {msg}")
    return msg


res = print_func("msg")
print(f"res = {res}")

输出如下:

print_func is called. Because of msg
hello world msg
res = msg

PEP 342 – Coroutines via Enhanced Generators 通过增强的生成器实现协程

想起 C++ 的协程目前还过于底层,一般只有第三方库的开发者使用,而且语法极其反人类,我就只能一声叹息。

协程本质是一个可以随时挂起,随时执行的方法。这种挂起和执行可以在之前介绍的惰性生成器的基础上来实现。不过Python现在的协程更多使用async-await来实现。

yield

yield从语句改为表达式(这意味着它需要返回一个值)。在生成器函数中,yield表达式用于暂停函数执行并返回一个值。当生成器被next()调用恢复时,yield表达式的值为None;当被send()调用恢复时,yield表达式的值为send()传入的参数。

看一个例子:

def counter(start=0):
    count = start
    while True:
        increment = yield count  
        # 暂停点
        if increment is not None:
            count += increment
        else:
            count += 1

这是一个生成器函数,但是初始化以后并不会立刻执行。需要调用一次next来激活(或者send(None)

c = counter(5)  # 从5开始计数
print(next(c))  # 输出: 5

恢复协程,(初次激活协程不可传入非None值)执行到 yield count(此时 count=5),yield充当return的角色,返回 5 并暂停。

print(next(c))

恢复协程,传入ValueNonecount自增1,执行到 yield count(此时 count=6),yield充当return的角色,返回 6 并暂停。

print(c.send(2))  # 传入2,输出: 8

恢复协程,传入Value2count自增2,执行到 yield count(此时 count=8),yield充当return的角色,返回 8 并暂停。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐