【问题标题】:Confused about Python decorators and “syntactic sugar”对 Python 装饰器和“语法糖”感到困惑
【发布时间】:2017-10-07 13:15:14
【问题描述】:

我一直难以真正理解 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. def 上键入func1=func2(func1)@func2func1 并不完全相同,因为如果是这样,那么如果func1=func2(func1,arg) 有效,那么@func2 作为装饰器就可以了;或

  2. 它们是相同的,尽管从上面的示例中看起来完全相反——如果是这样,为什么看起来相反?

这就是我现在所关注的。为什么这么多人说@decorator 只不过是写func1=decorator(func1) 的另一种方式,而当您在其中添加参数时它不会那样工作?

【问题讨论】:

  • 我不知道你是如何得出你的结论的。 @b on def a():a = b(a) 相同并不意味着 a = b(a, c) 也可以使用该语法;您认为times 来自@decorator 表单中的哪里?也许你应该看看例如stackoverflow.com/q/5929107/3001761 了解如何创建带参数的装饰器。
  • 什么叫“崩溃”?
  • 提示:装饰器采用 single 参数:要装饰的函数。但是你的装饰器要求 两个 args。那么它会从哪里得到 times arg 呢?
  • @Kanak 当他们使用装饰器表单时,它会抱怨没有提供所需的第二个位置参数。

标签: python decorator


【解决方案1】:

那是因为实际上你还没有很好地理解装饰器。如果要给装饰器传参数,装饰器应该这样嵌套,参数在入口层,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()

您可以取消对装饰器的注释并根据需要尝试。

【讨论】:

    【解决方案2】:

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

    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

    【讨论】:

      猜你喜欢
      • 2023-03-17
      • 2020-03-04
      • 2012-09-06
      • 2016-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-26
      相关资源
      最近更新 更多