python koans:クラスプロキシ

私は python koans を解決しています。 私は34日までは何の問題もありませんでした。

これが問題です:

プロジェクト:プロキシクラスを作成する

     

この課題では、プロキシクラスを作成します(1つはあなたのために開始されます   以下)。プロキシオブジェクトを任意の値で初期化できるはずです   オブジェクト。プロキシオブジェクトで呼び出されたすべての属性を転送する必要があります   ターゲットオブジェクトに追加します。各属性呼び出しが送信されると、プロキシ   送信された属性の名前を記録する必要があります。

     

プロキシクラスが開始されました。メソッドを追加する必要があります   不足しているハンドラおよびその他のサポート方法。仕様   プロキシクラスのAboutProxyObjectProjectに与えられます。

     

注:これはRuby Koansのそれとは少し違っていますが、   それができます!

これは今までの私の解決策です:

class Proxy(object):
    def __init__(self, target_object):
        self._count = {}
        #initialize '_obj' attribute last. Trust me on this!
        self._obj = target_object

    def __setattr__(self, name, value):pass


    def __getattr__(self, attr):
        if attr in self._count: 
            self._count[attr]+=1
        else: 
            self._count[attr]=1
        return getattr(self._obj, attr)

    def messages(self):
        return self._count.keys()

    def was_called(self, attr):
        if attr in self._count:
            return True
        else: False

    def number_of_times_called(self, attr):
        if attr in self._count:
            return self._count[attr]
        else: return False

このテストまで動作します:

def test_proxy_records_messages_sent_to_tv(self):
    tv = Proxy(Television())

    tv.power()
    tv.channel = 10

    self.assertEqual(['power', 'channel='], tv.messages())

tv.channel = 10 はテレビオブジェクトではなくプロキシオブジェクトによって取得されるため、 tv.messages() ['power']
私は __setattr __ メソッドを操作しようとしましたが、私はいつも無制限のループで終了します。

編集1:

私はこれを試しています:

def __setattr__(self, name, value):
        if hasattr(self, name):
            object.__setattr__(self,name,value)
        else: 
            object.__setattr__(self._obj, name, value)

しかし、私は最後のエントリのループでこのエラーが発生します:

RuntimeError: maximum recursion depth exceeded while calling a Python object


File "/home/kurojishi/programmi/python_koans/python 2/koans/about_proxy_object_project.py", line 60, in test_proxy_method_returns_wrapped_object
tv = Proxy(Television())                                                                                                                                     
File "/home/kurojishi/programmi/python_koans/python 2/koans/about_proxy_object_project.py", line 25, in __init__                                               
self._count = {}                                                                                                                                             
File "/home/kurojishi/programmi/python_koans/python 2/koans/about_proxy_object_project.py", line 33, in __setattr__                                            
object.__setattr__(self._obj, name, value)                                                                                                                   
File "/home/kurojishi/programmi/python_koans/python 2/koans/about_proxy_object_project.py", line 36, in __getattr__                                            
if attr in self._count:      

ループは __ getattr __ にあります。

1
投稿されたエラーは完了しましたか?例外は何ですか?
追加された 著者 joaquin,
私のヒントを試してみましたか?
追加された 著者 Frederick Roth,
試験で幸運を祈る!
追加された 著者 Frederick Roth,
申し訳ありませんが、これはちょっとしたエラーです。最後のエントリのエラーループ例外は 'RuntimeError:Pythonオブジェクトの呼び出し時に最大再帰深度を超過しました'
追加された 著者 kurojishi,
悲しそうにいいえ、私は激しいexameセッションだった私は信じています私は9月後の時間があります:)
追加された 著者 kurojishi,

6 答え

hasattr __setattr __ に使用して、ローカルオブジェクトまたはプロキシオブジェクトに書き込むべきかどうかを決定しています。これは、1つのケースを除くすべてのケースでうまく機能します。

