我正在解决the python koans。 直到34日我才有任何实际问题。
这就是问题:
项目:创建代理类
在此作业中,创建一个代理类(一个是为您启动的 下面)。您应该能够使用any初始化代理对象 宾语。应转发在代理对象上调用的任何属性 到目标对象。在发送每个属性调用时,代理 应该记录发送的属性的名称。
代理类是为您启动的。您需要添加一个方法 缺少处理程序和任何其他支持方法。规范 Proxy类的内容在AboutProxyObjectProject koan中给出。
注意:这有点棘手,它是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.messages()
为['power']
,因为tv.channel=10
由代理对象而非电视对象占用。
我试图操纵__setattr__
方法,但我总是以无限循环结束。
我正在尝试这个:
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__
。
答案 0 :(得分:5)
您正在hasattr
中使用__setattr__
来决定是否应该写入本地或代理对象。除了一个案例之外,这适用于所有情况。
在__init__
中,您有以下内容:
self._count = {}
这会调用带有__setattr__
的{{1}},此时此时不存在,因此(因此'_count'
返回hasattr
)会转发到代理对象。
如果你想使用你的方法,你必须像这样写False
:
__init__
答案 1 :(得分:4)
据我所知,当你设置和属性值时,你的问题可能与递归调用有关。 From docs:
如果__setattr__()
想要分配给实例属性,则不应该只执行“self.name = value
” - 这会导致对自身的递归调用。相反,它应该在实例属性的字典中插入值,例如“self.__dict__[name] = value
”。对于新式类,它应该调用具有相同名称的基类方法,而不是访问实例字典,例如“object.__setattr__(self, name, value)
”。
答案 2 :(得分:1)
在所有作业中调用setattr。它更像是getattribute而不是getattr。这也会影响__init__方法中的代码。
这意味着此代码的第一个分支几乎总是会失败,只有从object继承的属性才能通过测试:
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__:
...
答案 3 :(得分:1)
从测试开始,代理需要通过代理记录所有属性调用。代理只有很少的内置方法,特别用于日志记录,所以我的答案是:
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',...)
抱歉老问题。
我发现 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)
答案 4 :(得分:0)
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
答案 5 :(得分: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
答案 6 :(得分:0)
为什么不使用method_missing?
我的回答:
class Proxy
def initialize(target_object)
@object = target_object
# ADD MORE CODE HERE
@messages = []
end
# WRITE CODE HERE
def method_missing(method_name, *args, &block)
@messages.push method_name unless method_name == :messages
@object.send method_name, *args, &block
end
def messages
@messages
end
def called? target
@messages.include? target
end
def number_of_times_called target
result = 0
@messages.each do |t|
result += 1 if t == target
end
result
end
end