如何访问函数内的函数?

时间:2013-06-30 22:12:29

标签: python function

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

>>> def make_adder(x):
      def adder(y):
        return x+y
      return adder
>>> a = make_adder(5)
>>> a(10)
15

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

非常感谢良好的解释。

5 个答案:

答案 0 :(得分: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        

MAKE_CLOSURE操作码创建了一个带闭包的函数,一个嵌套函数引用父函数的xLOAD_CLOSURE操作码为函数构建闭包单元格。 / p>

不调用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        

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

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

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

cell = make_closure_cell(5)

现在我们可以在不调用adder()的情况下重新创建make_adder

>>> adder = FunctionType(make_adder.__code__.co_consts[1], globals(),
...                      None, None, (cell,))
>>> adder(10)
15

也许只是调用make_adder()会更简单。

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

上面的会话是考虑到Python 2和3之间的兼容性。较旧的Python 2版本的工作方式相同,尽管有些细节略有不同;某些属性具有不同的名称,例如func_code而不是__code__。如果您想了解详细信息,请在inspect modulePython datamodel中查看相关文档。

答案 1 :(得分:6)

不,你不能直接调用它,因为它是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>

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

>>> make_adder(5)(10)          
15

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

答案 2 :(得分:4)

您将函数adder返回给调用者,而不是调用它的结果,因此没有括号。

由于make_adder返回adder,您已经可以直接访问adder。实际上,a(10)实际上是对adder(10)的调用。

答案 3 :(得分:1)

作为@AshwiniChaudhary答案的附录,您可以使用可修改的对象模拟Python 3.x的非局部。例如:

def counter(name):
    x = [0]
    def inc(n):
        x[0] += n
        print "%s: %d" % (name, x[0])
    return inc

spam = counter('spams')
ham = counter('hams')

spam(3)
ham(1)
spam(1)
ham(2)

在python2.7中,它产生:

$ python closure.py
spams: 3
hams: 1
spams: 4
hams: 3

使用x[0]的原因是尝试重新分配x创建新的本地到inc x

def counter(name):
    x = 0
    def inc(n):
        x += n # this doesn't work!
        print "%s: %d" % (name, x[0])
    return inc

尝试使用它会产生:

Traceback (most recent call last):
  File "closure.py", line 11, in <module>
    spam(3)
  File "closure.py", line 4, in inc
    x += n
UnboundLocalError: local variable 'x' referenced before assignment

尝试使用global时,其余的显而易见的事情也失败了,因为它尝试访问模块级x而不是counter内的模块级nonlocal。 (这就是为什么counter首先被添加的原因!)

关闭时的另一点:它们可以通过实例变量机械地转换为类。我没有像上面那样定义class Counter(object): def __init__(self, name): self.name = name self.x = 0 def inc(self, n): self.x += n print "%s: %d" % (self.name, self.x) ,而是创建了一个类:

spam = Counter('spams')
spam.inc(3)

然后将其用作:

inc(self, n)
例如

。如果您想保留调用语法,Python允许这样做:而不是定义__call__(self, n),定义__call__ - 或将inc定义为调用class Counter(object): def __init__(self, name): self.name = name self.x = 0 def inc(self, n): self.x += n print "%s: %d" % (self.name, self.x) __call__ = inc spam = Counter('spams') ham = Counter('hams') spam.inc(3) ham.inc(1) spam(1) ham(2) ,从而产生:

{{1}}

它显示了班级中有点精神分裂的“两种方式”。 : - )

答案 4 :(得分:0)

我不确定这是否有帮助,但是您可以使用__call__样板来模拟所需的行为。例如:

class MakeAdder:
    def __init__(self, x):
        self.x = x

    def __call__(self, y):
        return self.x + y



a = MakeAdder(5)
a(10)
15