Есть ли способ получить доступ к формальным параметрам, если вы реализуете __getattribute__ - PullRequest
3 голосов
/ 26 мая 2009

Кажется, что __getattribute__ имеет только 2 параметра (self, name).

Однако в реальном коде метод, который я перехватываю, фактически принимает аргументы.

Есть ли доступ к этим аргументам?

Спасибо

Charlie

Ответы [ 7 ]

5 голосов
/ 26 мая 2009

__ 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)
3 голосов
/ 26 мая 2009

Вызов метода в 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__ будет недостаточно для ваших целей? Или, может быть, даже объект дескриптора, который возвращает функции. Дескрипторы обычно намного лучше при повторном использовании.

1 голос
/ 10 декабря 2009

Вот вариант предложения 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).)

1 голос
/ 26 мая 2009

Не думаю, что ты можешь. __getattribute__ не перехватывает вызов метода, он только перехватывает поиск имени метода. Поэтому он должен возвращать функцию (или вызываемый объект), которая затем будет вызываться с любыми параметрами, указанными на сайте вызова.

В частности, если он возвращает функцию, которая принимает (*args, **kwargs), то в этой функции вы можете исследовать аргументы так, как хотите.

Я думаю. Я не эксперт по Python.

1 голос
/ 26 мая 2009

Честно говоря, я не уверен, что понимаю ваш вопрос. Если вы хотите переопределить getattribute и сохранить оригинальные атрибуты, вы можете использовать __dict__

def __getattribute__(self, attr):
    if attr in self.__dict__:
          return self.__dict__[attr]
    # Add your changes here
0 голосов
/ 15 июля 2013

Благодаря 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...'
0 голосов
/ 26 мая 2009

Да

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
...