web-dev-qa-db-ja.com

Pythonの入れ子関数

Python次のようなコードを使用すると、どのような利点または影響が得られますか。

class some_class(parent_class):
    def doOp(self, x, y):
        def add(x, y):
            return x + y
        return add(x, y)

私はこれをオープンソースプロジェクトで見つけました。ネストされた関数の中で何か便利なことをしましたが、それを呼び出す以外は何もしませんでした。 (実際のコードは here にあります。)なぜ誰かがこのようにコーディングするのでしょうか?外側の通常の関数ではなく、ネストされた関数内にコードを記述することには、何らかの利点や副作用がありますか?

78
Hosam Aly

通常は、closuresにするために行います:

def make_adder(x):
    def add(y):
        return x + y
    return add

plus5 = make_adder(5)
print(plus5(12))  # prints 17

内部関数は、外側のスコープ(この場合はローカル変数x)から変数にアクセスできます。外側のスコープから変数にアクセスしていない場合、それらは実際には異なるスコープを持つ単なる通常の関数です。

103
Adam Rosenfield

関数ジェネレーターは別として、内部関数の作成はほとんど関数ジェネレーターの定義ですが、ネストされた関数を作成する理由は、読みやすさを向上させるためです。外部関数によってのみ呼び出される小さな関数がある場合は、定義をインライン化するので、その関数が何をしているかを判断するためにスキップする必要はありません。後で関数を再利用する必要がある場合は、カプセル化メソッドの外側にある内部メソッドをいつでも移動できます。

おもちゃの例:

import sys

def Foo():
    def e(s):
        sys.stderr.write('ERROR: ')
        sys.stderr.write(s)
        sys.stderr.write('\n')
    e('I regret to inform you')
    e('that a shameful thing has happened.')
    e('Thus, I must issue this desultory message')
    e('across numerous lines.')
Foo()
52
Ross Rogers

内部メソッドを使用する潜在的な利点の1つは、引数として渡すことなく外部メソッドのローカル変数を使用できることです。

def helper(feature, resultBuffer):
  resultBuffer.print(feature)
  resultBuffer.printLine()
  resultBuffer.flush()

def save(item, resultBuffer):

  helper(item.description, resultBuffer)
  helper(item.size, resultBuffer)
  helper(item.type, resultBuffer)

次のように書くことができます。

def save(item, resultBuffer):

  def helper(feature):
    resultBuffer.print(feature)
    resultBuffer.printLine()
    resultBuffer.flush()

  helper(item.description)
  helper(item.size)
  helper(item.type)
24
Kuba

そのようなコードの正当な理由をイメージすることはできません。

他のOpsのように、古いリビジョンでは内部機能に理由があるのか​​もしれません。

たとえば、これはslightlyより意味があります:

class some_class(parent_class):
    def doOp(self, op, x, y):
        def add(x, y):
            return x + y
        def sub(x,y):
            return x - y
        return locals()[op](x,y)

some_class().doOp('add', 1,2)

ただし、内部関数は代わりに(「プライベート」)クラスメソッドである必要があります。

class some_class(object):
    def _add(self, x, y):
        return x + y
    def doOp(self, x, y):
        return self._add(x,y)
8
Jochen Ritzel

ローカルメソッドの背後にある考え方は、ローカル変数に似ています:より大きな名前空間を汚染しないでください。ほとんどの言語はそのような機能を直接提供しないため、明らかに利点は限られています。

6
avguchenko

コードはまさにこのようでしたか?このようなことをする通常の理由は、パーシャル-ベイクインされたパラメーターを持つ関数を作成するためです。外部関数を呼び出すと、パラメーターを必要としない呼び出し可能オブジェクトが返されるため、パラメーターを渡すことが不可能な場所に格納して使用できます。ただし、投稿したコードはそれを行いません-呼び出し可能なものではなく、すぐに関数を呼び出して結果を返します。あなたが見た実際のコードを投稿すると便利かもしれません。

1
Daniel Roseman