Is there an easy way to do something at the beginning and end of each function in a class? I've looked into __getattribute__
, but I don't think that I can use it in this situation?
Here's a simplified version of what I'm trying to do:
class Thing():
def __init__(self):
self.busy = False
def func_1(self):
if self.busy:
return None
self.busy = True
...
self.busy = False
def func_2(self):
if self.busy:
return None
self.busy = True
...
self.busy = False
...
As an alternative to the accepted answer, if you want this decoration to only be applicable for instance methods, you could use __getattribute__
.
class Thing(object):
def __init__(self):
self.busy = False
def __getattribute__(self, name):
attr = object.__getattribute__(self, name)
if callable(attr) and not name.startswith('_') and attr.__self__ == self:
attr = decorator(attr)
return attr
def func_1(self):
# instance method will be wrapped by `decorator`
...
@classmethod
def class_func(cls):
# class method will not be wrapped by `decorator`
# when called using `self.`, `cls.` or `Thing.`.
...
@staticmethod
def static_func():
# static method will not be wrapped by `decorator`
# when called using `Thing.`.
...
object
and will not work for old-style classes in Python 2.callable
was removed in Python 3.0, but returned in 3.2. Alternatively, isinstance(obj, collections.Callable)
can be used.If you'd like to wrap class methods and static methods differently, you could inherit from a custom type
metaclass
:
class Meta(type):
def __getattribute__(*args):
print("staticmethod or classmethod invoked")
return type.__getattribute__(*args)
class Thing(object, metaclass=Meta):
...
def __getattribute__(self, name):
attr = object.__getattribute__(self, name)
if callable(attr) and not name.startswith('_'):
if attr.__self__ == self:
attr = decorator(attr)
else:
attr = Meta.__getattribute__(Thing, name)
return attr
The above metaclass=Meta
is Python 3 syntax. In Python 2, it must be defined as:
class Thing(object):
__metaclass__ = Meta
You can use decorators (if you don't know them you can refer to PEP-318):
def decorator(method):
def decorated_method(self, *args, **kwargs):
# before the method call
if self.busy:
return None
self.busy = True
# the actual method call
result = method(self, *args, **kwargs)
# after the method call
self.busy = False
return result
return decorated_method
class Thing():
def __init__(self):
self.busy = False
@decorator
def func_1(self):
...
@decorator
def func_2(self):
...
You might want to use functools.wraps
if you want the decorated method to "look like" the original method. The @decorator
is just syntactic sugar, you could also apply the decorator explicitly:
class Thing():
def __init__(self):
self.busy = False
def func_1(self):
...
func_1 = decorator(func_1) # replace "func_1" with the decorated "func_1"
In case you really want to apply it to all methods you can additionally use a class decorator:
def decorate_all_methods(cls):
for name, method in cls.__dict__.items():
if name.startswith('_'): # don't decorate private functions
continue
setattr(cls, name, decorator(method))
return cls
@decorate_all_methods
class Thing():
def __init__(self):
self.busy = False
def func_1(self):
...
def func_2(self):
...
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