如何访问函数内部的函数?
- 2025-04-10 09:46:00
- admin 原创
- 16
问题描述:
我想知道如何访问另一个函数中的函数。我看到了这样的代码:
>>> def make_adder(x):
def adder(y):
return x+y
return adder
>>> a = make_adder(5)
>>> a(10)
15
那么,还有其他方法可以调用该adder
函数吗?我的第二个问题是为什么在最后一行我调用adder
not adder(...)
?
非常感谢您的解释。
解决方案 1:
你确实不想掉进这个兔子洞,但只要你坚持,这是可能的。只要付出一些努力。
每次调用时都会重新创建嵌套函数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
码创建了一个带有闭包的函数,一个引用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
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()
是创建该函数以供其他东西稍后调用它。
上述会话是在考虑 Python 2 和 Python 3 之间的兼容性的情况下进行的。较旧的 Python 2 版本的工作方式相同,尽管某些细节略有不同;某些属性具有不同的名称,例如 而func_code
不是。如果您想了解具体细节,请在模块和Python 数据模型__code__
中查找有关这些内容的文档。inspect
解决方案 2:
不,您不能直接调用它,因为它是一个局部变量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
。返回的对象实际上称为闭包,因为即使函数make_addr
已经返回,adder
它返回的函数对象仍然可以访问变量。在 py3.x 中,你还可以修改using语句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
解决方案 3:
您正在将函数返回adder
给调用者,而不是调用它的结果,因此没有括号。
因为make_adder
返回adder
,您已经可以直接访问adder
。事实上,a(10)
实际上是对 的调用adder(10)
。
解决方案 4:
作为@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)
例如。如果你想保留调用语法,Python 允许这样做:不是定义inc(self, n)
,而是定义__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)
这表明类中存在着有点精神分裂的“两种调用方式”界面。:-)
解决方案 5:
我不确定这是否有帮助,但您可以使用__call__
样板来模拟您正在寻找的行为。例如:
class MakeAdder:
def __init__(self, x):
self.x = x
def __call__(self, y):
return self.x + y
a = MakeAdder(5)
a(10)
15
解决方案 6:
我的看法:
def testFn(Inner=0):
def subFunct():
print ('SubFunct Executed')
if Inner:
return subFunct
print ('Regular Exec')
testFn(Inner=1)()
此函数接受一个默认值为 0 的参数,设置为 True 时,父函数返回内部函数的对象。该对象是一个函数,是可调用的,并使用 '()' 调用:
'testFn(Inner=1)' will return the inner function object
'()' will call it
扫码咨询,免费领取项目管理大礼包!