如何访问函数内的函数?

ovr*_*ity 12 python function

我想知道如何访问另一个函数内的函数.我看到这样的代码:

>>> def make_adder(x):
      def adder(y):
        return x+y
      return adder
>>> a = make_adder(5)
>>> a(10)
15
Run Code Online (Sandbox Code Playgroud)

那么,还有另一种方法来调用adder 函数吗?我的第二个问题是为什么在最后一行我adder不打电话adder(...)

很好的解释非常感谢.

Mar*_*ers 18

你真的不想走下这个兔子洞,但如果你坚持,那就有可能.有一些工作.

每次调用时都会重新创建嵌套函数make_adder():

>>> import dis
>>> dis.dis(make_adder)
  2           0 LOAD_CLOSURE             0 (x)
              3 BUILD_TUPLE              1
              6 LOAD_CONST               1 (<code object adder at 0x10fc988b0, file "<stdin>", line 2>)
              9 MAKE_CLOSURE             0
             12 STORE_FAST               1 (adder)

  4          15 LOAD_FAST                1 (adder)
             18 RETURN_VALUE        
Run Code Online (Sandbox Code Playgroud)

MAKE_CLOSURE那里的操作码创建了一个带闭包的函数,一个x从父函数引用的嵌套函数(LOAD_CLOSURE操作码为函数构建闭包单元).

在不调用make_adder函数的情况下,您只能访问代码对象; 它与make_adder()函数代码一起存储为常量.但是,字节代码adder可以将x变量作为范围单元格访问,这使得代码对象几乎对您无用:

>>> make_adder.__code__.co_consts
(None, <code object adder at 0x10fc988b0, file "<stdin>", line 2>)
>>> dis.dis(make_adder.__code__.co_consts[1])
  3           0 LOAD_DEREF               0 (x)
              3 LOAD_FAST                0 (y)
              6 BINARY_ADD          
              7 RETURN_VALUE        
Run Code Online (Sandbox Code Playgroud)

LOAD_DEREF从闭包单元格加载值.要使代码对象再次成为函数对象,您必须将其传递给函数构造函数:

>>> from types import FunctionType
>>> FunctionType(make_adder.__code__.co_consts[1], globals(),
...              None, None, (5,))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: arg 5 (closure) expected cell, found int
Run Code Online (Sandbox Code Playgroud)

但正如您所看到的,构造函数希望找到一个闭包,而不是一个整数值.要创建一个闭包,我们需要一个具有自由变量的函数; 编译器标记为可用于结束的那些.并且它需要将那些已关闭的值返回给我们,否则无法创建闭包.因此,我们创建一个嵌套函数只是为了创建一个闭包:

def make_closure_cell(val):
    def nested():
        return val
    return nested.__closure__[0]

cell = make_closure_cell(5)
Run Code Online (Sandbox Code Playgroud)

现在我们可以adder()不用调用重新创建make_adder:

>>> adder = FunctionType(make_adder.__code__.co_consts[1], globals(),
...                      None, None, (cell,))
>>> adder(10)
15
Run Code Online (Sandbox Code Playgroud)

也许只是打电话make_adder()会更简单.

顺便提一下,正如您所看到的,函数是Python中的第一类对象.make_adder是一个对象,并通过添加(somearguments)调用,或调用该函数.在这种情况下,该函数返回另一个函数对象,您也可以调用它.在上面如何创建adder()没有调用的曲折例子中make_adder(),我make_adder没有调用它就引用了函数对象; 例如,反汇编附加到它的Python字节代码,或从中检索常量或闭包.同样,make_adder()函数返回adder函数对象; 该make_adder()是创建别的东西以后调用它的功能.

上面的会话考虑了Python 2和3之间的兼容性.较旧的Python 2版本的工作方式相同,尽管有些细节略有不同; 某些属性具有不同的名称,例如func_code代替__code__.如果您想了解详细信息,请在inspect模块Python数据模型中查找有关这些内容的文档.


Ash*_*ary 7

不,你不能直接调用它,因为它是一个局部变量make_adder.

您需要使用adder()因为return adder在调用adder时返回了函数对象make_adder(5).要执行此功能对象,您需要()

def make_adder(x):
       def adder(y):
           return x+y
       return adder
... 
>>> make_adder(5)             #returns the function object adder
<function adder at 0x9fefa74>
Run Code Online (Sandbox Code Playgroud)

在这里你可以直接调用它,因为你可以访问它,因为它是由函数返回的make_adder.返回的对象实际上称为闭包,因为即使函数make_addr已经返回,adder它返回的函数对象仍然可以访问该变量x.在py3.x中,您还可以修改xusing nonlocal语句的值.

>>> make_adder(5)(10)          
15
Run Code Online (Sandbox Code Playgroud)

Py3.x示例:

>>> def make_addr(x):
        def adder(y):
                nonlocal x
                x += 1
                return x+y
        return adder
... 
>>> f = make_addr(5)
>>> f(5)               #with each call x gets incremented
11
>>> f(5)
12

#g gets it's own closure, it is not related to f anyhow. i.e each call to 
# make_addr returns a new closure.
>>> g = make_addr(5)  
>>> g(5)
11 
>>> g(6)
13
Run Code Online (Sandbox Code Playgroud)