pythonの参照渡しスタイル(を積極的に利用するコーディング)にケチ付ける記事を書いたんだけど、しばらく経ってから「逆にpythonでCみたいなバリバリの参照渡しするにはどうしたら良いんだ?」という疑問を持った。
たとえば、Cで言うところのこういうもの。
#include<stdio.h> void time_ptr(int *ptr, int n){ *ptr = n*(*ptr); } int main(){ int a; a = 3; printf("%d\n", a); time_ptr(&a, 3); printf("%d\n", a); return 0; }
有名な話だけど、pythonの世界にはmutable(変更可能)なオブジェクトとimmutable(変更不能)なオブジェクトがあり、前者にはリストや辞書、後者にはタプルや文字列、整数などが含まれる。mutableなオブジェクトに対する操作(list.append()とか)は元のオブジェクトを変化させるが、immutableなオブジェクトに対する操作は常に新しいオブジェクトを生成する。そしてpythonのルールとして、すべてのオブジェクトは参照渡しされる。
こうして考えると、mutableなオブジェクトを参照渡しにするのは難しくない(むしろ容易だ。意図せず参照先をいじってしまい、ロジックエラーになって困るくらい)。一方、immutableなオブジェクトを参照渡しして、その値を書き換えたい(そんなシチュエーションがあるの? という疑問は黙殺するとして)場合、かなり困ったことになる。当たり前だ。なにせ、変更不能なのだから。
この問題(というほどじゃないけど)の解決策は、以下の2通り考えられる。
- 任意のオブジェクトidを指定して新規オブジェクトを生成する
- mutableの中に入れて渡してやる
ほんとうは1が実現できたら、面白いと思ったけど。残念ながらpythonで実現する方法は調べても出てこなかった(恐らくそんなものは存在しないと思う。あったら危なっかしくてしょうがないし)。なので素直に2を使うことにする。
上のCのコードは、こう書ける。
def time_ref(ptr, n): ptr[0] *= n def main(): a = [3] print(a[0]) time_ref(a, 3) print(a[0]) if __name__ == '__main__': main()
使い道は特に思いつかないけど、参照渡しにするためだけにリストオブジェクトを生成するのもちょっと不条理な感じでクール? いや、そんなことはないですね。