トップ Team(・∀・)2ch 新規 編集 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

Part17

 part17

委譲(デリゲート)での__getattribute__の使い方が分りません。(19-39)

19 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 01:54:54 
委譲(デリゲート)での__getattribute__の使い方が分りません。 
  従来の__getattr__() は、解決できない属性名の時だけ呼ばれるが 
  __getattribute__() は、属性値の取得が必要になったときに、常に呼ばれるらしいのですが。 

まず、__getattr__でのサンプル。 

class wrapper(object): 
  def __init__(self, obj): 
    self.__wrapped = obj # obj を組み込む。 
  def __getattr__(self, attrname): # attrnameは、知らない名前の属性が来たら呼び出される 
    return getattr(self.__wrapped, attrname) # 組み込まれたオブジェクトにgetattr 関数で処理を委託。 

l = wrapper([]) # リストを引数にwrapper クラスを呼び出すと、リストにできる操作なら全てできる

(実行すると、正しく動作します) 
>>> l.append(333) 
>>> l.pop(0) 
333 
20 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 01:56:08 
ここで、__getattr__を__getattribute__に置き換えます。 

class wrapper(object): 
  def __init__(self, obj): 
    self.__wrapped = obj # obj を組み込む。 
  def __getattribute__(self, attrname): 
    return getattr(self.__wrapped, attrname) 

l = wrapper([]) 

(試しに、実行してみます) 
>>> l.append(333) 
(ここで暴走) 

なぜ暴走するのかよく分りません。 
__getattribute__はどう使うのが正しいのでしょうか? 
21 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 02:48:11 
>>20 
self.__getattribute__ の中で self._wrapped を取得しようとすると 
さらに self.__getattribute__(self, '_wrapped') を呼び出して無限再帰ループになるみたい。 

解決方法は・・・たとえば、_wrapped をインスタンス属性としてもつんじゃなくて 
クラス属性にインスタンスをキーにした辞書をもつようにするとかでどう? 

class wrapper(object): 
    _wrapped = {} 
    def __init__(self, obj): 
        wrapper._wrapped[hash(self)] = obj 
    def __del__(self): 
        del wrapper._wrapped[hash(self)] 
    def __getattribute__(self, attrname): 
        return getattr(wrapper._wrapped[hash(self)], attrname) 
23 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 03:00:57 
__getattr__ を __getattribute__ に置き換えるって話じゃないのか 
24 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 03:08:57 
if attrname not in ['_wrapped', etc, etc]: 
  return getattr(self._wrapped, attrname) 
else: 
  super(wrapper, self).__getattribute(attrname) 
25 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 03:14:55 
>>21 でおkかと思ったけど、len() ができないなぁ。 

>>> l = wrapper([]) 
>>> len(l) 

Traceback (most recent call last): 
  File "<pyshell#84>", line 1, in <module> 
    len(l) 
TypeError: object of type 'wrapper' has no len() 

TypeError なあたり、決まった型にしか呼べないのかも。 
len(x) って内部で x.__len__ を返してるのかと思ってたけど、ちょっと違うのかな。 

>>> l.append(3) 
>>> l.__len__() 
1
27 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 04:03:11 
>>25 
アクセサでフィールド値を取得してから、len しないとダメじゃない? 
__getattribute__ じゃなく、__getattr__ の例で悪いけど、 
この場合、同じと思う。 

class wrapper(object): 
  def __init__(self, obj): 
    self.__wrapped = obj 
  def __getattr__(self, attrname): 
    return getattr(self.__wrapped, attrname) 
  def get_wrapped(self): # フィールド値取得メソッドを設定 
    return self.__wrapped 

l = wrapper([]) 
l.append(333) 

(ここで len を実行。正常に動作する) 
>>> len(l.get_wrapped()) 
1 
>>> 
28 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 04:37:24 
>>25 
__getattribute__ でフックされるのは、dir(l) で出てくるものだけだと思う。 
したがって、lenは使えないが__len__は使えるということになる。 

>>> dir(l) 
['__add__', '__class__', '__contains__', '__delattr__', 
'__delitem__', '__delslice__', '__doc__', '__eq__', '__ge__', 
'__getattribute__', '__getitem__', '__getslice__', '__gt__', 
'__hash__', '__iadd__', '__imul__', '__init__', '__iter__', 
'__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__reversed__', 
'__rmul__', '__setattr__', '__setitem__', '__setslice__', 
'__str__', 'append', 'count', 'extend', 'index', 'insert', 
'pop', 'remove', 'reverse', 'sort'] 
>>> 
30 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 04:50:46 
>>28 
なるほど。__len__ が特別だったのか。確かに __init__ とかフックされたら困るよなぁ。 
__len__ を定義して、ラップしているオブジェクトの len() を返すようにしたら、ちゃんと動きますた。 
質問主じゃないけど勉強になった。この先 __getattribute__ を使うかどうかはともかくとしてw 
35 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 12:43:37 
>>19 >>20 で __getattribute__ の使い方について質問した者です。 

なぜ、__getattr__ ではなく__getattribute__ を使いたいと思ったかというと、 
「スーパークラスの配列で,複数の異なるサブクラスのインスタンスを一元管理する」 
というテクニックを使えるようにしておきたいと思ったからです。 

  ちなみに、従来からあるメソッド __getattr__() は、解決できない属性名の時だけ呼ばれるが 
  __getattribute__() は、属性値の取得が必要になったときに、常に呼ばれる、とのこと。 

「スーパークラスの配列で,複数の異なるサブクラスのインスタンスを一元管理する」に 
ついては、javaの解説ページですが、下のところにあります。 
http://itpro.nikkeibp.co.jp/members/NSW/ITBASIC/20050620/162997/ 

