Pythonとポインタについて

5093 ワード

0はじめに
ポインタ(Pointer)はC、C++およびJava、Goなどの言語の非常に核心的で重要な概念であり、リファレンス(Reference)はポインタに基づいて構築された同様に重要な概念である.
ポインタはどのプログラミング言語にとっても必須で重要であり、Pythonはポインタという概念を意図的に曖昧に制限しているが、ポインタはPythonにとって依然として深く議論しなければならない話題である.
本稿では,C++とPythonに基づいて,Pythonにおけるポインタおよび参照に関連するいくつかの挙動について議論した.
1ポインタとは?なぜポインタが必要なの?
ポインタには2つの意味があります.
  • は、整形ポインタタイプ、ポインタポインタタイプ
  • などのデータ型のポインタタイプを指す.
  • は、メモリアドレスが格納変数、すなわちポインタ変数
  • を指す.
    ポインタのこの2つの意味は、変数として、ポインタによってメモリアドレスを取得することができ、このアドレスの値にアクセスする準備ができています.1つのタイプとして、メモリアドレスの正しいオフセット長が決定され、現在のタイプの単位メモリサイズに等しい必要があります.ポインタタイプ、すなわちvoid*が欠けている場合、メモリアドレスは保存されていますが、これは1つの始点アドレスにすぎません.ポインタは、始点から後ろへのオフセット量が分からないため、解ポインタ操作を拒否します.ポインタにアドレス、nullptrが欠けている場合、特定の場所のメモリを読み取ることはできません.
    ポインタの意味は主に以下の点です.
  • は、malloc、new、allocator等により取得する動的メモリ
  • を担持する.
  • pass-by-pointerを可能にする
  • pass-by-pointerのメリットには、以下の点が含まれますが、これらに限定されません.
  • 実パラメータの無意味な値コピーを回避し、効率を大幅に向上させる
  • .
  • は、ある変数に対する修正能力を変数自身の作用域
  • に限定するものではない.
  • は、swap、移動コンストラクタ、移動付与演算などの操作を、データ構造内部のポインタのみに対して操作することができるため、一時的なオブジェクト、移動後のソースなどのオブジェクトの全体的なメモリ操作
  • を回避することができる.
    このことから,ポインタに関連する各操作はプログラミングに必須であるか,あるいは非常に重要であることが分かる.
    2 C++での参照
    C++では,参照はポインタに似た性質を持つが,よりステルスで厳密である.C++の参照は、次の2つに分けられます.
    2.1左参照
    左の値は、初期化フェーズが左にバインドされ、再バインドは存在しません.左値参照は、バインドされた左値とほぼ同じ性質を有し、decltype宣言とは唯一の違いがあります.
    int numA = 0, &lrefA = numA;  // Binding an lvalue
    cout << ++lrefA << endl;      // Use the lvalue reference as lvalue & rvalue
    decltype(lrefA) numB = 1;     // Error!

    左の参照はpass-by-referenceでよく使用されます.
    void swap(int &numA, int &numB)
    {
        int tmpNum = numA;
        numA = numB;
        numB = tmpNum;
    }
    
    int main()
    {
        int numA = 1, numB = 2;
        swap(numA, numB);
        cout << numA << endl << numB << endl;  // 2 1
    }

    2.2右参照
    右の値は、構造関数の移動や割り当て操作の移動によく使用される初期化フェーズから右の値にバインドされます.これらの場合、移動コンストラクション関数と移動付与操作は、右の値参照によって被移動オブジェクトを引き継ぐ.
    右の値の参照は、本明細書の内容とは無関係であるため、ここでは詳細に説明しない.
    3 Pythonでの参照
    3.1 Pythonリファレンスが存在しない
    上記の議論から分かるように、「引用」はPythonにとって非常によく使われる用語であるが、これは明らかに不正確である.Pythonにはバインド操作が存在しないため、左参照は存在せず、右参照も存在しない.また、「可変オブジェクト(mutable object)」と「可変オブジェクト(immutable object)」という用語も正確ではない.
    3.2 Pythonのポインタ操作
    Pythonは参照していないが,その変数の挙動とポインタの挙動には高度な類似性があり,これは主に以下の点に現れていることが明らかになった.
  • は、いずれの場合(賦値、実パラメトリック伝達などを含む)にも明示的な値コピーが存在しないが、この場合、1回の参照カウント
  • のみが増加する.
  • 変数は、(トップレベルconst(top-level const)を含まないポインタに対応する)
  • に再バインドすることができる.
  • は、場合によっては(以下、この問題について詳細に説明する)、元の値
  • を関数実パラメータで修正することができる.
    このことから、Python変数は、参照変数ではなくポインタ変数に似ていることがわかります.
    3.2.1コンストラクタ戻りポインタ
    Pythonの記述には「すべてが対象」というよくある言葉がある.しかし、この言葉では、オブジェクトはポインタや参照ではなく値であるという重要な事実がよく無視されています.だから、この言葉の正確な説明は「すべて(ある種の欠けた)ポインタ」に訂正すべきで、修正後の説明は抽象的だが、これはもっと正確だ.
    オブジェクトはコンストラクション関数から来ているため、Pythonのコンストラクション関数は匿名のオブジェクトを構築し、そのオブジェクトのポインタを返すことがわかります.これはPythonとポインタの最初の重要なつながりです.
    コードの説明は次のとおりです.
    Pythonコードの場合:
    sampleNum = 0

    C++コードに似ていません.
    int sampleNum = 0;

    次のようになります.
    int __tmpNum = 0, *sampleNum = &__tmpNum;

    または、
    shared_ptr sampleNum(new int(0));

    3.2.2 __setitems__操作は暗黙的にポインタを解く
    Pythonとポインタのもう一つの重要な関連はPythonの暗黙的な解ポインタ挙動にある.Pythonでは明示的な解ポインタ操作は存在しないが、(存在し、かつ唯一である)_setitems__操作は暗黙的な解ポインタを行い、この方法で変数を修正することは、解ポインタ操作によって変数の元の値を変更することに等しい.この性質は、次のことを意味する.
  • 関連なしsetitems__の操作はいずれもポインタ再バインド
  • となる.
    Pythonコードの場合:
    numList = [None] * 10
    
    # Rebinding
    numList = [None] * 5

    これは次のようなものです.
    int *numList = new int[10];
    
    // Rebinding
    delete[] numList;
    numList = new int[5];
    
    delete[] numList;

    従ってnumListに対する非_setitems__操作によりnumListが新しいポインタにバインドされます.
  • 関連情報setitems__の操作はいずれも解針操作
  • となる.
    Pythonのハッシュ・テーブルへの依存度が高いため、「__setitems__に関する操作」はPythonにおいて実際には非常に広範な動作であり、主に以下を含む.
    (1)配列に対するインデックス操作
    (2)ハッシュテーブルの検索操作
    (3)_settattr__に関する動作(Pythonがハッシュテーブルにattributeを格納しているため、_settattr__動作は最終的に何らかの_settitems_動作となる)
    少し複雑な例で説明します.
    次のPythonコードの場合:
    class Complex(object):
    
        def __init__(self, real = 0., imag = 0.):
    
            self.real = real
            self.imag = imag
    
    
        def __repr__(self):
    
            return '(%.2f, %.2f)' % (self.real, self.imag)
    
    
    def main():
    
        complexObj = Complex(1., 2.)
    
        complexObj.real += 1
        complexObj.imag += 1
    
        # (2.00, 3.00)
        print(complexObj)
    
    
    if __name__ == '__main__':
        main()

    これは次のようなものです.
    class Complex
    {
    public:
        double real, imag;
        Complex(double _real = 0., double _imag = 0.): real(_real), imag(_imag) {}
    };
    
    ostream &operator<real++;
        complexObj->imag++;
    
        cout << *complexObj << endl;
    
        delete complexObj;
    
        return 0;
    }

    このことから,int,floatのような簡単なPythonタイプでも,我々がカスタマイズしたクラスでも,newを用いてオブジェクトを構築しポインタを返すのと同様の構造挙動を示した.Pythonでは「.」「[]」と「[]」の操作は、ポインタに対する「->」または「*」のポインタ解除操作に似ています.
    4後記
    本稿では,Python変数とポインタ,参照の2つの概念の関係を検討し,主に「Pythonには参照が存在しない」と「Python変数の挙動が何らかの欠落したポインタに類似している」という2つの論点を論証した.すべての論点は著者の個人的な観点であり、誤りがあれば、恭しく指摘する.
    桜雨楼2019.7蘇州