Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
619 views
in Technique[技术] by (71.8m points)

oop - How to catch any method called on an object in python?

I'm looking for a pythonic solution on how to store a method which is called on an object right inside the object.

Because in python, if I want to catch for example the abs() method, I will overload this operator like:

Catcher(object):
    def __abs__(self):
        self.function = abs

c = Catcher()
abs(c)  # Now c.function stores 'abs' as it was called on c

If I want to catch a function, which have an other attribute in it, for example pow(), I'm going to use this:

Catcher(object):
    def __pow__(self, value):
        self.function = pow
        self.value = value

c = Catcher()
c ** 2  # Now c.function stores 'pow', and c.value stores '2'

Now, what I'm looking for is a general solution, to catch and store any kind of function called on Catcher, without implementing all overloads, and other cases. And as You can see, I also want to store the values (maybe in a list, if there is more than one of them?) which are the attributes of a method.

Thanks in advance!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

A metaclass won't help here; although special methods are looked up on the type of the current object (so the class for instances), __getattribute__ or __getattr__ are not consulted when doing so (probably because they are themselves special methods). So to catch all dunder methods, you are forced to create them all.

You can get a pretty decent list of all operator special methods (__pow__, __gt__, etc.) by enumerating the operator module:

import operator
operator_hooks = [name for name in dir(operator) if name.startswith('__') and name.endswith('__')]

Armed with that list a class decorator could be:

def instrument_operator_hooks(cls):
    def add_hook(name):
        operator_func = getattr(operator, name.strip('_'), None)
        existing = getattr(cls, name, None)

        def op_hook(self, *args, **kw):
            print "Hooking into {}".format(name)
            self._function = operator_func
            self._params = (args, kw)
            if existing is not None:
                return existing(self, *args, **kw)
            raise AttributeError(name)

        try:
            setattr(cls, name, op_hook)
        except (AttributeError, TypeError):
            pass  # skip __name__ and __doc__ and the like

    for hook_name in operator_hooks:
        add_hook(hook_name)
    return cls

Then apply that to your class:

@instrument_operator_hooks
class CatchAll(object):
    pass

Demo:

>>> c = CatchAll()
>>> c ** 2
Hooking into __pow__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in op_hook
AttributeError: __pow__
>>> c._function
<built-in function pow>
>>> c._params
((2,), {})

So, even though our class doesn't define __pow__ explicitly, we still hooked into it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...