web-dev-qa-db-ja.com

Pythonで関数を前方宣言することは可能ですか?

Pythonで関数を前方宣言することは可能ですか?宣言する前に、自分のcmp関数を使用してリストをソートしたい。

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

呼び出し後にcmp_configsメソッドの定義を配置するようにコードを整理しました。このエラーで失敗します:

NameError: name 'cmp_configs' is not defined

cmp_configsメソッドを使用する前に「宣言」する方法はありますか?それは私のコードをきれいに見せますか?

この問題が発生しないようにコードを再編成する必要があると言う誘惑にかられる人もいると思います。ただし、これはおそらく避けられない場合があります。たとえば、いくつかの形式の再帰を実装する場合です。この例が気に入らない場合、関数を前方宣言するためにreallyが必要な場合があると仮定します。

Pythonで関数の前方宣言が必要になるこのケースを考慮してください:

def spam():
    if end_condition():
        return end_result()
    else:
        return eggs()

def eggs():
    if end_condition():
        return end_result()
    else:
        return spam()

end_conditionend_resultは以前に定義されています。

コードを再編成し、常に呼び出しの前に定義を置く唯一のソリューションですか?

153
Nathan Fellman

関数を定義したくない場合before使用され、定義するafterwardsは不可能ですが、他のモジュールで定義するのはどうですか?

技術的にはまだ最初に定義しますが、クリーンです。

次のような再帰を作成できます。

def foo():
    bar()

def bar():
    foo()

Pythonの関数は、値が匿名であるのと同じように匿名ですが、名前にバインドできます。

上記のコードでは、foo()はfooという名前の関数を呼び出しません。呼び出しが行われた時点でfooという名前にバインドされている関数を呼び出します。 fooを別の場所で再定義することができ、barは新しい関数を呼び出します。

宣言されていない変数を取得するように求めているため、問題を解決できません。

72
RichN

できることは、呼び出しを独自の関数にラップすることです。

そのため

foo()

def foo():
    print "Hi!"

壊れますが、

def bar():
    foo()

def foo():
    print "Hi!"

bar()

正常に動作します。

Pythonの一般的な規則はnotです。この関数はコード内で(Pascalのように)定義する必要がありますが、使用する前に定義する必要があります。

それが役に立てば幸いです。

97
Vanya

次の手順でスクリプトを開始する場合:

if __name__=="__main__":
   main()

そうすれば、おそらく「前方宣言」のようなことを心配する必要はないでしょう。インタプリタはすべての関数をロードしてから、main()関数を開始します。もちろん、すべてのインポートも正しいことを確認してください;-)

考えてみると、私はpythonで「前方宣言」のようなことを聞​​いたことがない...

82
jldupont

このスレッドを復活させることをおpoびしますが、ここで説明されていない戦略があり、それが適用される可能性があります。

リフレクションを使用すると、前方宣言と同じようなことができます。たとえば、次のようなコードのセクションがあるとします。

# We want to call a function called 'foo', but it hasn't been defined yet.
function_name = 'foo'
# Calling at this point would produce an error

# Here is the definition
def foo():
    bar()

# Note that at this point the function is defined
    # Time for some reflection...
globals()[function_name]()

このようにして、実際に定義される前に呼び出す関数を決定しました。事実上、前方宣言です。 pythonのステートメントglobals()[function_name]()は、上記の理由によりfoo() if function_name = 'foo'と同じです。pythonは呼び出す前に各関数を検索する必要があるためです。それ。 timeitモジュールを使用してこれら2つのステートメントを比較する場合、計算コストは​​まったく同じです。

もちろん、この例は非常に役に立ちませんが、関数を実行するのに必要な複雑な構造を持っているが、前に宣言する必要がある場合(または構造的に後からそれをする意味がほとんどない場合)、文字列と後で関数を呼び出してみてください。

10
KGardevoir

Cmp_configsの呼び出しが独自の関数定義内にある場合は、問題ないはずです。例を挙げましょう。

def a():
  b()  # b() hasn't been defined yet, but that's fine because at this point, we're not
       # actually calling it. We're just defining what should happen when a() is called.

