170

function Aaが必要なのは、Aだけであるとしましょうfunction B。AはB内で定義する必要がありますか?

簡単な例。2つのメソッド。1つは別のメソッドから呼び出されます。

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

def method_b(arg):
    return some_data

Pythonでは、def別の内部で宣言できdefます。したがって、method_bが必要であり、からのみ呼び出される場合、内部method_aで宣言する必要がありますか?このような :method_bmethod_a

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

    some_data = method_b(arg)

それとも私はこれを避けるべきですか?

4

12 に答える 12

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

これはあなたが探していたものですか?それはクロージャーと呼ばれます。

于 2011-01-28T18:29:58.830 に答える
52

これを行っても実際にはあまり得られません。実際、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また、ネストされた関数は、あなたのものとは異なり、そのバージョンで実際に呼び出されます。

于 2011-01-28T20:01:28.910 に答える
10

実際には、ある関数を別の関数内で宣言しても問題ありません。これは、デコレータの作成に特に役立ちます。

ただし、経験則として、関数が複雑な場合 (10 行以上) は、モジュール レベルで宣言することをお勧めします。

于 2011-01-28T18:31:19.933 に答える
8

この質問を見つけたのは、ネストされた関数を使用するとパフォーマンスに影響がある理由を知りたかったからです。クアッド コア 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が 1つある、square2ネストされた関数が 2 つある、およびネストされた関数square5が 5 つある ネストされた関数は宣言されるだけで、呼び出されません。

したがって、呼び出さない関数で 5 つのネストされた関数を定義した場合、関数の実行時間は、ネストされた関数を使用しない関数の 2 倍になります。ネストされた関数を使用するときは注意が必要だと思います。

この出力を生成するテスト全体の Python ファイルは、ideoneにあります。

于 2014-10-05T21:38:41.907 に答える
6

したがって、最終的には、特に内部関数がクロージャーではなく、関数内で必要なヘルパーのみである場合は特に、python 実装がどれほどスマートかどうかが問題になります。

関数が必要な場所にのみ配置され、他の場所では公開されていない、明確に理解できる設計は、関数がモジュール、メソッドとしてのクラス、または別の関数またはメソッドの内部に埋め込まれているかどうかに関係なく、優れた設計です。うまくやると、コードの明瞭さが本当に向上します。

また、内部関数がクロージャーである場合、その関数が他の場所で使用するために包含関数から返されない場合でも、明確にするのにかなり役立ちます。

したがって、一般的にはそれらを使用しますが、実際にパフォーマンスを懸念している場合はパフォーマンスへの影響の可能性に注意し、実際のプロファイリングを行って削除するのが最適であることを示す場合にのみ削除してください。

作成するすべての python コード全体で「内部関数 BAD」を使用するだけの時期尚早な最適化を行わないでください。お願いします。

于 2016-08-13T23:37:33.027 に答える
5

これは公開 API に関する単なる原則です。

Python を使用して、外部空間 (モジュールまたはクラス) での API の公開を避けることをお勧めします。関数はカプセル化の良い場所です。

それは良い考えかもしれません。確保するとき

  1. 内部関数は、外部関数によってのみ使用されます。
  2. インサイダー関数は、コードが話すため、その目的を説明するのに適した名前です。
  3. コードは、同僚 (または他のコードリーダー) が直接理解することはできません。

ただし、この手法を悪用すると問題が発生する可能性があり、設計上の欠陥を意味します。

私の経験から、あなたの質問を誤解しているかもしれません。

于 2012-12-18T10:02:55.877 に答える
1

次のようにします。

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

実行some_function()すると、実行されsome_other_function()て 42 が返されます。

編集: 私は当初、関数を別の関数内で定義すべきではないと述べましたが、これを行うことが実際的であることが時々指摘されました。

于 2014-05-30T12:26:48.620 に答える
1

そのようにするのはまったく問題ありませんが、クロージャーを使用する必要がないか、関数を返す必要がない限り、おそらくモジュールレベルに入れます。2番目のコード例であなたが意味していると思います:

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

それ以外の場合は、some_data が関数になります。

モジュール レベルでこれを使用すると、他の関数で method_b() を使用できるようになります。ドキュメントにSphinx (および autodoc) などを使用している場合は、method_b もドキュメント化できます。

オブジェクトで表現できることをしている場合は、機能をクラスの 2 つのメソッドに配置することを検討することもできます。探しているのがそれだけであれば、これにはロジックも含まれています。

于 2011-01-28T20:17:38.130 に答える