Package: classy-decorators
Classy DecoratorsHassle-free creation of decorators for functions and methods, OO-style.FeaturesOne decorator to rule them all; it works on functions, methods,
classmethods and staticmethods.Easily define parameters with a dataclass-like annotated attribute syntax.Runtime type-checking of decorator paremeters.For decorators with all-default or no parameters, parentheses are optional:@spam() == @spamInheritance is supported.Any decorator parameters and instance attributes are accessible (as copies)
in bound methods as well; no need to worry about those pesky descriptors.MyPy compatible and 100% test coverage.DependenciesPython 3.8 or 3.9.Installpipinstallclassy-decoratorsfor better runtime type checking:pipinstallclassy-decorators[typeguard]UsageThe following code can also be found in theexamples.Create a decorator by subclassingclassy_decorators.Decorator.
You can override the the decorated function or method using the__call_inner__method (__call__is meant for internal use only and should
not be used for this).Simple decoratorfromclassy_decoratorsimportDecoratorclassDouble(Decorator):def__call_inner__(self,*args,**kwargs)->float:returnsuper().__call_inner__(*args,**kwargs)*2To see it in action, let's decorate a function:@Doubledefadd(a,b):returna+bassertadd(7,14)==42You can also decorate methods and classmethods:classAddConstant:default_constant=319def__init__(self,constant=default_constant):self.constant=constant@Doubledefadd_to(self,value):returnvalue+self.constant@Double@classmethoddefadd_default(cls,value):returnvalue+cls.default_constantassertAddConstant(7).add_to(14)==42assertAddConstant.add_default(14)==666Decorator parametersOurDoubledecorator is pretty nice, but we can do better! So let's create a
decorator that is able to multiply results by any number instead of only by2:classMultiply(Decorator):factor:intdef__call_inner__(self,*args,**kwargs)->float:returnsuper().__call_inner__(*args,**kwargs)*self.factorBy simply setting the type-annotatedfactorattribute, we can use it as
decorator parameter. If you are familiar withdataclasses, you can see
that this is very similar to defining dataclass fields.@Multiply(2)defadd_and_double(a,b):returna+b@Multiply(factor=3)defadd_and_triple(a,b):returna+bassertadd_and_double(8,15)==46assertadd_and_triple(8,15)==69Default parameters and inheritanceIt's classy to be DRY, so let's combine ourDoubleandMultiplydecorators
into one that multiplies by2, unless specified otherwise:classDoubleOrMultiply(Multiply):factor=2@DoubleOrMultiplydefadd_and_double(a,b):returna+b@DoubleOrMultiply(factor=3)defadd_and_triple(a,b):returna+bassertadd_and_double(7,14)==42assertadd_and_triple(8,15)==69Advanced dataclass methodsTheDecoratorbase class provided, aside from__call_inner__, two other
interface methods you can override:Decorator.__decorate__(self, **params), which is called just after a
function method is decorated, with all decorator parameter values or defaults as
keyword arguments, i.e.DoubleOrMultiply.__decorate__(self, factor: int = 2).Decorator.__bind__(self, instance_or_class), which is called when a method
(not for functions), is bound to an instance, or when a class/static method is
bound to a class.Additionally, these properties can be used for figuring out what's been
decorated:is_functionis_method; either an instance, class- or static methodis_instancemethodis_classmethodis_staticmethodAnd for methods,is_boundandis_unboundare provided.If you're looking for the original wrapped function, you can find it at__func__.Classy, eh?