【问题标题】:Understanding how passing arguments to the decorated function works了解将参数传递给装饰函数的工作原理
【发布时间】:2020-11-12 00:08:40
【问题描述】:

我正在学习 Python 装饰器,但在理解将参数传递给装饰函数的工作原理时遇到了一些困难。我不是谈论将参数传递给装饰器本身,而是讨论原始函数需要额外参数的情况。例如,我试图理解以下简单示例(代码 sn-p 的学分转到this 答案):

def decorator(fn):
    def wrapper(arg1, arg2):
        print("I got args! Look: {0}, {1}".format(arg1, arg2))
        fn(arg1, arg2)
    return wrapper

@decorator
def print_full_name(first_name, last_name):
    print("My name is {0} {1}".format(first_name, last_name))

print_full_name("John", "Doe")
# outputs:
# I got args! Look: John Doe
# My name is John Doe

因此,如果我通过删除包装器的参数arg1arg2 故意破坏代码,则会收到以下错误:TypeError: wrapper() takes 0 positional arguments but 2 were given。对我来说很奇怪,当包装器在定义时没有括号内的任何参数时,它需要 2 个参数。另外,我不清楚 如何 first_namelast_name 参数值“映射”到包装函数的参数。我们只将这些参数传递给print_full_name 函数,但似乎它们也传递给了wrapper 函数。也许这与事情的运行顺序有关,但这对我来说并不清楚。

我知道这里有很多关于这个主题的很好的答案,但我找不到能清楚解释这个特定部分的答案。任何帮助将不胜感激。

【问题讨论】:

    标签: python python-decorators


    【解决方案1】:

    你应该分解发生的调用。

    所以,你有:

    def decorator(fn):
        def wrapper(arg1, arg2):
            print("I got args! Look: {0}, {1}".format(arg1, arg2))
            fn(arg1, arg2)
        return wrapper
    
    @decorator
    def print_full_name(first_name, last_name):
        print("My name is {0} {1}".format(first_name, last_name))
    
    print_full_name("John", "Doe")
    # outputs:
    # I got args! Look: John Doe
    # My name is John Doe
    

    因为你已经用decorator 装饰了print_full_name,那么当你这样做时

    print_full_name("John", "Doe")
    

    首先使用参数print_full_name 调用装饰器decorator,如:

    decorator(print_full_name)("John", "Doe")
    

    decorator(print_full_name) 定义为

    def decorator(fn):
        def wrapper(...):
            ...
            ...
        return wrapper
    

    并返回wrapper。因此,上面的结果是:

    wrapper("John", "Doe")
    

    那么,因为wrapper被定义为

    def wrapper(arg1, arg2)
    

    wrapper 的正文中,您有arg1="John"arg2="Doe"

    最后,因为wrapper是在decorator内部定义的,它知道fn是什么,fn=print_full_name在哪里。

    这就是它的工作原理。

    【讨论】:

      【解决方案2】:

      简而言之:传递参数一模一样;你只需要知道你实际调用的是哪个函数。


      装饰器语法只是变相的函数应用。没有它,你只会写

      def decorator(fn):
          def wrapper(arg1, arg2):
              print("I got args! Look: {0}, {1}".format(arg1, arg2))
              fn(arg1, arg2)
          return wrapper
      
      
      def print_full_name(first_name, last_name):
          print("My name is {0} {1}".format(first_name, last_name))
      
      print_full_name = decorator(print_full_name)
      

      所以现在,print_full_name 不指代打印句子的原始函数。它指的是decorator返回的函数。原有功能不丢失;新函数是对名称 fn 的闭包,它是对原始函数的引用。

      【讨论】:

        猜你喜欢
        • 2021-11-21
        • 1970-01-01
        • 2021-08-06
        • 2016-02-14
        • 2014-10-29
        • 2014-11-17
        • 2018-05-30
        • 2021-11-15
        • 1970-01-01
        相关资源
        最近更新 更多