Is there a way to access the formal parameters if you implement __getattribute__

最后都变了- 提交于 2019-12-10 17:07:08

问题


It seems as though __getattribute__ has only 2 parameters (self, name).

However, in the actual code, the method I am intercepting actually takes arguments.

Is there anyway to access those arguments?

Thanks,

Charlie


回答1:


__getattribute__ simply returns the attribute that was requested, in case of a method, the __call__ interface is then used to call it.

Instead of returning the method, return a wrapper around it, for instance:

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)



回答2:


Method invocation in Python is two step process, first a function is looked up, then it is invoked. For a more involved discussion see my answer to this question.

So you would need to do something like this:

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)

Also, overriding __getattribute__ is usually a bad idea. Because it is called on all attribute accesses it is really easy to end up in an infinite loop, and even if you do everything correctly it ends up being a pretty big performance hit. Are you sure that __getattr__ won't be enough for your purposes? Or maybe even a descriptor object that returns functions. Descriptors are usually a lot better at reuse.




回答3:


Honestly I'm not sure I understand your question. If you want a way to override getattribute and yet keep the original attributes you can use __dict__

def __getattribute__(self, attr):
    if attr in self.__dict__:
          return self.__dict__[attr]
    # Add your changes here



回答4:


I don't think you can. __getattribute__ doesn't intercept the method call, it only intercepts the method name lookup. So it should return a function (or callable object), which will then be called with whatever parameters specified at the call site.

In particular, if it returns a function which takes (*args, **kwargs), then in that function you can examine the arguments however you want.

I think. I'm not a Python expert.




回答5:


Here is a variation on Unknown's suggestion that returns a callable "wrapper" instance in place of the requested function. This does not require decorating any methods:

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

Ants Aasma above makes an important point regarding recursion when using __getattribute__. It is called by all attribute lookups on self, even those inside of the __getattribute__ method. Several of the other examples include references to self.__dict__ inside of __getattribute__ which will result in recursing until you exceed the maximum stack depth! To avoid this, use one of the base class' versions of __getattribute__ (e.g. object.__getattribute__(self,name).)




回答6:


Yes

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



回答7:


Thanks to ASk answer (big thank you mate) I managed to solve my problem, however I had to replace

     att = self.__dict__[attr]

with

att = type(self).__dict__[attr].__get__(self, type(self))

and here is working example with class decorator:

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


来源:https://stackoverflow.com/questions/911905/is-there-a-way-to-access-the-formal-parameters-if-you-implement-getattribute

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!