当仅被一个函数需要时,嵌套函数是个好方法吗?
假设有一个叫做 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
一般来说,不要在函数里面定义另一个函数。
除非你有非常好的理由,但其实你没有。
为什么不呢?
- 这样会让单元测试变得不方便。 你有在做单元测试吗?
- 而且这样做并不能完全隐藏代码,在Python中,没什么是完全安全的。
- 建议使用标准的Python代码风格来封装方法。
- 每次运行外层函数时,你都会不必要地重新创建一个相同的函数对象。
- 如果你的函数真的那么简单,你应该使用一个
lambda
表达式。
那么,什么情况下在函数里面定义函数是个好理由呢?
当你真正想要的是一个闭包的时候。
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
这就是你想要的东西吗?它被称为闭包。