あなたの __ init __ には次の行があります:

  self._count = {}

これはその時点では存在しないため( hasattr False を返します) '_ count' __ setattr __ プロキシされたオブジェクトに転送されます。

あなたのアプローチを使いたいなら、以下のように __ init __ を書かなければなりません:

def __init__(self, target_object):
    object.__setattr__(self, '_count', {})
    #initialize '_obj' attribute last. Trust me on this!
    object.__setattr__(self, '_obj', target_object)
5
追加された
喜んで:)
追加された 著者 Frederick Roth,
確認する時間がありましたか? ;)
追加された 著者 Frederick Roth,
おかげでこれは良い解決策に見えます、私はfreetimeの最初のスライスで試してみましょう、他の人のために同じ、私は非常に忙しくて、私は最後のものを試すことができなかった、私はすぐにそれらをチェックします可能な限り、多くのありがとう
追加された 著者 kurojishi,
私はすべてのkoansをやり直すのを遅くしています(完全な1つの家を国を変えて残し、PCはネットワークから切り離されました)。私はそれに到着します:)
追加された 著者 kurojishi,

私が多分あなたの問題は再帰呼び出しと関連し、あなたが値を設定し属性を付けると理解しています。 ドキュメントから

__setattr __()がインスタンス属性に割り当てたい場合、単に " self.name = value "を実行してはいけません。代わりに、 " self .__ dict__ [name] = value "などのインスタンス属性のディクショナリに値を挿入する必要があります。新しいスタイルのクラスでは、インスタンス辞書にアクセスするのではなく、 " object .__ setattr __(self、name、value)"という同じ名前の基本クラスメソッドを呼び出す必要があります。

4
追加された
その方法を使用して初期のアイデアを編集しました。やっていない
追加された 著者 kurojishi,

テストから、プロキシがすべての属性呼び出しをプロキシ経由でログに記録する必要があります。プロキシには、ログには例外的に使用される組み込みのメソッドがほとんどないので、私の答えは:

class Proxy(object):

    def __init__(self, target_object):
        self.logs=[]
        self._obj = target_object


    def __getattribute__(self, attrname):
        if attrname in ['_obj','logs','messages','was_called','number_of_times_called'] :
            return object.__getattribute__(self, attrname)
        else:
            self.logs.append(attrname)
            return object.__getattribute__((object.__getattribute__(self, '_obj')), attrname)


    def __setattr__(self, name, value):
        if hasattr(self, '_obj'):
            self.logs.append(name)
            object.__setattr__(object.__getattribute__(self,'_obj'), name, value)
        else :
            object.__setattr__(self, name, value)

この後、他のメソッド( 'messages'、 'was_called'、...)を実装するのは非常に簡単です。

申し訳ありませんがnecro'ing古い質問です。

getattribute を変更できることがわかりました。属性がターゲットオブジェクト内にあるかどうかをチェックするだけです。

def __getattribute__(self, attrname):
        if attrname not in dir(object.__getattribute__(self, '_obj')):
            return object.__getattribute__(self, attrname)
        else:
            self.logs.append(attrname)
            return object.__getattribute__((object.__getattribute__(self, '_obj')), attrname)
1
追加された
おかげでnecropostingとprobなし:)
追加された 著者 kurojishi,

setattrはすべての代入に対して呼び出されます。それはgetattrよりもgetattributeに似ています。これは__init__メソッドのコードにも影響します。

つまり、このコードの最初のブランチはほとんど常に失敗し、オブジェクトから継承した属性だけがテストに合格します。

def __setattr__(self, name, value):
    if hasattr(self, name):
        object.__setattr__(self,name,value)
    else: 
        object.__setattr__(self._obj, name, value)

代わりに  割り当てが_obj属性を持たない限り、プロキシのためのものであると仮定できます。したがって、__init__のコメント。プロキシのアトリビュートを設定し、ターゲットオブジェクトを追加すると、それ以降のすべてのアサインメントが送信されます。

