代码之家  ›  专栏  ›  技术社区  ›  Jonathan De Wet

装饰函数内的变量如何访问其范围外的值?

  •  3
  • Jonathan De Wet  · 技术社区  · 7 年前

    这是一个装饰器示例:

    def smart_divide(func):
       def inner(a,b):
          print("I am going to divide",a,"and",b)
          if b == 0:
             print("Whoops! cannot divide")
             return
    
          return func(a,b)
       return inner
    
    @smart_divide
    def divide(a,b):
        return a/b
    

    如果 func 那么变量是如何 a b 从中访问?

    这难道不象是在尝试这样做吗?

    def func(potato):
          print(y, x)
    

    有没有一个基本的概念我没有得到?这里发生的事情是Python中某些模式的一部分还是一种特殊情况 b 知道我们要看是因为它是一个发电机吗?


    使现代化

    来自的新示例 another stack exchange answer

    def my_shiny_new_decorator(a_function_to_decorate):
    
        def the_wrapper_around_the_original_function():
    
            print("Before the function runs")
    
            a_function_to_decorate()
    
            print("After the function runs")
    
        return the_wrapper_around_the_original_function
    
    def a_stand_alone_function():
        print("I am a stand alone function, don't you dare modify me")
    

    发电机采用手动方式

    a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
    a_stand_alone_function()
    

    正确的方法

    @my_shiny_new_decorator
    def another_stand_alone_function():
        print("Leave me alone")
    

    我认为这个例子可能会让我陷入困境,因为我试图将其扩展到涉及参数的情况。

    我现在意识到我所想象的毫无意义

    我认为我发布的原始代码与此等效

    divide = smart_divide(divide(a,b))
    

    如果执行的话会像这样

    def smart_divide(divide(a,b)):
       def inner(a,b):
          print("I am going to divide",a,"and",b)
          if b == 0:
             print("Whoops! cannot divide")
             return
    
          return func(a,b)
       return inner
    

    但这将导致除法(a,b)在顶行执行

    在新示例中,“一个独立函数”的末尾没有()。这意味着它被视为一个物体。

    我的想法是这样的 def smart_divide(divide(a,b)): 没有意义,因为函数不再被视为对象

    这让我很困惑 smart_devide 获取作为参数传递的信息。

    2 回复  |  直到 7 年前
        1
  •  2
  •   Daniel Pryden    7 年前

    smart_divide 收到 a b 传递给它。它返回一个函数对象( inner 功能),以及 那个 函数获取 b 已传递到 .

    如果您尝试以下方法,您可以看到实际发生的情况:

    def smart_divide(func):
       print("I am running in smart_divide; func=", func)
       def inner(a,b):
          print("I am going to divide",a,"and",b)
          if b == 0:
             print("Whoops! cannot divide")
             return
    
          return func(a,b)
       print("I am returning from smart_divide")
       return inner
    
    print("I am running at top level before declaring divide")
    
    @smart_divide
    def divide(a,b):
        return a/b
    
    print("The name 'divide' now refers to", divide)
    
    print("I am now going to call the divide function")
    divide(1, 2)
    

    这将输出:

    I am running at top level before declaring divide
    I am running in smart_divide; func= <function divide at 0x108ff2bf8>
    I am returning from smart_divide
    the name 'divide' now refers to <function smart_divide.<locals>.inner at 0x10565db70>
    I am now going to call the divide function
    I am going to divide 1 and 2
    
        2
  •  2
  •   clemens    7 年前

    不,你的装饰师回来了 inner 作为新的实施 divide . 因此,首先调用该函数 内部的 当程序执行时 divide(1, 2) 例如。呼叫 始终尊重 内部的 (并在代码中进行除法)。

    函数,如

    def divide(a, b):  # signature
        return a / b   # implementation or body
    

    由两部分组成。签名描述了参数和函数的实现。

    您的装饰器只会按如下方式修改函数的实现:

    def divide(a, b):                            # signature remains unmodified
        print("I am going to divide",a,"and",b)  # implementation of inner
        if b == 0:
            print("Whoops! cannot divide")
            return
        return a / b                             # call to the original implementation of divide   
    

    姓名和签名 保持不变。因此 内部的 重要的是,而不是你的装饰师的签名。