3.5. Class Decorator with Functions

3.5.1. Syntax

  • Decorator is a decorator name

  • my_function is a function name

Syntax:
@Decorator
def my_function(*args, **kwargs):
    pass
Is equivalent to:
my_function = Decorator(my_function)

3.5.2. Definition

class Decorator:
    def __init__(self, func):
        self._func = func

    def __call__(self, *args, **kwargs):
        return self._func(*args, **kwargs)
@Decorator
def echo(name):
    print(name)


echo('Mark Watney')
# Mark Watney

3.5.3. Examples

Listing 3.111. Login Check
class User:
    def __init__(self):
        self.is_authenticated = False

    def login(self, username, password):
        self.is_authenticated = True


class LoginCheck:
    def __init__(self, func):
        self._func = func

    def __call__(self, *args, **kwargs):
        if user.is_authenticated:
            return self._func(*args, **kwargs)
        else:
            print('Permission Denied')


@LoginCheck
def edit_profile():
    print('Editing profile...')


user = User()

edit_profile()
# Permission Denied

user.login('admin', 'MyVoiceIsMyPassword')

edit_profile()
# Editing profile...
Listing 3.112. Dict Cache
class Cache(dict):
    def __init__(self, func):
        self._func = func

    def __call__(self, *args):
        return self[args]

    def __missing__(self, key):
        self[key] = self._func(*key)
        return self[key]


@Cache
def my_function(a, b):
    return a * b


my_function(2, 4)           # 8         # Computed
my_function('hi', 3)        # 'hihihi'  # Computed
my_function('ha', 3)        # 'hahaha'  # Computed

my_function('ha', 3)        # 'hahaha'  # Fetched from cache
my_function('hi', 3)        # 'hihihi'  # Fetched from cache
my_function(2, 4)           # 8         # Fetched from cache
my_function(4, 2)           # 8         # Computed


my_function
# {
#   (2, 4): 8,
#   ('hi ', 3): 'hihihi',
#   ('ha', 3): 'hahaha',
#   (4, 2): 8,
# }

3.5.4. Assignments

3.5.4.1. Decorator Class Abspath

English
  1. Create function print_file(filename: str) -> str which prints file content (filename given as an argument)

  2. Create decorator ToAbsolutePath

  3. Decorator converts to absolute path (path + filename), if filename given as an argument is a relative path

Polish
  1. Stwórz funkcję print_file(filename: str) -> str która wyświetla zawartość pliku (nazwa pliku podana jako argument)

  2. Stwórz dekorator ToAbsolutePath

  3. Dekorator zamienia ścieżkę na bezwzględną (path + filename), jeżeli nazwa pliku podana jako argument jest względna

Hint
  • from pathlib import Path

  • current_directory = Path.cwd()

  • path = Path(current_directory, filename)

3.5.4.2. Decorator Class Type Check

English

Todo

English translation

Polish
  1. Użyj danych z sekcji "Input" (patrz poniżej)

  2. Stwórz dekorator - klasę CheckTypes

  3. Dekorator ma sprawdzać typy danych, wszystkich parametrów wchodzących do funkcji

  4. Jeżeli, którykolwiek się nie zgadza, wyrzuć wyjątek TypeError

  5. Wyjątek ma wypisywać:

    • nazwę parametru

    • typ, który parametr ma (nieprawidłowy)

    • typ, który był oczekiwany

Input
@CheckTypes
def isworking(a: str, b: int, c: float = 0) -> bool:
    return True
Tests
>>> isworking('hello', 1)
True

>>> isworking('hello', b=1)
True

>>> isworking(a='hello', b=1)
True

>>> isworking(b=1, a='hello')
True

>>> isworking(1, 'hello')
Traceback (most recent call last):
    ...
TypeError: Argument 1 is <class 'int'>, but <class 'str'> was expected

>>> isworking(1, b='hello')
Traceback (most recent call last):
    ...
TypeError: Argument 1 is <class 'int'>, but <class 'str'> was expected

>>> isworking(a=1, b='hello')
Traceback (most recent call last):
    ...
TypeError: Argument a is <class 'str'>, but <class 'str'> was expected

>>> isworking(b='hello', a=1)
Traceback (most recent call last):
    ...
TypeError: Argument b is <class 'str'>, but <class 'int'> was expected
Hint
echo.__annotations__
# {'a': <class 'str'>, 'b': <class 'int'>, 'c':  <class 'float'>, 'return': <class 'bool'>}