オライリーのPythonクックブック第二版の中で以下のようなPython2.3や2.4で書かれた古いコードがあります。
Python2
1class Proxy(object): 2 """ すべてのプロキシクラスのベースクラス """ 3 def __init__(self, obj): 4 super(Proxy, self).__init__() 5 self._obj = obj 6 def __getattr__(self, attrib): 7 return getattr(self._obj, attrib) 8def make_binder(unbound_method): 9 def f(self, *a, **k): 10 return unbound_method(self._obj, *a, **k) 11 # Python2.4以降ではこうする 12 f.__name__ = unbound_method.__name__ 13 return f 14known_proxy_classes = {} 15def proxy(obj, *specials): 16 """ 特殊メソッドを移譲できるプロキシを作るファクトリ関数 """ 17 # 適当なカスタムクラスがあるのでは? 18 obj_cls = obj.__class__ 19 key = obj_cls, specials 20 cls = known_proxy_classes.get(key) 21 if cls is None: 22 # 既存のプロキシクラスがないので自動生成する 23 cls = type("%sProxy" % obj_cls.__name__, (Proxy,), {}) 24 for name in specials: 25 name = '__%s__' % name 26 unbound_method = getattr(obj_cls, name) 27 setattr(cls, name, make_binder(unbound_method)) 28 # 次に呼び出された時のためにキャッシュする 29 known_proxy_classes[key] = cls 30 # 必要なプロキシをインスタンス化して返す 31 return cls(obj) 32
Python2
1def empty_copy(obj): 2 class Empty(obj.__class__): 3 def __init__(self): pass 4 newcopy = Empty() 5 newcopy.__class__ = obj.__class__ 6 return newcopy 7 8class YourClass(object): 9 def __init__(self): 10 ## assume there's a lot of work here 11 def __copy__(self): 12 newcopy = empty_copy(self) 13 ## copy some relevant subset of self's attributes to newcopy 14 return newcopy 15 16if __name__ == '__main__': 17 import copy 18 y = YourClass() # This, of course, does run __init__ 19 print(y) 20 z = copy.copy(y) # ...but this doesn't 21 print(z) 22
これらの中で、
Python2
1# Python2.4以降ではこうする 2f.__name__ = unbound_method.__name__
や
Python2
1newcopy.__class__ = obj.__class__
この部分が何のためにこのような処理を書き加えているのか教えて頂けないでしょうか?
また、現在のPython3でも上記コードは動きますが、Python3.x現在でも上記のようなコードを書き加えるものなのでしょうか?
その本には何と書いてありますか?
質問ありがとうございます。以下のように書かれています。
上のコードの説明が、
「プロキシやメソッドの自動委譲は、Pythonでは__getattr__メソッドがあるおかげで楽に書くことが出来る。Pythonは属性の参照を行って上手くいかなかった場合、__getattr__メソッドを自動的に呼び出す。従来のオブジェクトモデルでは、__getattr__は特殊メソッドに対しても通常通りに適用された。(2007年)現在、新規に書くコードについては新しいスタイルのオブジェクトのみを使うことが推奨されている。いつの日か、これから何年か後にはPython3.0が従来のオブジェクトモデルを排除することになっている。新しいスタイルのオブジェクトモデルでは、Pythonは実行時に特殊メソッドの検索を行わず、クラスオブジェクトに用意された『スロット』のみを参照する。スロットはクラスオブジェクトが作成または修正された時のみ更新される。このため、ラッピングされたオブジェクトに特殊メソッドを委譲したいプロキシオブジェクトは、それ専用に仕立て上げられたクラスに属している必要がある。幸いにもレシピで示した通り、クラスを実行時に生成してインスタンス化するのはPythonではとても簡単である。~~~」
下のコードの説明が、
「__init__メソッドがハイコストなため、__copy__メソッドで当該のクラス(つまりself)の空インスタンスをまず作ることでこれをバイパスしたい場合も多い。もっともシンプルで汎用的な方法は、Pythonがインスタンスのクラスを動的に変更できることを利用するものである。つまりこのレシピのように、空のインスタンスを作るためのクラスをローカルに用意してそのクラスで新しいオブジェクトを作り、その__class__属性を差し替えてしまう方法である。空のインスタンスを作るEmptyクラスが、コピーしようとするオブジェクトのクラスobj.__class__から継承する部分は無害ではあるものの古いスタイルのクラスにおいては冗長な書き方かもしれない。しかしこの継承によって、このレシピは新旧両方ののクラススタイルで動作することが可能となっている。obj.__class__の継承を行う場合、Emptyクラスの__init__メソッドはオーバーライドする必要があり、そうしなければこのレシピは意味が無くなってしまう。こうして目的のクラスの空オブジェクトを作ったら、たいていはコピー元からselfへと、属性の一部をコピーする必要がある。~~~」
、となっています。分かったような分からないような...という感じです。
desriptorあたりを調べれば理解が進みそうですね
アドバイスありがとうございます。デスクリプタも学習してみます。
回答2件
あなたの回答
tips
プレビュー