a()  # This call fails, because b() hasn't been defined yet, 
     # and thus trying to run a() fails.

def b():
  print "hi"

a()  # This call succeeds because everything has been defined.

一般に、関数(main()など)内にコードを配置すると、問題が解決します。ファイルの最後でmain()を呼び出すだけです。

10
BJ Homer

いいえ、Pythonで関数を前方宣言する方法はないと思います。

あなたがPythonインタープリターであると想像してください。行に着いたら

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

cmp_configsが何かを知っているか、知らないかのどちらかです。続行するには、cmp_configsを知っている必要があります。再帰があるかどうかは関係ありません。

7
unutbu

pythonには前方宣言のようなものはありません。関数が必要になる前に宣言されていることを確認する必要があります。関数の本体は、関数が実行されるまで解釈されないことに注意してください。

次の例を考えてみましょう。

def a():
   b() # won't be resolved until a is invoked.

def b(): 
   print "hello"

a() # here b is already defined so this line won't fail.

関数の本体は、関数を呼び出すと解釈される別のスクリプトであると考えることができます。

6
Piotr Czapla

アルゴリズムはトップダウンで理解するのが最も簡単な場合があり、全体的な構造から始めて詳細にドリルダウンします。

あなたは前方宣言なしでそうすることができます:

def main():
  make_omelet()
  eat()

def make_omelet():
  break_eggs()
  whisk()
  fry()

def break_eggs():
  for Egg in carton:
    break(Egg)

# ...

main()
5
funroll

ファイル自体をインポートします。ファイルがtest.pyと呼ばれると仮定します:

import test

if __name__=='__main__':
    test.func()
else:
    def func():
        print('Func worked')
3
user10488833

Pythonで関数を前方宣言することはできません。関数を定義する前にロジックを実行している場合、とにかく問題が発生している可能性があります。スクリプトの最後にあるif __== '__main__'にアクションを配置し(自明ではない場合は「main」と名付けた関数を実行することにより)、コードはよりモジュール化され、それを必要に応じてモジュール。

また、リストの内包表記をジェネレーターエクスプレス(つまり、print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))に置き換えます

また、廃止されたcmpも使用しないでください。 keyを使用し、小なり関数を提供します。

3
Mike Graham
# declare a fake function (prototype) with no body
def foo(): pass

def bar():
    # use the prototype however you see fit
    print(foo(), "world!")

# define the actual function (overwriting the prototype)
def foo():
    return "Hello,"

bar()

出力:

Hello, world!
1
Murphy

「この問題が発生しないようにコードを再編成するだけです。」正しい。簡単です。常に機能します。

関数は、参照する前にいつでも提供できます。

「ただし、これはおそらく避けられない場合があります。たとえば、ある種の再帰を実装する場合などです」

リモートでそれがどのように可能かはわかりません。関数を使用する前に定義できない場所の例を提供してください。

0
S.Lott

ちょっと待ってcmp_configsが定義される前に、モジュールがサンプルのprintステートメントに到達したとき、あなたはそれが何を期待しているのですか?

Printを使用した質問の投稿が実際に次のような表現を試みている場合:

fn = lambda mylist:"\n".join([str(bla)
                         for bla in sorted(mylist, cmp = cmp_configs)])

このステートメントを実行する前にcmp_configsを定義する必要はありません。コードの後半で定義するだけで十分です。

ラムダへの引数のデフォルト値としてcmp_configsを参照しようとしている場合、これは別の話です:

fn = lambda mylist,cmp_configs=cmp_configs : \
    "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

この行に到達する前に、cmp_configs変数を定義する必要があります。

[編集-この次の部分は正しくないことが判明しました。関数のコンパイル時にデフォルトの引数値が割り当てられ、後でcmp_configsの値を変更してもその値が使用されるためです。]

幸いなことに、Pythonはそのままタイプに対応しているので、気にしませんwhatcmp_configsとして定義しているので、このステートメントを序文にするとよいでしょう:

cmp_configs = None

そして、コンパイラは幸せになります。 fnを呼び出す前に、必ず実際のcmp_configsを宣言してください。

0
PaulMcG