擬循環参照【第 6 回 Python × Blender】
今回は Blender の Python API の素晴らしい設計の特徴の一つを見たいと思います。
モジュール性・拡張性・自由な抽象化を目指すために、bpy
モジュールは無限のネスト構造が可能となっています。
これが今回紹介したいbpy
モジュールの擬循環参照(pseudo-circular referencing)機能です。
循環参照とは?
本題とは関係ないので興味のない方はここは飛ばしてもらって構わないのですが、そもそも循環参照とは何でしょうか?
循環参照の例としては 2 つのオブジェクトの間でお互いを参照しあっている場合です。例えば以下のような Python コードを見てください。
lhs_list = [] rhs_list = [] lhs_list.append(rhs_list) rhs_list.append(lhs_list)
こうするとlhs_list = [rhs_list]
,rhs_list = [lhs_list]
という風になります。
するとlhs_list[0]
はrhs_list
を参照し、rhs_list[0]
はlhs_list
を参照しているのでここで循環参照が起きています。
こうなるとlhs_list
,rhs_list
共に[[[[...]]]]
と無限にリストが入れ子(ネスト構造)に入ってしまうので、通常はメモリリークを起こしエラーとなります。
ただし Python のガベージコレクタ(使われなくなったオブジェクトのメモリ領域を解放してくれる)はこのような循環参照を起こしているオブジェクトも解放しエラーを防いでくれます。
明示的に__del__
メソッドでオブジェクトのメモリ領域解放条件を指定したクラス以外のオブジェクトで、循環参照によるエラーは Python においては起こらないようです(詳しいことは以下のサイトを参照してください)。
emptypage.jp
bpy の擬循環参照
ここからが今日の本題です。
以下の Python スクリプトを Blender の Text Editor で実行してみてください(Blender で Python スクリプトを使う方法は以下の記事を参考にしてください)。
tamaki-py.hatenablog.com
import bpy print(id(bpy.data.objects.data)) print(id(bpy.data.objects.data.objects.data)) print(id(bpy.data.objects.data.objects.data.objects.data)) print(id(bpy.data.meshes.data)) print(id(bpy.data.meshes.data.objects.data)) print(id(bpy.data.meshes.data.objects.data.scenes.data.worlds.data.materials.data))
ターミナルを見ると実行結果は以下のようになり、
bpy.data.objects.data
bpy.data.objects.data.objects.data
bpy.data.objects.data.objects.data.objects.data
bpy.data.meshes.data
bpy.data.meshes.data.objects.data
bpy.data.meshes.data.objects.data.scenes.data.worlds.data.materials.data
これらのメモリアドレスは全て同じで、これらは全て同一のオブジェクトを示しているということが確認できます。
4664398088 4664398088 4664398088 4664398088 4664398088 4664398088
この結果は Blender Python API の強力な特徴の一つを示しています。
どういうことかと言いますと、Blender オブジェクトに対しドットシンタックスで.data
という風に付け加えると、これはそのオブジェクトの親のデータブロック(parent datablock)への参照を返すということです。
これによりbpy
モジュールをいちいち呼び出さずにオブジェクトをビルドしたり操作できます。