Python中的嵌套函数


83

像这样的Python代码可以给我们带来什么好处或启示:

class some_class(parent_class):
    def doOp(self, x, y):
        def add(x, y):
            return x + y
        return add(x, y)

我在一个开源项目中发现了这一点,它在嵌套函数内部做了一些有用的事情,但是除了调用它之外,什么也没做。(可以在此处找到实际的代码。)为什么有人会这样编码?在嵌套函数内部而不是外部普通函数中编写代码是否有好处或副作用?


4
这是您找到的实际代码,还是只是构造的简化示例?
MAK

这是一个简化的示例。实际的代码可以在这里找到:bazaar.launchpad.net/%7Eopenerp/openobject-server/trunk/...
胡沙姆阿里

2
您的链接(指向HEAD)现在不正确。尝试:bazaar.launchpad.net/~openerp/openobject-server/trunk/annotate/...
克雷格麦奎因

你是对的克雷格。谢谢。
Hosam Aly

Answers:


111

通常,您这样做是为了关闭

def make_adder(x):
    def add(y):
        return x + y
    return add

plus5 = make_adder(5)
print(plus5(12))  # prints 17

内部函数可以从封闭范围访问变量(在这种情况下,是局部变量x)。如果您不从封闭范围访问任何变量,那么它们实际上只是具有不同范围的普通函数。


5
为此,我更喜欢partials :plus5 = functools.partial(operator.add, 5)。装饰器将是关闭的更好示例。
Jochen Ritzel 09年

4
谢谢,但是正如您在我发布的代码段中所见,情况并非如此:嵌套函数只是在外部函数中调用。
Hosam Aly

57

除了函数生成器以外,内部函数的创建几乎是函数生成器的定义,我创建嵌套函数的原因是为了提高可读性。如果我有一个只能由外部函数调用的小函数,那么我会内联定义,这样您就不必跳过来确定该函数在做什么。如果以后需要重用该函数,则可以始终将内部方法移到封装方法之外。

玩具示例:

import sys

def Foo():
    def e(s):
        sys.stderr.write('ERROR: ')
        sys.stderr.write(s)
        sys.stderr.write('\n')
    e('I regret to inform you')
    e('that a shameful thing has happened.')
    e('Thus, I must issue this desultory message')
    e('across numerous lines.')
Foo()

4
唯一的问题是,这有时会使单元测试更加困难,因为您无法访问内部函数。
Challenger5

1
他们看起来真可爱!
逐渐变细

FWIY @ Challenger5如果内部函数类似于私有类函数,则无论如何它们都不会进行单元测试。我已经开始这样做,以使方法更具可读性,同时使定义与所讨论的方法保持接近。
米切尔·柯里

26

使用内部方法的一个潜在好处是,它允许您使用外部方法的局部变量而无需将其作为参数传递。

def helper(feature, resultBuffer):
  resultBuffer.print(feature)
  resultBuffer.printLine()
  resultBuffer.flush()

def save(item, resultBuffer):

  helper(item.description, resultBuffer)
  helper(item.size, resultBuffer)
  helper(item.type, resultBuffer)

可以写成如下,可以说读起来更好

def save(item, resultBuffer):

  def helper(feature):
    resultBuffer.print(feature)
    resultBuffer.printLine()
    resultBuffer.flush()

  helper(item.description)
  helper(item.size)
  helper(item.type)

8

我无法为此类代码提供任何充分的理由。

像其他Ops一样,在较早的版本中可能有内部功能的原因。

例如,这使得稍微更有意义:

class some_class(parent_class):
    def doOp(self, op, x, y):
        def add(x, y):
            return x + y
        def sub(x,y):
            return x - y
        return locals()[op](x,y)

some_class().doOp('add', 1,2)

但是内部函数应该是(“私有”)类方法:

class some_class(object):
    def _add(self, x, y):
        return x + y
    def doOp(self, x, y):
        return self._add(x,y)

是的,也许doOp用了一个字符串来指定要在参数上使用的运算符...
Skilldrick

1
@ArtOfWarfare最好只使用模块。类是针对状态的。
aehlke 2013年

6

局部方法背后的思想类似于局部变量:不要污染较大的名称空间。显然,好处是有限的,因为大多数语言也不直接提供这种功能。


1

您确定代码完全像这样吗?进行此类操作的正常原因是要创建局部函数-具有内置参数的函数。调用外部函数将返回不需要参数的可调用对象,因此可以在无法传递参数的地方进行存储和使用。但是,您发布的代码将无法执行此操作-它会立即调用该函数并返回结果,而不是可调用的结果。发布您看到的实际代码可能会很有用。


1
我在对问题的评论中添加了指向原始代码的链接。如您所见,我的是一个简化的示例,但仍然几乎相同。
Hosam Aly
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.