6.3. Decorator Function with Cls

6.3.1. Rationale

  • mydecorator is a decorator name

  • MyClass is a class name

Syntax:
@mydecorator
class MyClass:
    ...
Is equivalent to:
MyClass = mydecorator(MyClass)

6.3.2. Syntax

  • mydecorator is a decorator name

  • MyClass is a class name

Definition:

def decorator(cls):
    class Wrapper(cls):
        def __new__(cls, *args, **kwargs):
            ...
    return Wrapper


def decorator(cls):
    def wrapper(*args, **kwargs):
        instance = cls.__new__(cls, *args, **kwargs)
        return instance
    return wrapper

Decoration:

@decorator
class MyClass:
    ...

Usage:

my = MyClass()

6.3.3. Example

def run(cls):
    def wrapper(*args, **kwargs):
        instance = cls.__new__(cls, *args, **kwargs)
        return instance
    return wrapper


@run
class Astronaut:
    def hello(self, name):
        return f'My name... {name}'


astro = Astronaut()
astro.hello('José Jiménez')
# 'My name... José Jiménez'

6.3.4. Use Case

Logger:

import logging

def logger(cls):
    class Wrapper(cls):
        logger = logging.getLogger(cls.__name__)
    return Wrapper


@logger
class Astronaut:
    pass


print(Astronaut.logger)
# <Logger Astronaut (WARNING)>

Object Birthday:

from time import time

def since(cls):
    class Wrapper(cls):
        _instance_created = time()
    return Wrapper


@since
class Astronaut:
    pass


print(Astronaut._instance_created)
# 1607187641.3407109

Singleton using functional wrapper:

def singleton(cls):
    def wrapper(*args, **kwargs):
        if not hasattr(cls, '_instance'):
            instance = object.__new__(cls, *args, **kwargs)
            setattr(cls, '_instance', instance)
        return getattr(cls, '_instance')
    return wrapper


@singleton
class DatabaseConnection:
    def connect(self):
        print(f'Connecting... using {self._instance}')


a = DatabaseConnection()  # Creating instance
a.connect()
# Connecting... using <__main__.DatabaseConnection object at 0x10cd56fa0>

b = DatabaseConnection()  # Reusing instance
b.connect()
# Connecting... using <__main__.DatabaseConnection object at 0x10cd56fa0>

Singleton using class wrapper:

def singleton(cls):
    class Wrapper(cls):
        def __new__(cls, *args, **kwargs):
            if not hasattr(cls, '_instance'):
                instance = object.__new__(cls, *args, **kwargs)
                setattr(cls, '_instance', instance)
            return getattr(cls, '_instance')
    return Wrapper


@singleton
class DatabaseConnection:
    def connect(self):
        print(f'Connecting... using {self._instance}')


a = DatabaseConnection()  # Creating instance
a.connect()
# Connecting... using <__main__.singleton.<locals>.Wrapper object at 0x1085b6fa0>

b = DatabaseConnection()  # Reusing instance
b.connect()
# Connecting... using <__main__.singleton.<locals>.Wrapper object at 0x1085b6fa0>

6.3.5. Assignments

Todo

Create assignments