在python中,假设我有一个看起来像这样的基本类结构:
class Foo(object):
def do_something(self):
print 'doing a thing'
def do_another_thing(self):
print 'doing another thing'
class Bar(Foo):
def do_another_thing(self):
super(bar, self).do_another_thing()
print 'doing more stuff still'
我了解如何构建__mro__
属性,但是我想添加日志记录,以便在输出中看到每个类拨打其呼叫时发现/调用的方法。因此,例如,我希望它按照下面的注释来登录:
f = Foo()
b = Bar()
f.do_something()
#print 'Implementing foo method do_something'
b.do_something()
#print 'Did not find method do_something for bar'
#print 'Implementing foo method do_something'
f.do_another_thing()
#print 'Implementing foo method do_another_thing'
b.do_another_thing()
#print 'Implementing bar method do_another_thing'
#print 'Implementing foo method do_another_thing'
我已经摆弄__getattribute__
和__get__
,但显然我不太了解这些方法无法根据需要实现。我还考虑使用装饰器,但我认为以某种方式使用描述符可能是在这里走的路线。
这是我到目前为止尝试过的:
class Bar(Foo):
def do_another_thing(self):
super(Bar, self).do_another_thing()
print 'doing more stuff still'
def __getattribute__(self, key):
self_dict = object.__getattribute__(type(self), '__dict__')
if key in self_dict:
print 'Implementing {} method {}'.format(type(self).__name__, key)
v = object.__getattribute__(self, key)
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
print 'Did not find method {} for {}'.format(key, type(self).__name__)
mro = object.__getattribute__(type(self), '__mro__')
for thing in mro[1:]:
v = thing.__getattribute__(self, key)
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
我也在FOO中重新定义了此__getattribute__
,我的输出如下:
Implementing Foo method do_something
doing a thing
Did not find method do_something for Bar
Did not find method do_something for Bar
doing a thing
Implementing Foo method do_another_thing
doing another thing
Implementing Bar method do_another_thing
doing another thing
doing more stuff still
因此,我能够在第一个继承的第一层捕获正确的记录,但无法正确地将呼叫从bar中返回到foo,以便我可以利用Foo的__getattribute__
。
我做到了 - 这似乎在我的用例中起作用,尽管我可能有多个继承的问题:
class loggingObject(object):
def __getattribute__(self, item):
#we let the smart __getattribute__ method of object get the object we want
v = object.__getattribute__(self, item)
#if it's not a function, then we don't care about logging where it came from
import types
if not isinstance(v, types.MethodType):
#so we finish off default implementation
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
#get the dictionary of all explicitly defined items in the class that self is an instance of
self_dict = object.__getattribute__(type(self), '__dict__')
#if item is in self_dict, then the class that self is an instance of did implement this function
if item in self_dict:
#we log, and then implement default __getattribute__ behaviour
print 'Implementing {} method {}'.format(type(self).__name__, item)
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
#if we are are, item is not explicitly in self_dict, and hence the class self is an instance of did not implement this function
print 'Did not find explicit method {} for {}'.format(item, type(self).__name__)
#unbind the function from self so it can be used for comparison
actual_function = v.__func__
#get the __mro__ for the class that self is an instance of
mro = object.__getattribute__(type(self), '__mro__')
#we loop through the mro to comapre functions in each class going up the inheritance tree until we find a match
for cls in mro[1:]:
try:
#get the function for cls
function = object.__getattribute__(cls, '__dict__')[item]
#if the function from class cls and the actual_function agree, that is where we got it from
if function == actual_function:
print 'Implementing {} method {}'.format(cls.__name__, item)
break
except KeyError:
print 'Did not find explicit method {} for {}'.format(item, cls.__name__)
#now that we have logged where we got the function from, default behaviour
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
class Foo(loggingObject):
def do_something(self):
print 'doing a thing'
def do_another_thing(self):
print 'doing another thing'
class Bar(Foo):
def do_another_thing(self):
super(Bar, self).do_another_thing()
print 'doing more stuff still'