def __setattr__(self, name, value):
    if hasattr(self, '_obj'):
        object.__setattr__(self._obj, name, value)
    else:
        object.__setattr__(self, name, value)

しかし、hasattrを使用することによって、再帰を防ぐために__getattr__を変更して_objをチェックする必要があります。

def __getattr__(self, name):
    if '_obj' == name:
        raise AttributeError

    if attr in self._count: 
        self._count[attr]+=1
    else: 
        self._count[attr]=1
    return getattr(self._obj, attr)

__setattr__メソッドでプロキシの__dict__属性を直接検査する方法もあります:

def __setattr__(self, name, value):
    if '_obj' in self.__dict__:
    ...
1
追加された
class Proxy(object):
   """Proxy class wraps any other class, and adds functionality to remember and report all messages called.
Limitations include that proxy blocks all direct subclass calls to:
messages, number_of_times_called, was_called, _obj, and _message_counts.
These calls must be made directly like my_proxy_instance._obj.messages.
"""


def __init__(self, target_object):
    print 'initializing a proxy for ' + target_object.__class__.__name__
    # WRITE CODE HERE
    self._message_counts = Counter();
    #initialize '_obj' attribute last. Trust me on this!
    self._obj = target_object

# WRITE CODE HERE                                   
def  __getattr__(self, attr_name):
    print 'getting an attribute: "' + attr_name + '" from "' + self._obj.__class__.__name__  + '"'
    self._message_counts[attr_name] += 1
    print self._message_counts
    return object.__getattribute__(self._obj, attr_name)

#def __getattribute__(self, attr_name):
#    print "intercepted!~ " + attr_name
#    object.__getattribute__(self, attr_name)

def __setattr__(self, attr_name, value):
    if((attr_name == '_obj') | (attr_name == '_message_counts')): # special proxy attributes.
        print 'setting the PROXY attribute: "' + attr_name + '"'
        object.__setattr__(self, attr_name, value)
    else:
        print 'setting the REAL attribute: "' + attr_name + '"'
        self._message_counts[attr_name+"="] += 1
        object.__setattr__(self._obj, attr_name, value)

def messages(self):
    return self._message_counts.keys()

def number_of_times_called(self, attr_name):
    return self._message_counts[attr_name]

def was_called(self, attr_name):
    return attr_name in self._message_counts
0
追加された

私がしたのは、プロキシの属性へのすべての呼び出しを受け取り、再帰を避けるために object .__ getattribute __ を介して呼び出すことでした。

それはメソッドでは機能しませんでしたので、try..except AttributeErrorでメソッド呼び出しをラップして、プロキシで最初に試しました。エラーが発生した場合は、子オブジェクトでそれらを試してください。

誰かがもっと優雅な解決策を持っている場合は、それを見て大好きです。

from runner.koan import *
from collections import Counter

class Proxy(object):
    def __init__(self, target_object):
        self._messages=[]
        self._obj = target_object

    def messages(self):
        return self._messages

    def was_called(self, message):
        return message in self._messages

    def number_of_times_called(self, message):
        _count = Counter(self._messages).get(message)
        if _count: 
            return _count
        else: # catch None
            return 0

    def __getattribute__(self, attr_name):
        try: # call on self
            retval = object.__getattribute__(self, attr_name)
        except AttributeError: # call on child object
            retval = self._obj.__getattribute__(attr_name)
            object.__getattribute__(self, '_messages').append(attr_name)

        return retval

    def __setattr__(self, attr_name, attr_value):
        if hasattr(self, '_obj'): # call child object and log message
            self._obj.__setattr__(attr_name, attr_value)
            attr_name += "="
            object.__getattribute__(self, '_messages').append(attr_name)
        else: # use this before_obj is set in __init__
            object.__setattr__(self, attr_name, attr_value)

    def messages(self):
        return self._messages


0
追加された