#!/usr/bin/env python """ decorama -- demo of 8 combinations of decorator implementation decorators applied to functions: 1. decorator without args @deco_func decorator returns replacement function 2. decorator with args @deco_func(arg, ...) decorator returns wrapper wrapper returns replacement function 3. class without args @deco_class __call__ IS replacement function 4. class with args @deco_class(arg, ...) __call__ RETURNS replacement function #TODO: straighten this mess out decorators applied to classes: (NOTE: replacement class is frequently the original class, but doesn't have to be) 5. decorator function without args @deco_func decorator returns replacement class 6. decorator function with args @deco_func(arg, ...) decorator returns wrapper wrapper return replacement class 7. decorator class without args @deco_class __call__ returns replacement class 8. decorator class with args @deco_class(arg, ...) ??? """ from functools import wraps, update_wrapper def deco_one(wrapped_function): """ function without params decorating a function -- the simplest kind of decorator @deco_one def bar(): pass same as bar = deco_one(bar) :param wrapped_function: function to decorate :return: replacement function """ @wraps(wrapped_function) def _replacement(*args, **kwargs): print("GREETINGS from deco_one!") result = wrapped_function(*args, **kwargs) return result + 1 return _replacement @deco_one def target_one(): print("Hello from target_one") return 1 print('-' * 60) print("** deco_one() **") print('-' * 60) result = target_one() print("target_one() result is:", result) result = target_one() print("target_one() result is:", result) print('-' * 60) def deco_two(animal): """ function with params decorating a function The decorator gets the args, and then returns a wrapper function. The wrapper gets the function being wrapped, and returns the replacement function. @deco_two('some string') def bar(): pass same as bar = deco_two('some_string')(bar) or, wrapper = foo('blah') bar = wrapper(bar) :param value: decorator parameter :return: wrapper function (which returns replacement function) """ def wrapper(wrapped_function): @wraps(wrapped_function) def _replacement(*args, **kwargs): print(("GREETINGS from deco_two -- animal is {}!".format(animal))) result = wrapped_function(*args, **kwargs) return result return _replacement return wrapper @deco_two('wombat') def target_two_alpha(): print("GREETINGS from target_two_alpha") @deco_two('coatimundi') def target_two_beta(): print("GREETINGS from target_two_beta") target_two_alpha() target_two_alpha() target_two_beta() class DecoThree(): """ class without params decorating a function __call__() is the replacement function __init__() is passed the wrapped function @DecoThree def bar(): pass same as bar = DecoThree(bar) """ def __init__(self, wrapped_function): self.__name__ = wrapped_function.__name__ self._wrapped = wrapped_function def __call__(self, *args, **kwargs): print("GREETINGS from deco_three!") result = self._wrapped(*args, **kwargs) return result + 3 class DecoFour(): """ class with params decorating a function __call__() RETURNS the replacement function @deco_four('blah') def bar(): pass same as bar = deco_four('blah')(bar) or, wrapper = deco_four('blah') bar = wrapper(bar) """ def __init__(self, value): self._value = value def __call__(self, wrapped_function): @wraps(wrapped_function) def _replacement(*args, **kwargs): print(("GREETINGS from deco_four -- value is {}!".format(self._value))) result = wrapped_function(*args, **kwargs) return result + 4 return _replacement print("Function decorators:") @deco_one @deco_two('APPLE') @DecoThree @DecoFour('BANANA') def target_function(color, value): """ Target function for decorators 1-4 :param color: Color as string :param value: Any value :return: None """ print(("Hello from target_function -- color is {} and value is {}".format(color, value))) print(("Target function's name is", target_function.__name__)) return 10 * value TF_RESULT = target_function('red', 10) print(("RESULT is", TF_RESULT)) print(('-' * 50)) TF_RESULT = target_function('green', 45) print(("RESULT is", TF_RESULT)) print(('-' * 50)) print() print() def deco_five(target_class): """ function without params decorating a class :param target_class: class to be decorated :return: modified class """ print("GREETINGS from deco_five!") @property def _temp(self): return self._value1 target_class.value_one = _temp return target_class def deco_six(fruit): """ function with params decorating a class; returns wrapper which is applied to target class :param fruit: :return: modified class """ print("GREETINGS from deco_six!") def wrapper(target_class): target_class._fruit = fruit @property def _temp(self): return self._fruit target_class.fruit = _temp return target_class return wrapper @deco_five @deco_six('MANGO') class TargetClass(): def __init__(self, v1, v2, v3, v4): self._value1 = v1 self._value2 = v2 self._value3 = v3 self._value4 = v4 T1 = TargetClass('fee', 'fi', 'fo', 'fum') print(("T1 is", T1)) print(("value_one:", T1.value_one)) print(('-' * 50)) T2 = TargetClass('eeny', 'meeny', 'miny', 'mo') print(("T2:", T2)) print(("T2.value_one:", T2.value_one)) print(("T2.fruit:", T2.fruit)) print(('-' * 50)) class DecoSeven(): """ class without params decorating a class __new__() returns the modified class (not __init__, because __init__ is *instance* initializer) """ def __new__(cls, class_): # self is the old class print("GREETINGS from deco_seven!") class_.color = 'blue' return class_ @DecoSeven class TargetClass(): pass T1 = TargetClass() print((T1.color)) print(("T1 id:", id(T1))) print((TargetClass.__name__, T1)) T2 = TargetClass() print((T2.color)) print(("T2 id:", id(T2))) print(('-' * 50)) class DecoEight(): """ class with params decorating a class __call__() returns the modified class """ def __init__(self, color): print("GREETINGS from deco_eight!") self._color = color def __call__(self, old_class): old_class.color = self._color return old_class @DecoEight('purple') class TargetClass(): pass T1 = TargetClass() print((T1.color))