如果仅功能B需要功能A,是否应在B内部定义A?[关闭]

问题:如果仅功能B需要功能A,是否应在B内部定义A?[关闭]

简单的例子。两种方法,一种从另一种调用:

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)

还是应该避免这样做?

Let’s say that a function A is required only by function B, should A be defined inside B?

Simple example. Two methods, one called from another:

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

def method_b(arg):
    return some_data

In Python we can declare def inside another def. So, if method_b is required for and called only from method_a, should I declare method_b inside method_a? like this :

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

    some_data = method_b(arg)

Or should I avoid doing this?


回答 0

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

这是您要找的东西吗?这叫做闭包

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

Is this what you were looking for? It’s called a closure.


回答 1

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

You don’t really gain much by doing this, in fact it slows method_a down because it’ll define and recompile the other function every time it’s called. Given that, it would probably be better to just prefix the function name with underscore to indicate it’s a private method — i.e. _method_b.

I suppose you might want to do this if the nested function’s definition varied each time for some reason, but that may indicate a flaw in your design. That said, there is a valid reason to do this to allow the nested function to use arguments that were passed to the outer function but not explicitly passed on to them, which sometimes occurs when writing function decorators, for example. It’s what is being shown in the accepted answer although a decorator is not being defined or used.

Update:

Here’s proof that nesting them is slower (using Python 3.6.1), although admittedly not by much in this trivial case:

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

Note I added some self arguments to your sample functions to make them more like real methods (although method_b2 still isn’t technically a method of the Test class). Also the nested function is actually called in that version, unlike yours.


回答 2

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

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

这是使用内置的示例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 是关闭的示例。

从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表示敬意。我发现此示例更容易理解,希望可以帮助回答@mango的评论。)

A function inside of a function is commonly used for closures.

(There is a lot of contention over what exactly makes a closure a closure.)

Here’s an example using the built-in sum(). It defines start once and uses it from then on:

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

In use:

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

Built-in python closure

functools.partial is an example of a closure.

From the python docs, it’s roughly equivalent to:

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

(Kudos to @user225312 below for the answer. I find this example easier to figure out, and hopefully will help answer @mango’s comment.)


回答 3

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

除非您有充分的理由。你不知道

为什么不?

在函数内部定义函数的真正好的理由什么?

当您真正想要的是当当网

Generally, no, do not define functions inside functions.

Unless you have a really good reason. Which you don’t.

Why not?

What is a really good reason to define functions inside functions?

When what you actually want is a dingdang closure.


回答 4

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

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

It’s actually fine to declare one function inside another one. This is specially useful creating decorators.

However, as a rule of thumb, if the function is complex (more than 10 lines) it might be a better idea to declare it on the module level.


回答 5

我找到了这个问题,因为我想提出一个问题,如果使用嵌套函数,为什么会对性能产生影响。我在带有四核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

我对平方1,平方2和平方5进行了以下20次测量:

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文件。

I found this question because I wanted to pose a question why there is a performance impact if one uses nested functions. I ran tests for the following functions using Python 3.2.5 on a Windows Notebook with a Quad Core 2.5 GHz Intel i5-2530M processor

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

I measured the following 20 times, also for square1, square2, and square5:

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

and got the following results

>>> 
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 has no nested function, square1 has one nested function, square2 has two nested functions and square5 has five nested functions. The nested functions are only declared but not called.

So if you have defined 5 nested funtions in a function that you don’t call then the execution time of the function is twice of the function without a nested function. I think should be cautious when using nested functions.

The Python file for the whole test that generates this output can be found at ideone.


回答 6

这只是有关暴露API的原则。

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

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

  1. 内部函数由外部函数使用。
  2. 内部函数具有很好的名称来解释其用途,因为代码可以说明。
  3. 代码无法被您的同事(或其他代码阅读器)直接理解。

即使滥用此技术也可能会引起问题并暗示设计缺陷。

仅根据我的经验,也许会误解您的问题。

It’s just a principle about exposure APIs.

Using python, It’s a good idea to avoid exposure API in outer space(module or class), function is a good encapsulation place.

It could be a good idea. when you ensure

  1. inner function is ONLY used by outer function.
  2. insider function has a good name to explain its purpose because the code talks.
  3. code cannot directly understand by your colleagues(or other code-reader).

Even though, Abuse this technique may cause problems and implies a design flaw.

Just from my exp, Maybe misunderstand your question.


回答 7

因此,最后主要是一个关于python实现有多聪明的问题,尤其是在内部函数不是闭包而只是in函数仅需要帮助器的情况下。

在清晰易懂的设计中,仅将功能放在需要的地方,而不在其他地方公开才是好的设计,无论它们是嵌入在模块,方法类中还是在另一个函数或方法中。如果做得好,它们确实可以提高代码的清晰度。

并且当内部函数是一个闭包时,即使该函数没有从包含函数中返回以供其他地方使用,它也可以大大提高清晰度。

因此,我想通常会使用它们,但要在您真正关心性能时注意性能可能受到的影响,并且只有在进行实际性能分析后最好将它们删除才能删除它们。

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

So in the end it is largely a question about how smart the python implementation is or is not, particularly in the case of the inner function not being a closure but simply an in function needed helper only.

In clean understandable design having functions only where they are needed and not exposed elsewhere is good design whether they be embedded in a module, a class as a method, or inside another function or method. When done well they really improve the clarity of the code.

And when the inner function is a closure that can also help with clarity quite a bit even if that function is not returned out of the containing function for use elsewhere.

So I would say generally do use them but be aware of the possible performance hit when you actually are concerned about performance and only remove them if you do actual profiling that shows they best be removed.

Do not do premature optimization of just using “inner functions BAD” throughout all python code you write. Please.


回答 8

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

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

否则,some_data将成为函数。

在模块级别拥有它会允许其他函数使用method_b(),如果您使用Sphinx(和autodoc)之类的文档进行记录,那么它也将允许您记录method_b。

如果您正在执行某个对象可以表示的操作,则可能还需要考虑将功能放在类的两个方法中。如果这就是您要查找的内容,那么它也包含了逻辑。

It’s perfectly OK doing it that way, but unless you need to use a closure or return the function I’d probably put in the module level. I imagine in the second code example you mean:

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

otherwise, some_data will be the function.

Having it at the module level will allow other functions to use method_b() and if you’re using something like Sphinx (and autodoc) for documentation, it will allow you to document method_b as well.

You also may want to consider just putting the functionality in two methods in a class if you’re doing something that can be representable by an object. This contains logic well too if that’s all you’re looking for.


回答 9

做类似的事情:

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

如果要运行some_function(),它将运行some_other_function()并返回42。

编辑:我最初说过,您不应该在另一个函数内部定义一个函数,但有人指出,有时这样做是很实际的。

Do something like:

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

if you were to run some_function() it would then run some_other_function() and returns 42.

EDIT: I originally stated that you shouldn’t define a function inside of another but it was pointed out that it is practical to do this sometimes.


回答 10

您可以使用它来避免定义全局变量。这为您提供了其他设计的替代方案。提供解决方案的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)允许在外部函数范围内使用变量,而无需在内部函数中声明它们。在某些情况下可能有用。

You can use it to avoid defining global variables. This gives you an alternative for other designs. 3 designs presenting a solution to a problem.

A) Using functions without globals

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) Using functions with globals

_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) Using functions inside another function

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

Solution C) allows to use variables in the scope of the outer function without having the need to declare them in the inner function. Might be useful in some situations.


回答 11

函数在函数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))

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