Кажется, что __getattribute__
имеет только 2 параметра (self, name)
.
Однако в реальном коде метод, который я перехватываю, фактически принимает аргументы.
Есть ли способ получить доступ к этим аргументам?
Спасибо,
Чарли
Кажется, что __getattribute__
имеет только 2 параметра (self, name)
.
Однако в реальном коде метод, который я перехватываю, фактически принимает аргументы.
Есть ли способ получить доступ к этим аргументам?
Спасибо,
Чарли
__getattribute__ просто возвращает запрошенный атрибут, в случае метода для его вызова используется интерфейс __call__.
Вместо того, чтобы возвращать метод, верните обертку вокруг него, например:
def __getattribute__(self, attr):
def make_interceptor(callble):
def func(*args, **kwargs):
print args, kwargs
return callble(*args, **kwargs)
return func
att = self.__dict__[attr]
if callable(att):
return make_interceptor(att)
Вызов метода в Python представляет собой двухэтапный процесс: сначала функция ищется, а затем вызывается. Для более подробного обсуждения см. мой ответ на этот вопрос.
Итак, вам нужно будет сделать что-то вроде этого:
def __getattribute__(self, key):
if key == "something_interesting":
def func(*args, **kwargs):
# use arguments, and possibly the self variable from outer scope
return func
else:
return object.__getattribute__(self, key)
Кроме того, переопределение __getattribute__ обычно является плохой идеей. Поскольку он вызывается при всех обращениях к атрибутам, очень легко попасть в бесконечный цикл, и даже если вы все сделаете правильно, это приведет к довольно большому удару по производительности. Вы уверены, что __getattr__ будет недостаточно для ваших целей? Или, может быть, даже объект-дескриптор, который возвращает функции. Дескрипторы обычно намного лучше подходят для повторного использования.
Честно говоря, я не уверен, что понимаю ваш вопрос. Если вам нужен способ переопределить getattribute и при этом сохранить исходные атрибуты, вы можете использовать __dict__
def __getattribute__(self, attr):
if attr in self.__dict__:
return self.__dict__[attr]
# Add your changes here
Я не думаю, что вы можете. __getattribute__
не перехватывает вызов метода, он перехватывает только поиск имени метода. Таким образом, он должен возвращать функцию (или вызываемый объект), которая затем будет вызываться с любыми параметрами, указанными в месте вызова.
В частности, если он возвращает функцию, которая принимает (*args, **kwargs)
, то в этой функции вы можете проверять аргументы, как хотите.
Я думаю. Я не эксперт по Python.
Вот вариант предложения Unknown, который возвращает вызываемый экземпляр "обертки" вместо запрошенной функции. Это не требует декорирования никакими методами:
class F(object):
def __init__(self, func):
self.func = func
return
def __call__(self, *args, **kw):
print "Calling %r:" % self.func
print " args: %r" % (args,)
print " kw: %r" % kw
return self.func(*args,**kw)
class C(object):
def __init__(self):
self.a = 'an attribute that is not callable.'
return
def __getattribute__(self,name):
attr = object.__getattribute__(self,name)
if callable(attr):
# Return a callable object that can examine, and even
# modify the arguments prior to calling the method.
return F(attr)
# Return the reference to any non-callable attribute.
return attr
def m(self, *a, **kw):
print "hello from m!"
return
>>> c=C()
>>> c.a
'an attribute that is not callable.'
>>> c.m
<__main__.F object at 0x63ff30>
>>> c.m(1,2,y=25,z=26)
Calling <bound method C.m of <__main__.C object at 0x63fe90>>:
args: (1, 2)
kw: {'y': 25, 'z': 26}
hello from m!
>>>
Муравьи Аасма выше делает важное замечание относительно рекурсии при использовании __getattribute__
. Он вызывается при поиске всех атрибутов в self
, даже внутри метода __getattribute__
. Несколько других примеров включают ссылки на self.__dict__
внутри __getattribute__
, что приведет к рекурсии до тех пор, пока вы не превысите максимальную глубину стека! Чтобы избежать этого, используйте одну из версий базового класса __getattribute__
(например, object.__getattribute__(self,name)
.)
Да
def argumentViewer(func):
def newfunc(*args, **kwargs):
print "Calling %r with %r %r" % (func, args, kwargs)
return func(*args, **kwargs)
return newfunc
class Foo(object):
def __getattribute__(self, name):
print "retrieving data"
return object.__getattribute__(self, name)
@argumentViewer
def bar(self, a, b):
return a+b
# Output
>>> a=Foo()
>>> a.bar
retrieving data
<bound method Foo.newfunc of <__main__.Foo object at 0x01B0E3D0>>
>>> a.bar(2,5)
retrieving data
Calling <function bar at 0x01B0ACF0> with (<__main__.Foo object at 0x01B0E3D0>, 2, 5) {}
7
Благодаря ASk ответу (большое спасибо, приятель) мне удалось решить мою проблему, однако мне пришлось заменить
att = self.__dict__[attr]
с
att = type(self).__dict__[attr].__get__(self, type(self))
и вот рабочий пример с декоратором класса:
def getattribute(self, name):
if name.startswith('__') and name.endswith('__'):
return object.__getattribute__(self, name)
s = '*** method "%s" of instance "%s" of class "%s" called with'\
' following'
print s % (name, id(self), self.__class__.__name__),
def intercept(call, name):
def func(*args, **kwargs):
print 'args: {}, kwargs: {} ***'.format(args, kwargs)
c = call(*args, **kwargs)
print '*** "%s" finished ***' % name
return c
return func
a = type(self).__dict__[name].__get__(self, type(self))
if callable(a):
return intercept(a, name)
return object.__getattribute__(self, name)
def inject(cls):
setattr(cls, '__getattribute__', getattribute)
return cls
@inject
class My_Str(object):
def qwer(self, word='qwer'):
print 'qwer done something here...'