当仅被一个函数需要时,嵌套函数是个好方法吗?

183 投票
12 回答
268279 浏览
提问于 2025-04-16 10:48

假设有一个叫做 function A 的函数,它只被另一个叫 function B 的函数需要,那么 A 是否应该放在 B 里面定义呢?

这里有个简单的例子。两个方法,一个是从另一个方法里调用的:

def method_a(arg):
    some_data = method_b(arg)

def method_b(arg):
    return some_data

在 Python 里,我们可以在一个 def 里面再定义一个 def。所以,如果 method_b 只在 method_a 中被调用,那我是不是应该把 method_b 放在 method_a 里面定义呢?像这样:

def method_a(arg):
    
    def method_b(arg):
        return some_data

    some_data = method_b(arg)

还是说我应该避免这样做呢?

12 个回答

30

一般来说,不要在函数里面定义另一个函数。

除非你有非常好的理由,但其实你没有。

为什么不呢?

那么,什么情况下在函数里面定义函数是个好理由呢?

当你真正想要的是一个闭包的时候。

55

这样做其实没什么好处,反而会让method_a变慢,因为每次调用的时候都会重新定义和编译那个其他的函数。考虑到这一点,可能更好的做法是给函数名加个下划线,表示它是一个私有方法,比如_method_b

我想如果有某种原因导致嵌套函数的定义每次都不一样,你可能会想这么做,但这可能说明你的设计有问题。不过,确实有一个合理的理由去这么做,那就是让嵌套函数可以使用传给外部函数的参数,而这些参数并没有明确传递给它们。这种情况在写函数装饰器的时候有时会出现,比如说。这个在被接受的答案中有展示,尽管没有定义或使用装饰器。

更新:

这里有证据表明嵌套函数会更慢(使用Python 3.6.1),虽然在这个简单的例子中,速度差别并不大:

setup = """
class Test(object):
    def separate(self, arg):
        some_data = self._method_b(arg)

    def _method_b(self, arg):
        return arg+1

    def nested(self, arg):

        def method_b2(self, arg):
            return arg+1

        some_data = method_b2(self, arg)

obj = Test()
"""
from timeit import Timer
print(min(Timer(stmt='obj.separate(42)', setup=setup).repeat()))  # -> 0.24479823284461724
print(min(Timer(stmt='obj.nested(42)', setup=setup).repeat()))    # -> 0.26553459700452575

注意我在你的示例函数中添加了一些self参数,让它们更像真实的方法(虽然method_b2技术上仍然不是Test类的方法)。而且在那个版本中,嵌套函数实际上是被调用的,不像你的例子。

136
>>> def sum(x, y):
...     def do_it():
...             return x + y
...     return do_it
... 
>>> a = sum(1, 3)
>>> a
<function do_it at 0xb772b304>
>>> a()
4

这就是你想要的东西吗?它被称为闭包

撰写回答