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

nuk*_*ukl 140 python coding-style

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

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

def method_b(arg):
    return some_data
Run Code Online (Sandbox Code Playgroud)

在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
Run Code Online (Sandbox Code Playgroud)

或者我应该避免这样做?

use*_*312 132

>>> 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
Run Code Online (Sandbox Code Playgroud)

这是你在找什么?它被称为闭包.

  • 这是一个更好的解释.我删除了我的答案 (13认同)
  • 它没有回答这个问题. (8认同)
  • @mango:这只是一个传达概念的玩具示例 - 在实际使用中,`do_it()`可能会比单个`return`语句中某些算术可以处理的更复杂一些. (4认同)
  • @mango用一个稍微有用的例子回答了你的问题.http://stackoverflow.com/a/24090940/2125392 (2认同)

mar*_*eau 45

通过这样做你并没有真正获得太多收益,实际上它会变慢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
Run Code Online (Sandbox Code Playgroud)

注意我self在你的示例函数中添加了一些参数,使它们更像真实的方法(尽管method_b2在技​​术上仍然不是Test类的方法).此外,嵌套函数实际上是在该版本中调用的,与您的版本不同.

  • 每次调用外部函数时,它实际上并没有完全编译内部函数,尽管它必须创建一个函数对象,这需要一些时间.另一方面,函数名称变为局部而非全局函数,因此调用函数的速度更快.在我的试验中,时间方面它基本上是洗涤的大部分时间; 如果多次调用内部函数,它甚至可能更快. (19认同)
  • 是的,你需要多次调用内部函数.如果你在一个循环中调用它,或者只是在几次中调用它,那么为函数设置本地名称的好处将开始超过创建函数的成本.在我的试验中,当你将内部函数调用3-4次时会发生这种情况.当然,通过定义函数的本地名称,例如`method_b = self._method_b`然后调用`method_b`来避免重复的属性查找,你可以获得相同的好处(没有那么多的成本).(碰巧我最近做了很多时间.:) (6认同)
  • @kindall:是的,这是真的.我修改了我的计时测试,因此它对辅助功能进行了30次调用,结果转过来了.在你有机会看到这个回复之后我会删除我的回答.谢谢你的启示. (3认同)
  • 只是想解释我的-1,因为这是一个信息丰富的答案:我给它一个-1因为它把重点放在一个微不足道的性能差异上(在大多数情况下,代码对象创建只需要函数执行时间的一小部分) ).在这些情况下需要考虑的重要一点是,内联函数是否可以提高代码的可读性和可维护性,我认为这通常是因为您不必搜索/滚动文件来查找相关代码. (2认同)
  • @Blixt:我认为你的逻辑是有缺陷的(而且投票不公平)因为同一类的另一个方法与同一类中的另一个方法非常"远",即使它没有嵌套也是极不可能的(并且极不可能在另一个文件中).在我的回答中,第一句话是"通过这样做你并没有真正获得多少",这指出这是一个微不足道的差异. (2认同)
  • @Mayou36:当然,你有权发表自己的意见。我的回答故意含糊不清,因为速度差异很小,唯一需要考虑的其他因素大多是主观的,例如与封装、可读性等的重要性相关的因素。 (2认同)

Civ*_*Fan 26

函数内部的函数通常用于闭包.

(有一个很大的竞争究竟是什么使一个封闭的封闭.)

这是使用内置的示例sum().它定义start一次并从那时起使用它:

def sum_partial(start):
    def sum_start(iterable):
        return sum(iterable, start)
    return sum_start
Run Code Online (Sandbox Code Playgroud)

正在使用:

>>> 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
Run Code Online (Sandbox Code Playgroud)

内置python封口

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

从python文档中,它大致相当于:

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
Run Code Online (Sandbox Code Playgroud)

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


Civ*_*Fan 15

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

除非你有一个很好的理由.你没有.

为什么不?

在函数内定义函数的真正原因什么?

当你真正想要的是一个dingdang 闭合.


vz0*_*vz0 10

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

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

  • 在一个类中,是的,但仅在*函数*中呢?封装是分层的. (3认同)
  • 这是可能的,但我同意,你需要一个很好的理由去做.对于仅在您的类中使用的函数,使用前面的下划线会更加python. (2认同)

mir*_*173 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
Run Code Online (Sandbox Code Playgroud)

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

s=0
for i in range(10**6):
    s+=square0(i)
Run Code Online (Sandbox Code Playgroud)

并得到以下结果

>>> 
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]
>>> 
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 你做的比较并不是很有用.这就像在函数中添加虚拟语句并说它更慢.martineau的示例实际上使用了封装的函数,我没有注意到通过运行他的示例有任何性能差异. (5认同)

cha*_*787 5

这只是关于暴露 API 的一个原则。

使用python,最好避免暴露API在外部空间(模块或类),函数是一个很好的封装地方。

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

  1. 内部函数由外部函数使用。
  2. insider 函数有一个很好的名称来解释其目的,因为代码会说话。
  3. 您的同事(或其他代码阅读器)无法直接理解代码。

尽管如此,滥用这种技术可能会导致问题并暗示设计缺陷。

仅从我的经验来看,也许误解了你的问题。


小智 5

所以最终这主要是一个关于 python 实现有多智能的问题,特别是在内部函数不是闭包而只是一个函数中需要的助手的情况下。

在干净易懂的设计中,函数只在需要的地方使用而不在其他地方暴露是好的设计,无论它们是嵌入到模块中,作为方法的类,还是在另一个函数或方法中。如果做得好,它们确实会提高代码的清晰度。

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

所以我会说通常会使用它们,但是当您真正关心性能时要注意可能的性能下降,并且只有在您进行实际分析表明它们最好被删除时才删除它们。

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


归档时间:

查看次数:

235910 次

最近记录:

6 年,2 月 前