クラスをカスタマイズしちゃうよ

     pythonのクラスネタです。ちょいちょい忘れるんで備忘録っす。
     Pythonでカスタムクラスを作った場合、そのカスタムクラスのさまざまな挙動をカスタマイズする事が出来ます。
     今回はサンプルとして、ついでにOpenMayaも取り入れつつ、やってみました。

     まずはこんな感じ。
    # -*- coding:utf-8 -*-
    from maya import OpenMaya
    
    class Node(object):
        def __init__(self, name):
            # 引数nameで与えられた名前をMObjectとして登録する。
            selectionlist = OpenMaya.MSelectionList()
            self.mobject = OpenMaya.MObject()
            selectionlist.add(name)
            selectionlist.getDependNode(0, self.mobject)
    
        def node(self):
            # 登録されたMObjectからMFnDependencyNodeを返す。
            return OpenMaya.MFnDependencyNode(self.mobject)
    
     上記のクラスは、呼び出し時にMayaのシーンに存在するノード名を指定すると、そのMobjectを保持するクラスです。
     さらにnodeメソッドによって上記MObjectをベースとしたMFnDependencyNodeを返すように、nameメソッドで現在のノード名を返すようにしています。
     maya.cmdsの時と違ってノードを名前ベースではなくMObjectで保持しているので、そのノードの名前を変更しても変更後の名前をちゃんと取ってこれます。
    from maya import cmds
    # Transformノードを作って、その名前をNodeクラスへ渡す。
    trs = Node(cmds.createNode('transform', n='test'))
    
    # 作成したノードをリネームする。
    cmds.rename(trs.name(), 'newTransform')
    
    print(trs.name())
    # Result : newTransform ← ちゃんと新しい名前になっている。
    
     こんな感じ。

     これはこれで便利なんですが、これだとノード名にアクセスする場合に毎回毎回nameメソッドを呼ばないといけませんよね。
     それじゃぁちょっと不便・・・って事で、これから挙動をカスタマイズして行きまっせ!!



    ■通常の取り扱いが文字っぽい感じになるようにカスタマイズする! ー __repr__

     まずは文字列っぽい感じに取り扱ってもらえるようにカスタマイズしてみましょう。
     そんな時は__repr__メソッドを上書きします。
    # -*- coding:utf-8 -*-
    from maya import OpenMaya
    
    class Node(object):
        def __init__(self, name):
            # 引数nameで与えられた名前をMObjectとして登録する。
            selectionlist = OpenMaya.MSelectionList()
            self.mobject = OpenMaya.MObject()
            selectionlist.add(name)
            selectionlist.getDependNode(0, self.mobject)
    
        def node(self):
            # 登録されたMObjectからMFnDependencyNodeを返す。
            return OpenMaya.MFnDependencyNode(self.mobject)
    
        def name(self):
            # ノード名を返す。
            return self.node().name()
    
        def __repr__(self):
            return self.name()
    
     __repr__メソッドで任意の文字を返すようにすると、Pythonではこれを”オブジェクトの公式な文字列”として取り扱ってくれるようになります。
     これでプリントした時などに、ノード名が表示されるようになったり、cmdsのコマンドに渡した時にも文字のように扱ってくれるようになります。
    trs = sample.Node(cmds.createNode('transform', n='testB'))
    # 作成したノードをリネームする。
    cmds.rename(trs, 'newTransform') # ← 今回はnameメソッドを呼ばなくてもOK!
     
    print(trs)
    # Result : newTransform1 ← ちゃんと新しい名前になっている。
    
     おぉ〜、便利〜ヾ(。>﹏<。)ノ゙✧*。


    ■比較された時の挙動を偽造するゾ!! ー __eq__

     さてさて、__repr__のお陰で普段使いの時は文字っぽくなりましたが、まだまだ偽装は足りません。コイツはあくまでNodeクラスなのです。
     例えば上記の例で言えば変数trsは現在"newTransform1"と言う文字であるように偽装していますが、本当は自作クラス”Node”なのです。
     従ってこう言うことをするとウソがバレます。
    # プリントしてみる。
    print(trs)
    # Result : newTransform1 #
    
    # 比較してみる。
    trs == 'newTransform1'
    # Result: False # 
    
     そう、比較されてしまうとtrsはNodeクラスであって”newTransform1”と言う文字ではないでFalseになってしまうのです。
     これも普段使いする場合は面倒ですねぇ・・・。cmdsは文字列である事を前提にした取り扱いなので、こう言うケースは多々発生してしまいます。
     そんな時は__eq__メソッドを上書きするのだ!!
    # -*- coding:utf-8 -*-
    from maya import OpenMaya
    
    class Node(object):
        def __init__(self, name):
            # 引数nameで与えられた名前をMObjectとして登録する。
            selectionlist = OpenMaya.MSelectionList()
            self.mobject = OpenMaya.MObject()
            selectionlist.add(name)
            selectionlist.getDependNode(0, self.mobject)
    
        def node(self):
            # 登録されたMObjectからMFnDependencyNodeを返す。
            return OpenMaya.MFnDependencyNode(self.mobject)
    
        def name(self):
            # ノード名を返す。
            return self.node().name()
    
        def __repr__(self):
            return self.name()
    
        def __eq__(self, other):
            return self.name() == other
    
     これで偽装完了!!
     比較される時には__eq__メソッドが呼ばれるので、その時に比較したい内容(今回はノード名)と比較対象(比較対象は第2引数otherに入っている)の比較結果をbool型で返してあげればOKなわけです。


    ■辞書のキーになるぞ!! ー __hash__

     今度はNodeクラスを辞書型のキーにした場合の話。
    # 三度newTransform1をNodeに使用。
    trs = sample.Node('newTransform1')
    
    # 辞書のキーとしてtrsを使用してみる。
    d = {trs : 'transformNode'}
    
    # こっちは当然OK!
    d[trs]
    # Result: 'transformNode' # 
    
    # でも、文字列をキーにしてみてもダメポ・・・
    d['newTransform1']
    # Error: KeyError: file  line 1: newTransform1 # 
    
     こんな感じで辞書のキーにしてみたものの、アクセスする時はノード名でアクセスしてもダメなのです。
     そんな時は__hash__メソッドを上書き!!
    # -*- coding:utf-8 -*-
    from maya import OpenMaya
    
    class Node(object):
        def __init__(self, name):
            # 引数nameで与えられた名前をMObjectとして登録する。
            selectionlist = OpenMaya.MSelectionList()
            self.mobject = OpenMaya.MObject()
            selectionlist.add(name)
            selectionlist.getDependNode(0, self.mobject)
    
        def node(self):
            # 登録されたMObjectからMFnDependencyNodeを返す。
            return OpenMaya.MFnDependencyNode(self.mobject)
    
        def name(self):
            # ノード名を返す。
            return self.node().name()
    
        def __repr__(self):
            return self.name()
    
        def __eq__(self, other):
            return self.name() == other
    
        def __hash__(self):
            return self.name().__hash__()
    
     Nodeクラスの__hash__メソッド内で、ノード名(文字列)の__hash__メソッドを返してあげれば偽装完了なわけです。ちょっとややこしい・・・けど、これでまた一つ便利になった?

    ■足し算をカスタマイズ! ー __add__

     まだまだあります。それは・・・足し算だぁぁ!!
     よく変数に入れたノード名とアトリビュート名をくっつけることってありますよね!そんな時は__add__メソッドを上書きしましょう。
    # -*- coding:utf-8 -*-
    from maya import OpenMaya, cmds
    
    class Node(object):
        def __init__(self, name):
            # 引数nameで与えられた名前をMObjectとして登録する。
            selectionlist = OpenMaya.MSelectionList()
            self.mobject = OpenMaya.MObject()
            selectionlist.add(name)
            selectionlist.getDependNode(0, self.mobject)
    
        def node(self):
            # 登録されたMObjectからMFnDependencyNodeを返す。
            return OpenMaya.MFnDependencyNode(self.mobject)
    
        def name(self):
            # ノード名を返す。
            return self.node().name()
    
        def __repr__(self):
            return self.name()
    
        def __eq__(self, other):
            return self.name() == other
    
        def __hash__(self):
            return self.name().__hash__()
    
        def __add__(self, other):
            # 自分に何かを足す場合の処理。
            return self.name() + other
    
        def __radd__(self, other):
            # 自分を何かに足す場合の処理。
            return other + self.name()
    
    
     これで足し算されてもOKになりました〜。
     因みに足し算などの拡張の場合、自分に何かが足される場合の__add__と、自分を何へ足す場合の__radd__の2パターンがありますので注意!!


    ■他にもアイディア次第で色んな便利機能が出来るかも!?

     Pythonのクラスではコレ以外にもたくさんカスタマイズできる項目があります。多すぎてとても全てを紹介しきれいないので、もっと詳しく知りたい方は本家の3. データモデル(原文)を見てみましょ〜

     最後に、こんな事も出来るよカスタマイズ!って事で終わりにしたいと思います。
    # -*- coding:utf-8 -*-
    from maya import OpenMaya, cmds
    
    class Node(object):
        def __init__(self, name):
            # 引数nameで与えられた名前をMObjectとして登録する。
            selectionlist = OpenMaya.MSelectionList()
            self.mobject = OpenMaya.MObject()
            selectionlist.add(name)
            selectionlist.getDependNode(0, self.mobject)
    
        def node(self):
            # 登録されたMObjectからMFnDependencyNodeを返す。
            return OpenMaya.MFnDependencyNode(self.mobject)
    
        def name(self):
            # ノード名を返す。
            return self.node().name()
    
        def __repr__(self):
            return self.name()
    
        def __eq__(self, other):
            return self.name() == other
    
        def __hash__(self):
            return self.name().__hash__()
    
        def __add__(self, other):
            # 自分に何かを足す場合の処理。
            return self.name() + other
    
        def __radd__(self, other):
            # 自分を何かに足す場合の処理。
            return other + self.name()
    
        def __getitem__(self, key):
            # 引数keyのアトリビュートの値を返す。
            return cmds.getAttr(self + '.' + key)
    
        def __setitem__(self, key, value):
            # 引数keyのアトリビュートへvalueをセットする。
            cmds.setAttr(self + '.' + key, value)
    
    
    trs = Node(cmds.createNode('transform'))
    trs['translateX'] = 5
    print trs['tx']
    # 5.0 #
    
    スポンサーサイト

    コメントの投稿

    非公開コメント

    プロフィール

    Eske

    Author:Eske
    萌えイラストレーターを目指す3DCGイラストレーター。
    現在ポケモンカードゲーム、ガンダムトライエイジ、ガンダムコンクエスト、妖怪ウォッチとりつきカードゲームなどで3DCGを使用したイラストレーターとして参加中。

    主にここでは日々気づいたメモなんかを残してます。
    イラストのお仕事も受け付けております。ココからアクセスできますので、お気軽にご相談下さい。

    最新記事
    最新コメント
    カテゴリ
    最新トラックバック
    月別アーカイブ
    検索フォーム
    リンク
    QRコード
    QR