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!
To call a class method, put the class as the first argument. Class methods can be can be called from instances and from the class itself. All of these use the same method. The method can use the classes variables and methods.
The first method to find the methods is to use the dir() function. This function takes an object as an argument and returns a list of attributes and methods of that object. From the output, we can observe that it has returned all of the methods of the object.
Calling an object's method is similar to getting an object's variable. To call an object's method, simply append the method name to an object reference with an intervening '. ' (period), and provide any arguments to the method within enclosing parentheses.
This means that it can deal with classes and objects to model the real world. A Python method is a label that you can call on an object; it is a piece of code to execute on that object. But before we begin getting any deeper, let’s take a quick look at classes and objects.
Lastly, we can also use the hasattr () method to find out the methods of a Python object. This function checks if an object has an attribute. The function takes two arguments: object and attribute. It checks if the attribute is present in that particular object.
Python Functions vs Method 1 While a method is called on an object, a function is generic. 2 Since we call a method on an object, it is associated with it. Consequently, it is able to access and operate on the... 3 A method may alter the state of the object; a function does not, when an object is passed as an argument to it. We have... More ...
So, To create a callable object in python, we will implement the __call__() method in the function definition of the object as seen in the example given abve.
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.
This is a way to do it.
import inspect
from functools import wraps
from collections import namedtuple
call = namedtuple('Call', ['fname', 'args', 'kwargs'])
calls = []
def register_calls(f):
@wraps(f)
def f_call(*args, **kw):
calls.append(call(f.__name__, args, kw))
print calls
return f(*args, **kw)
return f_call
def decorate_methods(decorator):
def class_decorator(cls):
for name, m in inspect.getmembers(cls, inspect.ismethod):
setattr(cls, name, decorator(m))
return cls
return class_decorator
@decorate_methods(register_calls)
class Test(object):
def test1(self):
print 'test1'
def test2(self):
print 'test2'
Now all the calls to test1
and test2
will be registers in the calls
list
.
decorate_methods
applies a decorator to each method of the class. register_calls
registers the calls to the methods in calls
, with the name of the function and the arguments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With