4.3. Function Decorator with Classes

4.3.1. Rationale

  • mydecorator is a decorator name

  • MyClass is a class name

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

4.3.2. Syntax

  • mydecorator is a decorator name

  • MyClass is a class name

Listing 4.60. 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
Listing 4.61. Decoration
@decorator
class MyClass:
    ...
Listing 4.62. Usage
my = MyClass()

4.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'

4.3.4. Use Case

Listing 4.63. Add logger attribute to class
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)>
Listing 4.64. 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>
Listing 4.65. 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>

4.3.5. Assignments

Todo

Create assignments