4

私は変数のスコープについて学んでおり、次の Python コードを見たときにいくつかのスレッドを調べていました。

a = 1
b = 2
c = 3

def foo():

    print a
    print b
    print c
    c = c + 1

def main():

    foo()

main()

と を出力し1 2ますUnBoundLocalError: local variable 'c' referenced before assignment。これをJavaに翻訳すると

public class Test1 {

static int a = 1;
static int b = 2;
static int c = 3;

public static void foo()
{
    System.out.println(a);
    System.out.println(b);
    System.out.println(c);
    c = c + 1;
}   

public static void main(String[] args)
{
    foo();
}   
}

プリントアウトし1 2 3ます。私はそれを正しく翻訳したと確信しています(そうでない場合は非常に恥ずかしいです)。私の質問は、Java ではエラーが発生しないのに Python ではエラーが発生するのはなぜですか? それは、異なるスコープや、それらが解釈およびコンパイルされる方法と関係がありますか?

4

4 に答える 4

3

Python を学習している多くの人々 (「割り当て前に参照」式を使用して stackoverflow.com で調査を行ってください) のように、あなたが持っていた理解不能と驚きは、ドキュメンテーションが時々ひどく書かれているという事実によるものです。

このエラーの説明は次のとおりです。

コード ブロック内の任意の場所で名前バインディング操作が発生した場合、ブロック内の名前のすべての使用は、現在のブロックへの参照として扱われます。これは、名前がバインドされる前にブロック内で使用されると、エラーを引き起こす可能性があります。このルールは微妙です。Python には宣言がなく、コード ブロック内の任意の場所で名前バインディング操作を実行できます。コード ブロックのローカル変数は、ブロックのテキスト全体をスキャンして名前バインディング操作を行うことによって決定できます。

http://docs.python.org/2/reference/executionmodel.html

私の意見では、この抜粋は、コードが実行されたときに実行されることをうまく表現していません。「スキャンによって判断できる
」と言うのは欺瞞であり、このスキャンはオプションであるという印象を与えます。

この点について私の意見を確認するようなものを読んだことはありませんが、個人的には次のように考えています:
- 実際、このスキャン常に実行されますが、それはオプションではありません
- さらに重要なことに、このスキャンは呼び出し可能なオブジェクトの呼び出しの前に行われますブロックが定義していること

.

実際、最初に理解しておくべき重要な概念があります。

呼び出し可能なオブジェクトの定義は、オブジェクトが呼び出される前に行われます

「定義」という用語は、次の 2 つの方法で理解できるため、あいまいであることを認識する必要があります
。1/ 定義 = 何かを「定義」するスクリプト内のコード ブロック
2/ 定義 = 実行時のこのコード ブロックの実行定義された呼び出し可能オブジェクトを作成するスクリプトの

私はこれらの主張に基づいています:

ブロックは、ユニットとして実行されるPython プログラムテキスト一部です。ブロックとは、モジュール、関数本体、およびクラス定義です。

http://docs.python.org/2/reference/executionmodel.html

.

関数定義は、ユーザー定義の関数オブジェクトを定義します (...)
関数定義 [センス 1] は、実行可能なステートメントです。その実行は、現在のローカル名前空間の関数名を関数オブジェクトにバインドします (...)関数定義 [センス 2] は関数本体を実行
しません。これは、関数が呼び出されたときにのみ実行されます。

http://docs.python.org/2/reference/compound_stmts.html#function-definitions

.

A function definition defines a user-defined function object: とても美しいトートロジー! この文は何も説明していません。
[センス 1] では、「定義」は「定義するコード ブロック (=テキスト)」を意味します[
センス 2] では、「定義」は「定義するコード ブロックの実行」を意味します。テキスト (定義の意味 1) は何も実行せず、テキストとして受動的に嘘をつきます...

「定義」という名前があいまいで、ドキュメントが時々ひどく書かれていることがわかります.....

最後の抽出は関数定義に関するものですが、その概念は明らかにクラスや他の呼び出し可能なオブジェクトに拡張できます。クラスはコード ブロックによっても定義され、定義 (センス 2 = 定義するコード ブロックの実行) と呼び出しの 2 つのステップが存在します。

.

したがって、私の主張は、呼び出し可能なオブジェクト内の識別子のスキャンとそのスコープの決定は、コード ブロック [= 定義センス 1] が実行される瞬間に実行されると考えるように確立されているということです。この実行は、いわゆる "定義」[センス2]も。
それが私の意見の重要なポイントとして指摘したかったことです。

.

PS: ドキュメントの上記の抜粋での「変数」という用語の使用は、嘆かわしいことです。「変数」は、Python で使用される場合、別の非常にあいまいな用語だからです。
それが嘆かわしいことの証拠は、OPがJavaで何が起こるか、Pythonで何が起こるかを比較して彼の質問を提示することです。
基本的な公式ドキュメントのどこかに、Python ではコーダーが「コンテンツが変更される可能性のあるメモリのチャンク」として機能するエンティティにアクセスできないという事実の確固たる説明があった場合、この種の混乱はめったに起こらないはずです。
でもそれはまた別の話

于 2013-07-13T05:17:04.193 に答える
1

Python は変数のローカル スコープをチェックし、ローカル スコープで宣言または参照されていない場合は、より高いスコープを検索します。関数で使用c=c+1することにより、Python はcローカル スコープで参照し、宣言されていないため印刷しようとするとエラーをスローします。を削除するc=c+1と、c と出力されます。期待する動作を取得するにはglobal c、関数内に配置します。

注:通常、グローバル変数を使用することはお勧めできません。そのため、pythonic の代替手段として、変数を関数の引数として渡すか、実行していることがクラスに適している場合は、変数をselfにします。

例えば

class myclass:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3

    def count(self):
        print self.a
        print self.b
        print self.c
        self.c = self.c + 1

def main():
    thing = myclass()
    thing.count()
    thing.count()

main()

与える

nero@ubuntu:~/so$ python -i so.py 
1
2
3
1
2
4
>>> 
于 2013-07-13T03:10:40.257 に答える
1

Python はインタープリタ言語ですが、関数スコープ全体を分析することに混乱があると思います。例を参照してください。

>>> a=1;b=2;c=3
>>> def foo():
...     print a, b, c #c refers to the c in the outer scope
... 
>>> foo()
1 2 3
>>> 
>>> def foo():
...     print a, b, c #c refers to the local c defined later
...     c = 2
... 
>>> foo()
1 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'c' referenced before assignment
>>>

Python スコープ ルールについては、LEGBを参照してください。

于 2013-07-13T03:44:07.603 に答える