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

时间:2017-10-07 13:15:14

标签: python decorator

我一直很难真正了解Python的一些高级概念。我只是觉得我终于理解了装饰师并撞到了另一面墙。

据我了解,装饰者的核心事实是以下两件事完全相同:

@function_generator
def original_function:
    ...

original_function = function_generator(original_function)

也就是说正在做的是function_generator被调用并传递original_function,然后function_generator回到另一个函数,然后给出原始函数的名称,所以当代码调用original_function时,真正执行的是函数生成器返回的代码。

但后来我尝试了这个:

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()

此代码工作正常,完全符合我的预期。但是当我取消注释@decorator并注释掉print_name = decorator(print_name, iterate)时,它会崩溃。

我运行此代码测试的原因是因为我看到了一个装饰器的高级示例,他们说我们必须将装饰器包装在另一个函数中以传入参数(如iterate)。但是,在我看来,如果这是真的,那么@func2仅仅是将func1=func2(func1)打成谎言的句法糖。

以下哪项是正确的:

  1. 键入func1=func2(func1)@func2的{​​{1}}并不完全相同,因为如果是def,那么func1是否有效然后func1=func2(func1,arg)作为装饰者会工作;或

  2. 它们是相同的,即使从上面的例子看起来恰恰相反 - 如果是这样,为什么它看起来恰恰相反?

  3. 这就是我现在挂断的东西。为什么有这么多人说@func2只是另一种写@decorator的方法,当你在混合中添加一个参数时它不会那样工作?

2 个答案:

答案 0 :(得分:0)

那是因为实际上你还是不太懂装饰师。如果你想将参数传递给装饰器,装饰器应该像这样嵌套,参数在入口级别,并且func在内层传递。

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()

您可以取消注释装饰器并按照您的意愿进行尝试。

答案 1 :(得分:0)

您可以这样做的一种方法是创建一个装饰工厂:一个返回装饰器的函数。调用它的语法看起来像带有参数列表的装饰器。这是您的代码的修改版本:

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()

<强>演示

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

如果我们重写这段代码的最后一部分,可能有助于理解发生了什么:

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

所以decorator_factory(iterates)返回dec,然后dec用于装饰print_name