Python三大器:装饰器、迭代器、生成器

  Python的“三大器”通常指的是迭代器(Iterators)、生成器(Generators)和装饰器(Decorators)。这三种构造是Python中非常强大的功能,广泛应用于各种编程场景中,特别是在处理数据流、函数增强和代码抽象方面。

迭代器(Iterators)

  迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。迭代器有两个基本的方法:iter() 和 next()。字符串、列表或元组对象都可用于创建迭代器:

my_list = [1, 2, 3, 4]
my_iter = iter(my_list)

print(next(my_iter))  # 输出 1
print(next(my_iter))  # 输出 2

生成器(Generators)

  生成器是一种用普通函数语法定义的迭代器。它们使用yield语句每次产生一个值,暂停执行,保存执行状态,以便下次从它离开的地方继续执行。生成器提供了一种高效的方式来处理大数据集,因为它们在任何时候只需处理数据集中的一部分,而不是在开始时就加载整个数据集到内存中。

def my_generator():
    n = 1
    print('This is printed first')
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n

for item in my_generator():
    print(item)

装饰器(Decorators)

  装饰器是修改其他函数的功能的函数。它们提供了一种简单的方法来修改函数、方法或类的行为,而不需要改动它们的代码。装饰器在定义时使用@符号,紧跟在函数的定义之前。装饰器可以被认为是返回另一个函数的函数,通常用于在不修改原始函数代码的情况下增加额外的功能。

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()
动态传参
  • 使用*args处理不定数量的位置参数

当你不确定调用函数时会传递多少个参数,或者想要允许函数接受任意数量的参数时,可以使用args。在函数定义中,参数前的星号表示将所有位置参数(那些没有被命名的参数)收集到一个名为args的元组中。

def print_args(*args):
    for arg in args:
        print(arg)

print_args('one', 'two', 'three')
  • 使用**kwargs处理不定数量的关键字参数

与*args类似,**kwargs允许你处理那些在函数调用时提供的不定数量的关键字参数。这些关键字参数被打包进一个字典中,其中参数名是字典的键,参数值是字典的值。

def print_kwargs(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_kwargs(first='one', second='two', third='three')
  • 组合使用*args和**kwargs

你可以在同一个函数定义中同时使用args和**kwargs来允许它接受任意数量的位置参数和关键字参数。
注意,当同时使用
args和kwargs时,*args必须在kwargs之前。

def print_all_args(*args, **kwargs):
    for arg in args:
        print(arg)
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_all_args('one', 'two', third='three', fourth='four')
  • 函数调用时使用*

同样地,当调用函数时,你也可以使用*和**语法来展开序列或字典,作为参数传递给函数。

args = [1, 2, 3]
kwargs = {'a': 4, 'b': 5}
def func(*args, **kwargs):
    print(args)
    print(kwargs)

func(*args, **kwargs)
闭包
  • 闭包的创建涉及三个关键点:
    1. 必须有一个嵌套函数(函数内部定义的函数)。
    2. 嵌套函数必须引用其外部作用域中的变量。
    3. 外部函数必须返回嵌套函数。
通用装饰器写法
  • 解决被装饰函数存在参数与返回值的问题
# 装饰器
def wrapper(fn):
    def inner(*args, **kwargs):
        """在执行目标函数之前"""
        ret = fn(*args, **kwargs) # 处理目标函数的返回值
        """在执行目标函数之后"""
        return ret
    return inner

# 目标函数
@wrapper
def target():
    pass

# 执行
target()
同一个函数有多个装饰器
  • 装饰器
def decorator1(func):
    def wrapper():
        print("Decorator1 before function execution")
        func()
        print("Decorator1 after function execution")
    return wrapper

def decorator2(func):
    def wrapper():
        print("Decorator2 before function execution")
        func()
        print("Decorator2 after function execution")
    return wrapper
  • 使用装饰器
@decorator1
@decorator2
def say_hello():
    print("Hello!")
  • 调用&&输出
say_hello()

Decorator1 before function execution
Decorator2 before function execution
Hello!
Decorator2 after function execution
Decorator1 after function execution
  • 装饰器执行顺序

  理解装饰器的执行顺序非常重要,特别是当装饰器影响函数的执行方式时。装饰器的执行顺序遵循“先进后出”的规则,可以想象成一个栈结构,最后应用的装饰器会首先执行其额外的逻辑。

参数化装饰器

  当装饰器接受参数时,需要添加另一层函数来处理这些参数。这会稍微复杂一些,但原理相同。每个装饰器依然是从下到上应用,只不过每个装饰器本身可能会因为参数而有更复杂的行为。

  在这个例子中,say_goodbye函数首先被repeat装饰器修饰,repeat装饰器接受一个参数times,然后是decorator1。这展示了即便装饰器接受参数,装饰器的应用顺序和行为规则依然不变。

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@decorator1
@repeat(2)
def say_goodbye():
    print("Goodbye!")
def gua_outer(name):
    def gua(fn):
        def inner(*args, **kwargs):
            print(f"开启{name}外挂")
            ret = fn(*args, **kwargs)
            print("关闭外挂")
            return ret
        return inner
    return gua

@gua_outer("锁血")  # 先执行函数的调用,函数返回一个装饰器,和@组合成语法糖
def dnf():
    print('我要打LOL')
    
@gua_outer("自瞄")
def cs_go():
    print("我要打CSGO")

评论