简体   繁体   English

对Python装饰器和“语法糖”感到困惑

[英]Confused about Python decorators and “syntactic sugar”

I have always had trouble really understanding some of Python's advanced concepts. 我一直很难真正理解Python的一些高级概念。 I just thought I finally understood decorators and ran into another wall. 我只是以为我终于了解了装饰师并碰到了另一堵墙。

As I understand, the core truth to decorators is that the following two things are exactly the same: 据我了解,装饰器的核心真理是以下两件事完全相同:

@function_generator
def original_function:
    ...

and

original_function = function_generator(original_function)

which is to say that what is being done is that function_generator is being called and passed the original_function , then function_generator hands back a different function which then is given the original function's name, so that when the code calls the original_function , what really gets executed is the code the function generator returns. 也就是说,正在执行的操作是调用function_generator并传递了original_function ,然后function_generator递回另一个函数,然后为其指定原始函数的名称,以便当代码调用original_function ,真正执行的是函数生成器返回的代码。

But then I tried this: 但是后来我尝试了这个:

def decorator(passed_func, times):
    """the decorator"""
    def replacement():
        """the replacement"""
        for i in range(times):
            passed_func()
    return replacement

#@decorator
def print_name():
    """prints my name"""
    print("My name is Benn")

iterate = int(input("How many times?"))
print_name = decorator(print_name, iterate)
print_name()

This code works fine and does exactly what I expect. 这段代码可以正常工作,并且完全符合我的期望。 But when I uncomment the @decorator and comment out print_name = decorator(print_name, iterate) , it crashes. 但是,当我取消注释@decorator并注释掉print_name = decorator(print_name, iterate) ,它崩溃。

The reason I ran this code test is because I was seeing an advanced example of decorators where they said we have to wrap the decorator in a whole other function to pass in arguments (like iterate ). 我运行此代码测试的原因是因为我看到了一个装饰器的高级示例,其中他们说我们必须将装饰器包装在其他函数中才能传递参数(例如iterate )。 However, it seems to me that if that is true it makes the statement that @func2 is merely syntactic sugar for typing func1=func2(func1) into a lie. 但是,在我看来,如果这是真的,那么它就会声明@func2仅仅是将func1=func2(func1)键入谎言中的语法糖。

So which of the following is true: 因此,以下哪一项是正确的:

  1. typing func1=func2(func1) is NOT exactly the same as @func2 over the def for func1 , because if it was, then if func1=func2(func1,arg) works then @func2 as a decorator would work; 打字func1=func2(func1)是不完全一样的@func2deffunc1 ,因为如果是,那么如果func1=func2(func1,arg)的作品,然后@func2作为装饰会工作; or 要么

  2. they are the same even though from the above example it seems exactly the opposite – if so, why does it seem the opposite? 即使在上述示例中看起来完全相反,它们也是相同的–如果是这样, 为什么看起来相反?

That's what I am hung up on right now. 这就是我现在挂断的电话。 Why do so many people say that @decorator is nothing but another way to write func1=decorator(func1) when it does not work that way when you add an argument in to the mix? 为什么这么多人说@decorator只是写func1=decorator(func1)另一种方式,而当您在混合中添加参数时,它却无法正常工作吗?

That's because actually you still didn't understand decorator well. 那是因为实际上您仍然不太了解装饰器。 If you want to pass parameters to a decorator, the decorator should nested like this, with the parameter at the entry level, and the func is passed in the inner level. 如果要将参数传递给装饰器,则装饰器应像这样嵌套,将参数置于入口级别,并且将函数传递到内部级别。

def decorator(times):
    def wrapper(passed_func):
        """the wrapper"""

        def replacement():
            """the replacement"""
            for i in range(times):
                passed_func()

        return replacement

    return wrapper


# @decorator(3)
def print_name():
    """prints my name"""
    print("My name is Benn")


iterate = int(input("How many times?"))
print_name = decorator(iterate)(print_name)
print_name()

You can uncomment the decorator and try as you wish. 您可以取消注释装饰器,然后根据需要尝试。

One way you can do this is to create a decorator factory: a function that returns a decorator. 一种实现方法是创建装饰器工厂:一个返回装饰器的函数。 The syntax for calling it looks like a decorator with an argument list. 调用它的语法看起来像带有参数列表的装饰器。 Here's a modified version of your code: 这是您代码的修改版本:

def decorator_factory(times):
    def decorator(passed_func):
        """the decorator"""
        def replacement():
            """the replacement"""
            for i in range(times):
                passed_func()
        return replacement
    return decorator

iterates = int(input("How many times? "))

@decorator_factory(iterates)
def print_name():
    """prints my name"""
    print("My name is Benn")

print_name()

demo 演示

How many times? 4
My name is Benn
My name is Benn
My name is Benn
My name is Benn

It may help to understand what's happening if we re-write the last part of that code like this: 如果我们这样重写代码的最后一部分,可能有助于了解正在发生的情况:

dec = decorator_factory(iterates)
@dec
def print_name():
    """prints my name"""
    print("My name is Benn")

So decorator_factory(iterates) returns dec , and then dec is used to decorate print_name . 因此decorator_factory(iterates) print_name decorator_factory(iterates)返回dec ,然后使用dec来装饰print_name

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM