Is it possible to modify a class so as to make available a certain method decorator, without having to explicitly import it and without having to prefix it (@something.some_decorator
):
class SomeClass:
@some_decorator
def some_method(self):
pass
I don't think this is possible with a class decorator, because that is applied too late. The option that seems more promising is using a metaclass, but I am unsure how, my guess is that I would have to have some_decorator
be introduced into the namespace of SomeClass
.
Thanks to @MartijnPieters for pointing out that staticmethod
and classmethod
are built-ins. I had expected them to be part of the type
machinery.
To be clear, I don't have any explicit use-case for this, I am just curious as to whether this is at all possible.
ADDENDUM, now that the question has been answered. The original reason why I was looking beyond simply importing or defining a decorator locally, was that I had defined a decorator that would only work if a certain container attribute was initialised on an object, and I was looking for a way to tie enforcing this to the availability of the decorator. I ended up checking whether the attribute existed and if not initialising it within the decorator, which may very well be the lesser evil here.
Yes, in Python 3 you can use the metaclass __prepare__
hook. It is expected to return a mapping, and it forms the basis of the local namespace for the class body:
def some_decorator(f):
print(f'Decorating {f.__name__}')
return f
class meta(type):
@classmethod
def __prepare__(mcls, name, bases, **kw):
return {'some_decorator': some_decorator}
class SomeClass(metaclass=meta):
@some_decorator
def some_method(self):
pass
Running the above produces
Decorating some_method
However, you should not use this. As the Zen of Python states: Explicit is better than implicit, and introducing magic names into your classes can easily lead to confusion and bugs. Importing a metaclass is no different than importing a decorator, you replaced one name with another.
A class decorator can still apply other decorators to methods on a class after the class body has been created. The @decorator
syntax is just syntactic sugar for name = decorator(decorated_object)
, you can always apply a decorator later on by using name = decorator(name)
, or in a class context, as cls.name = decorator(cls.name)
. If you need to pick and choose which methods this should apply to, you could pick criteria like the method name, or attributes set on the method, or the docstring of the method, etc. Or just use the decorator directly on the methods.
As best I can tell a metaclass can do this. You somehow need to get the decorators available to the metaclass, possibly by importing, and then you can include them in the prepared namespace:
class includewraps(type):
def prepare(*args):
from functools import wraps
return {'wraps': wraps}
class haswraps (metaclass = includewraps):
# wraps is available in this scope
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