如果只有功能B需要功能A应该在B内定义A?

时间:2011-01-28 18:23:11

标签: python coding-style

简单的例子。两种方法,一种叫另一种方法:

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 吗?像这样:

{{1}}

或者我应该避免这样做?

13 个答案:

答案 0 :(得分:131)

>>> 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

这是你在找什么?它被称为closure

答案 1 :(得分:43)

通过这样做你并没有获得多少收益,实际上它会减慢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类的方法)。此外,嵌套函数实际上是在该版本中调用的,与您的不同。

答案 2 :(得分:24)

函数内部的函数通常用于closures

lot of contention超过what exactlya closure a closure。)

以下是使用内置sum()的示例。它定义start一次并从那时开始使用它:

def sum_partial(start):
    def sum_start(iterable):
        return sum(iterable, start)
    return sum_start

使用中:

>>> sum_with_1 = sum_partial(1)
>>> sum_with_3 = sum_partial(3)
>>> 
>>> sum_with_1
<function sum_start at 0x7f3726e70b90>
>>> sum_with_3
<function sum_start at 0x7f3726e70c08>
>>> sum_with_1((1,2,3))
7
>>> sum_with_3((1,2,3))
9

内置python闭包

functools.partial是一个闭包的例子。

From the python docs,它大致相当于:

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

(请到下面的@ user225312获得答案。我觉得这个例子比较容易理解,希望有助于回答@芒果的评论。)

答案 3 :(得分:13)

通常,不,不要在函数内定义函数。

除非你有充分的理由。你没有。

为什么不呢?

是什么在函数内定义函数的一个很好的理由?

当您实际想要的是一个dingdang closure

答案 4 :(得分:10)

在另一个函数中声明一个函数实际上很好。这对创建装饰器特别有用。

但是,根据经验,如果函数很复杂(超过10行),最好在模块级别声明它。

答案 5 :(得分:7)

我发现了这个问题,因为我想提出一个问题,如果使用嵌套函数会导致性能影响。我在具有四核2.5 GHz Intel i5-2530M处理器的Windows笔记本上使用Python 3.2.5运行以下功能的测试

def square0(x):
    return x*x

def square1(x):
    def dummy(y):
        return y*y
    return x*x

def square2(x):
    def dummy1(y):
        return y*y
    def dummy2(y):
        return y*y
    return x*x

def square5(x):
    def dummy1(y):
        return y*y
    def dummy2(y):
        return y*y
    def dummy3(y):
        return y*y
    def dummy4(y):
        return y*y
    def dummy5(y):
        return y*y
    return x*x

我测量了以下20次,也用于square1,square2和square5:

s=0
for i in range(10**6):
    s+=square0(i)

并得到以下结果

>>> 
m = mean, s = standard deviation, m0 = mean of first testcase
[m-3s,m+3s] is a 0.997 confidence interval if normal distributed

square? m     s       m/m0  [m-3s ,m+3s ]
square0 0.387 0.01515 1.000 [0.342,0.433]
square1 0.460 0.01422 1.188 [0.417,0.503]
square2 0.552 0.01803 1.425 [0.498,0.606]
square5 0.766 0.01654 1.979 [0.717,0.816]
>>> 

square0没有嵌套函数,square1有一个嵌套函数,square2有两个嵌套函数,square5有五个嵌套函数。嵌套函数仅被声明但未被调用。

因此,如果您在一个不调用的函数中定义了5个嵌套函数,那么函数的执行时间是没有嵌套函数的函数的两倍。我认为在使用嵌套函数时应该谨慎。

可以在ideone找到生成此输出的整个测试的Python文件。

答案 6 :(得分:4)

这只是曝光API的原则。

使用python,避免在外层空间(模块或类)中暴露API是个好主意,函数是一个很好的封装位置。

这可能是一个好主意。当你确保

  1. 外部函数使用 ONLY
  2. 内幕功能有一个名称来解释其目的,因为代码会说话。
  3. 您的同事(或其他代码阅读器)无法直接理解代码。
  4. 尽管如此,滥用这种技术可能会导致问题并暗示设计缺陷。

    就在我的exp中,也许会误解你的问题。

答案 7 :(得分:4)

所以最后它在很大程度上是关于python实现有多聪明的问题,特别是在内部函数不是闭包但只是函数需要帮助的情况下。

在干净可理解的设计中,只有在需要它们并且不在其他地方暴露的功能才是好的设计,无论它们是嵌入模块,类是作为方法,还是嵌入在另一个函数或方法中。如果做得好,他们确实提高了代码的清晰度。

当内部函数是一个闭包时,即使该函数没有从包含函数返回以供其他地方使用,它也可以帮助清晰。

所以我会说通常会使用它们,但是当你真正关心性能时要注意可能的性能影响,如果你做的实际分析表明它们最好被删除,那么只能删除它们。

在你编写的所有python代码中,不要过早地使用“内部函数BAD”进行优化。请。

答案 8 :(得分:1)

完全可以这样做,但除非你需要使用闭包或返回我可能放在模块级别的函数。我想在第二个代码示例中你的意思是:

...
some_data = method_b() # not some_data = method_b

否则,some_data将成为函数。

在模块级别使用它将允许其他函数使用method_b(),如果您使用类似Sphinx(和autodoc)的文档,它也允许您记录method_b。

如果您正在做一些可以由对象表示的内容,您也可以考虑将功能放在一个类中的两个方法中。如果您正在寻找所有这些,那么这也很好地包含了逻辑。

答案 9 :(得分:1)

做类似的事情:

def some_function():
    some_other_function()
def some_other_function():
    return 42 

如果您要运行some_function(),则会运行some_other_function()并返回42。

编辑:我最初声明你不应该在另一个内部定义一个函数,但有人指出,有时这样做是可行的。

答案 10 :(得分:1)

mdlp的回答对我不起作用。

这样做了:

def some_function():
    return some_other_function()
def some_other_function():
    return 42

print some_function()

答案 11 :(得分:0)

您可以使用它来避免定义全局变量。这为您提供了其他设计的替代方案。提出问题解决方案的3种设计。

A)使用没有全局变量的函数

def calculate_salary(employee, list_with_all_employees):
    x = _calculate_tax(list_with_all_employees)

    # some other calculations done to x
    pass

    y = # something 

    return y

def _calculate_tax(list_with_all_employees):
    return 1.23456 # return something

B)将函数与全局变量一起使用

_list_with_all_employees = None

def calculate_salary(employee, list_with_all_employees):

    global _list_with_all_employees
    _list_with_all_employees = list_with_all_employees

    x = _calculate_tax()

    # some other calculations done to x
    pass

    y = # something

    return y

def _calculate_tax():
    return 1.23456 # return something based on the _list_with_all_employees var

C)在另一个函数中使用函数

def calculate_salary(employee, list_with_all_employees):

    def _calculate_tax():
        return 1.23456 # return something based on the list_with_a--Lemployees var

    x = _calculate_tax()

    # some other calculations done to x
    pass
    y = # something 

    return y

解决方案 C)允许在外部函数范围内使用变量,而无需在内部函数中声明它们。在某些情况下可能有用。

答案 12 :(得分:0)

Function in function python

def Greater(a,b):
    if a>b:
        return a
    return b

def Greater_new(a,b,c,d):
    return Greater(Greater(a,b),Greater(c,d))

print("Greater Number is :-",Greater_new(212,33,11,999))