(略)部長さん,課長さん,担当さんをそれぞれ表すBuchoクラス,Kachoクラス,Tantoクラスを 
定義し,それぞれのクラスのメンバーとして給与の金額を返すgetKyuyoという関数があるとします。 
複数のクラスに同じ名前の関数があるのですから,それらを汎化して社員を表すShainクラスを定義 
しましょう。(略) 

サブクラスであるBuchoクラス,Kachoクラス,Tantoクラスは,0を返すgetKyuyoを継承することに 
なります。こんな関数を継承しても役に立ちませんね。スーパークラスから継承した関数の機能が 
サブクラスに合わない場合は,サブクラスで同名のメソッドを記述すれば上書き変更できます。 
これを「オーバーライド(override)」と呼びます。部長さんの給与は70万円,課長さんの給与は 
50万円,担当さんの給与は30万円としましょう 

(略)汎化を行わなかったら「役職ごとに給与を求めて集計する」という手順になるでしょう。 
汎化を行ったことで「社員の給与を一気に集計する」という手順が実現できるのです。(終) 

まあ、それに必要かなと思ったんですが、単にshain = [] に shain.append(shainObject)で集めて、 
for で 要素に次々と .getKyuyo して、それを加算していくだけでいいような気もします。 

本当に __getattribute__ が使えないと困るのかな? 頭冷やして考えないと分からないw 
とにかく、いろいろ参考になりました。みなさん、どうもありがとう。 
37 名前:デフォルトの名無しさん 投稿日:2007/02/18(日) 14:06:03 
>>35 
そのプログラムは単にオブジェクト指向のポリモーフィズム(多様性)を利用しているだけなので 
__getattribute__ は必要ありません。そのプログラムを Python で書くなら、こんな感
じになります。 

class Shain: 
    def getKyuyo(self): return 0 
class Bucho(Shain): 
    def getKyuyo(self): return 700000 
class Kacho(Shain): 
    def getKyuyo(self): return 500000 
class Tanto(Shain): 
    def getKyuyo(self): return 300000 

s = [ Bucho(), Kacho(), Kacho(), Tanto(), Tanto(), Tanto() ] 

goukei = 0 
for p in s: goukei += p.getKyuyo() 
print goukei 


ちなみに、Python では Bucho Kacho Tanto を Shain のサブクラスにしなくても動きます。 
これは Java の「変数の型」という概念が Python にはないからです。 
Python ではオブジェクトに対してメソッドを呼び出した場合、その名前を持つメソッドが 
動的に(実行時に)決定されるので、スーパークラスで抽象化する必要はありません。(ダックタイピング) 
(ただし、スーパークラスの振る舞いを引き継ぐ為の継承は有効な手段です) 
呼び出されるメソッドが静的に(コンパイル時に)決定される Java や C++ などとの大きな違いですね。 
39 名前:35 投稿日:2007/02/18(日) 14:26:56 
>>37 
サンクス。

a=1.2+1.0000000000000000ってやると... (875-879)

875 名前:デフォルトの名無しさん 投稿日:2007/04/03(火) 01:34:43 
a=1.2 
a=1.2+1.0000000000000000 
ってやるとaが
2.2000000000000002
になって困ってます
どうやったら解決しますか? 
876 名前:デフォルトの名無しさん 投稿日:2007/04/03(火) 01:39:54 
>>> from decimal import Decimal 
>>> Decimal('1.2') + Decimal('1.0000000000000000') 
Decimal("2.2000000000000000") 
878 名前:デフォルトの名無しさん 投稿日:2007/04/03(火) 02:17:09 
ありがとうございます 
879 名前:デフォルトの名無しさん 投稿日:2007/04/03(火) 08:02:41 
ほんとにぱいそん信者は使えないねw 

チミ達はIDEというかエディターなどというものはなに使ってる (915-922)

915 名前:デフォルトの名無しさん 投稿日:2007/04/05(木) 18:35:18 
チミ達はIDEというかエディターなどというものはなに使ってる 
まさかメモ帳はありえないだろ普通wwww 
おれはsciteを使ってます 
916 名前:デフォルトの名無しさん 投稿日:2007/04/05(木) 19:05:00 
vim-7 
917 名前:デフォルトの名無しさん 投稿日:2007/04/05(木) 19:10:29 
xyzzy 
918 名前:デフォルトの名無しさん 投稿日:2007/04/05(木) 19:21:39 
IDLE 
919 名前:デフォルトの名無しさん 投稿日:2007/04/05(木) 20:45:08 
emacs 

five-things-I-hate ってちゃんと使ってないと怖くて書けないよな‥ 
920 名前:デフォルトの名無しさん 投稿日:2007/04/05(木) 21:06:43 
PyScripter + IDLE 
921 名前:デフォルトの名無しさん 投稿日:2007/04/05(木) 21:18:10 
秀丸 
922 名前:デフォルトの名無しさん 投稿日:2007/04/05(木) 23:21:04 
秀丸 たまに PyCrust 併用 
最終確認が必要な場合は eclipse など

intをfloatにするほうほうがわかりません (931-933)

931 名前:デフォルトの名無しさん 投稿日:2007/04/06(金) 21:57:30 
intをfloatにするほうほうがわかりません 
932 名前:デフォルトの名無しさん 投稿日:2007/04/06(金) 22:05:59 
>>>float(4) 
4.0
933 名前:デフォルトの名無しさん 投稿日:2007/04/06(金) 22:09:14 
ありがとうございます 
とてもさんこうになります 
カッコのなかは文字じゃないとだめとおもってました