如何访问函数内部的函数?

2025-04-10 09:46:00
admin
原创
16
摘要:问题描述:我想知道如何访问另一个函数中的函数。我看到了这样的代码:>>> def make_adder(x): def adder(y): return x+y return adder >>> a = make_adder(5) &...

问题描述:

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

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

那么,还有其他方法可以调用该adder 函数吗?我的第二个问题是为什么在最后一行我调用addernot 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
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用