pythonは解釈言語です!

5662 ワード

コードブロックの詳細(必見)
コードブロックは、pythonコードのセグメントを1つのユニットとして、全体として実行することができる.以下は公式マニュアルの説明です.
A Python program is constructed from code blocks. A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition. Each command typed interactively is a block. A script file (a file given as standard input to the interpreter or specified as a command line argument to the interpreter) is a code block. A script command (a command specified on the interpreter command line with the ‘-c’ option) is a code block. The string argument passed to the built-in functions eval() and exec() is a code block.

したがって、次のタイプのコードブロックがあります.
  • モジュールファイルは、コードブロック
  • である.
  • 関数体は、コードブロック
  • である.
  • classの定義はコードブロック
  • である
  • インタラクティブ(python idle)の各コマンドラインは、独立したコードブロック
  • である.
  • スクリプトファイルは、コードブロック
  • です.
  • スクリプトコマンドは、コードブロック(python-c「xxx」)
  • です.
  • eval()とexec()の内容もそれぞれのコードブロック
  • がある.
    コードブロックの役割は、コードを組織することであり、コードブロック範囲を終了すると、役割ドメイン範囲を終了することを意味する.たとえば、関数ブロックを終了すると、関数の役割ドメインが終了し、関数内のローカル変数が関数の外部にアクセスできなくなります.
    また、pythonは解釈言語で、1行を読んで1行を解釈します.これは、1行を読むたびに前の行を忘れることを意味します.しかし、実際には、より厳密には、1つのコードブロックを読んで1つのコードブロックを解釈することであり、これは、コードブロックの内容を読むときに、このコードブロックの中で読んだ内容に属していることを一時的に覚え、完全なコードブロックを読んでから、このコードブロックを統一的に解釈することを意味する.
    まず、pythonのインタラクティブツールidleツールでのみテストできるコードブロックに属する行を読む場合について説明します.
    >>> x=2000
    >>> y=2000
    >>> x is y
    False
    >>> x=2000;y=2000
    >>> x is y
    True

    理論的にはセミコロンは文の区切り記号であり、結果には影響しません.しかし、なぜ最初のx is yがFalseで、2番目のx is yがTrueなのか.
    最初のx is yを分析した.インタラクティブツールidleの各コマンドは個別の文ブロックであるため、x=2000を説明するとすぐに2000という数値オブジェクトを忘れ、x変数自体も忘れてしまう.次に、説明y=2000を読み出します.さっき説明したx=2000を覚えていないので、メモリに2000という数値を保存するための数値構造を再作成し、yで指します.言い換えれば、xとyが指す2000はメモリ内で異なるデータオブジェクトであるため、x is yはFalseである.
    次のx is yはTrueを返します.
    >>> x=2000;y=2000
    >>> x is y
    True

    pythonは行で説明されるため、コマンドはコードブロックです.x=2000;y=2000の場合、pythonはまずこの行全体を読み出し、xとyの数値オブジェクトが2000であることを発見し、x,y=2000,2000に等価な簡単な最適化を行います.これは、コードブロック内に属し、2000であるため、メモリにデータオブジェクトが作成され、xとyがこのデータオブジェクトを参照することを意味します.したがって、x is yはTrueを返します.
    idleツールの各コマンドは独立したコードブロックですが、pyファイルは完全なコードブロックであり、関数、exec()などの他のコードブロックをネストすることもできます.したがって、上記の行賦値文がpyファイルに格納されている場合、結果はTrueになります.
    例:
    x = 2000
    y = 2000
    print(x is y)   # True
    def f1():
        z=2000
        z1=2000
        print(x is z)   # False
        print(z is z1)  # True
    
    f1()

    pythonはまずx=2000を読み出し、メモリにグローバル役割ドメインに属する2000データオブジェクトを作成し、y=2000を解釈すると、このグローバルオブジェクト2000はすでに存在していることに気づいた(xとyは同じグローバルコードブロック内にあるため)、新しい2000ペアは追加的に作成されない.ここで反映される結果は、「同じコードブロック内では、依然として1行の解釈1行を読むが、このコードブロックを終了する前に、このコードブロックの内容を忘れず、このコードブロックを統一的に手配する」ということである.
    同理def f1()内のコードブロックは、zがローカル役割ドメインの変数であり、より標準的には異なるコードブロック内にあるため、ローカル役割ドメインメモリ領域に新しいデータオブジェクト2000が作成されるため、x is zはFalseを返す.前述の説明によれば、z1 is zはTrueを返す.
    前文で複数回発生した異常をレビューします.
    x = 3
    def f1():
        print(x)
        x=4
    f1()

    エラーメッセージ:
    UnboundLocalError: local variable 'x' referenced before assignment

    def文が実行されると、defは関数を宣言し、関数体はコードブロックであるため、このコードブロックに属する内容をコードブロックに従って読み出す.まずprint(x)を読みますが、直接説明するのではなく、それを覚えて下に読み続け、x=4を読みます.これは、xがローカル変数であることを意味します.次に、コードブロック全体を統一的に配置し、print(x)のxをグローバル変数ではなくローカル変数と見なす.なお、defが終了するまでxの付与は行われず、ローカル変数xが記録され、付与操作は関数呼び出し時に行われる.関数f()を呼び出すとprint(x)のxはローカル変数であるが、まだ値が付与されていないため、エラーが報告される.
    でも下を見ると、どうしてまたTrueに戻ったの?
    >>> x=256
    >>> y=256
    >>> x is y
    True

    Pythonは起動時にメモリに通常の小さな整数値(-5~256)のオブジェクトを事前に作成しているため、非常に頻繁に使用されている(pythonの内部で使用されているものもある).したがって、この範囲内の整数は直接参照され、メモリに新しい数値オブジェクトは追加的に作成されないため、x is yは常にtrueを返します.さらに、これらの小さな整数は、役割ドメインにまたがることができます.
    x = 3 
    def f1():
        y=3
        print(x is y)   # True
    
    f1()

    前文ループ内の関数の問題を見てみましょう.
    def f1():
        for i in range(5):
            def n():
                print(i)
        return n
    
    f1()()

    前述の現象について説明したように,内部関数n()におけるprint(i)のiはループの反復に伴って変化せず,固定値i=4である.
    pythonはまずdef f1()のコードブロックを説明し、このコードブロックの役割ドメイン内に属する変数iおよびnを記録するが、iおよびnは値を付与しない.すなわち、変数nが関数変数であることは一時的に知られていない.
    同様に、def n()コードブロックを解釈する必要がある場合、このコードブロックに関連する変数iは、この変数iが外層関数に属するにすぎないが、いずれにしても、このコードブロックはiを記憶し、外部関数の役割ドメインであることを記憶する.
    なお、関数の宣言中は、変数iに関連するすべての役割ドメイン内でiを付与することはなく、このi変数名が保存されているだけで、関数が呼び出されたときにのみ付与操作が行われる.
    f 1()の呼び出しが開始されると、関数体のコードの実行が開始され、ループ反復が開始され、関数n()が複数回宣言され、反復のたびに生成されたn()は、元に記録された変数nをこの新しい宣言された関数体(付与された動作に相当し、変数nが参照するオブジェクトは、一般的なデータオブジェクトではなく、関数体構造であるにすぎない)に指し示す.ループで関数n()を宣言するだけで呼び出しは行われないため、n()のiには値付け操作は行われません.また、ループ反復のたびに変数nが新しい関数体を指し、以前の反復中に定義された関数が破棄(上書き)されるため、最終的には最後のループ時に宣言された関数n()のみが記憶され、i=4となる.
    f 1()()を呼び出すと、f 1()を呼び出すと返される関数n()が呼び出され、そのときになってn()内のiに値が付与され、値が付与されるとその外層関数f 1()の役割ドメインが検索され、この役割ドメイン内のiがメモリ内の数値4を指していることがわかり、最終出力4が出力される.
    次のコードを見てください.
    def f1():
        for i in range(5):
            def n():
                print(i)
            n()
        return n
    
    f1()

    出力結果:
    0
    1
    2
    3
    4

    f 1()を呼び出すと、ループの反復が実行され、反復するたびにn()が呼び出され、反復するたびにn()のiが付与されることを意味する.
    なお、前述したように、関数のデフォルトパラメータは、関数宣言時に付与されるので、以下のリストLの各要素が表す関数は、変数iが異なる数値オブジェクトを指す.
    def f1():
        L = []
        for i in range(5):
            def n(i=i):
                print(i)
            L.append(n)
        return L
    
    f1()[0]()
    f1()[1]()
    f1()[2]()
    f1()[3]()
    f1()[4]()

    実行結果:
    0
    1
    2
    3
    4