<jupyter_start><jupyter_text>Decorator Application (Decorator Class) If you recalls how we wrote a parameterized decorator, we had to write a decorator factory that took in the arguments for our decorator and then returned the decorator (which could reference the arguments as free variables).Very simply:<jupyter_code>def my_dec(a, b):
    def dec(fn):
        def inner(*args, **kwargs):
            print('decorated function called: a={0}, b={1}'.format(a, b))
            return fn(*args, **kwargs)
        return inner
    return dec
@my_dec(10, 20)
def my_func(s):
    print('hello {0}'.format(s))
my_func('world')<jupyter_output>decorated function called: a=10, b=20
hello world<jupyter_text>So, our decorator factory was passed some arguments, and returned a callable which took one single parameter, the function being decorated, but also had access to the arguments passed to the factory. Now, recall that we can make our class instances callable, simply by implementing the `__call__` method.Here's a simple example:<jupyter_code>class MyClass:
    def __init__(self, a, b):
        self.a = a
        self.b = b
        
    def __call__(self):
        print('MyClass instance called: a={0}, b={1}'.format(self.a, self.b))
my_class = MyClass(10, 20)
my_class()<jupyter_output>MyClass instance called: a=10, b=20<jupyter_text>So let's modify this just a bit, and have the `__call__` method be our decorator!<jupyter_code>class MyClass:
    def __init__(self, a, b):
        self.a = a
        self.b = b
        
    def __call__(self, fn):
        def inner(*args, **kwargs):
            print('MyClass instance called: a={0}, b={1}'.format(self.a, self.b))
            return fn(*args, **kwargs)
        return inner<jupyter_output><empty_output><jupyter_text>So, we can decorate our functions this way:<jupyter_code>@MyClass(10, 20)
def my_func(s):
    print('Hello {0}!'.format(s))<jupyter_output><empty_output><jupyter_text>Remember that `@MyClass(10, 20)` returned an object of type `MyClass`. But  that object is itself callable, so we could do something like:``my_func = MyClass(10, 20)(my_func)``or, more simply``@MyClass(10, 20)def my_func(s):    print(s)``<jupyter_code>my_func('Python')<jupyter_output>MyClass instance called: a=10, b=20
Hello Python!
{'fraction_non_alphanumeric': 0.11829375549692173, 'fraction_numerical': 0.019349164467897976, 'mean_word_length': 4.254041570438799, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '323925', 'license': 'MIT', 'n_tokens_mistral': 767, 'n_tokens_neox': 738, 'n_words': 236, 'path': 'Part1/Section 07 - Scopes, Closures and Decorators/11 - Decorator Application - Decorator Class.ipynb', 'repo_name': 'bondxue/python-deepdive-master', 'size': '2274'}