Pythonにポインタはないですが似たようなことはできます

672, 2023-06-01

目次

Pythonにポインタってないんですか?

ないです。
C系列の言語などはポインタという機能がありますがPythonにはありません。
しかしポインタに近いことはできます。

この記事ではPythonのポインタ(みたいな処理)について具体的に解説します。

ポインタの動作と振る舞い

Pythonのコードをやる前にポインタの動作と振る舞いを確認しておきましょう。
C言語などではポインタというのは変数の値を間接的に書き換えるときに使われます。

#include <stdio.h>

int main(void) {
    int a = 1;  // 変数a
    int *p = &a;  // 変数aのアドレスをポインタ変数pに入れる

    *p = 2;  // ポインタ変数の実体(変数a)を書き換える

    printf("a = %d\n", a);  // a = 2

    return 0;
}

上記のようにポインタ変数を使うと別の変数のアドレスを保持し、そのアドレスにアクセスして変数の値を書き換える、ということができます。
この機能はデニス・リッチーが開発した機能で今日でもGo, Rustなど最新の言語で使われている機能です。

要はポインタは「間接的に別の変数を参照/書き換える」という機能ですね。
これがPythonでも実現できないか? というところです。

Pythonのリストを使ってポインタにチャレンジ

リストを使うと間接的な変数の参照を保持することができます。
試しに以下のコードを実行してみます。

a = 1  # 書き換え対象の変数
p = [a]  # リストでaの参照を保持

p[0] = 2  # 書き換え?

print(a)  # 1(変わっていない)

期待する動作はa == 2ですが結果はaの値は変わらずです。
なぜなんでしょうか?
これを明らかにするには変数のidを見ます。

a = 1  # 書き換え対象の変数
print('id(a)', id(a))  # id(a) 2148251730160

p = [a]  # リストでaの参照を保持

print('id(p[0])', id(p[0]))  # id(p[0]) 2148251730160
p[0] = 2
print('id(p[0])', id(p[0]))  # id(p[0]) 2148251730192

print(a)  # 1(変わっていない)

上記のコードを実行すると以下の結果になります。
id()の値は環境ごとに違います)

$ python main.py
id(a) 2148251730160
id(p[0]) 2148251730160
id(p[0]) 2148251730192
1

あら、なんとp[0] = 2の直後を見るとp[0]id()の値が変わってますね。
ちなみにid()というのはオブジェクトのアイデンティティを確認するための関数です。
この関数を使うとそのオブジェクトの一意なIDを得ることができます。
つまりIDが違っているということは別のオブジェクトになっているということです。

代入によって別のオブジェクトがp[0]に代入されてるわけですね。

Pythonでポインタみたいなことはできないのか?

さきほどのようにリストを使っても変数のアドレスは代入時に変わってしまいポインタのような振る舞いをさせることはできませんでした。
しかしintの変数ではなくリストを使えば似たようなことはできます。

a = [1]  # リストで整数をラップ
b = a  # bにaの参照を入れる

b[0] = 2  # bから間接的にaの要素を書き換える

print(a[0])  # 2

リストは代入時に参照が変数に代入されるため、その参照を使えば間接的に他のリストの要素を変えることができます。
これを応用して関数にポインタを渡す場合を考えてみます。

def func(arg: list):
    arg[0] = 2  # リストの要素を書き換え


a = [1]  # 整数をリストでラップ
func(a)  # 関数にリストを渡す

print(a[0])  # 2(書き変わってる)

上記のように関数にもリストを渡せば間接的にリスト内の要素を関数内で変更することができます。
このようにPythonではポインタに近いことをやりたいときはリストを使います。



この記事のアンケートを送信する