Рубрики
Без рубрики

Контекстные менеджеры и перекрестные проблемы в Python

Мои контекстные менеджеры Python и как использовать их для перекрестных проблем. Tagged с Python, начинающими, архитектурой.

Привет,

В этой короткой статье я хотел бы поговорить о контекстных менеджерах. Я лично считаю, что в основе они просто форма декораторов. Если вы не знаете, что декоратор, проверьте Декоратор Статья Википедии.

Декораторы могут быть использованы для реализации проблем перекрестных вырезов. У нас есть компонента, и нам нужны регистрация и безопасность, мы могли бы написать логику для ведения журнала и обработки безопасности в компоненте, но некоторые люди считают, что компонент A должен быть компонентом, а не ComponentAtallalsoknowsaboutsecurity и. Поскольку компонент не несет ответственности за авторизацию запросов или журнала вызовов на внешнюю службу журнала, мы можем обернуть компонент в декоратор, который делает именно это.

Формальное определение для перекрестных проблем, взятых из Википедии, является следующим:

«В разработке программного обеспечения, ориентированной на аспект, перекрестные проблемы -это аспекты программы, которая влияет на другие проблемы Эти опасения часто не могут быть чистыми разложенными из остальной части системы как в проектировании, так и в реализации, и могут привести к либо рассеянию (дублирование кода), запутанное (значительные зависимости между системами) или оба ».

И некоторые примеры перекрестных проблем включают:

Поскольку контекстные менеджеры похожи на декораторы, вы можете использовать их для реализации перекрестных проблем. Давайте рассмотрим.

В Python вы можете иметь два типа контекстных менеджеров: функция и класс. Чтобы функция ведет себя как менеджер контекста, она должна быть украшена с помощью декоратора @ContextManager, и для того, чтобы класс ведут себя как менеджер контекста, который он должен реализовать Введите и Выход Анкет

Контекстные менеджеры можно назвать с помощью оператора. Следующий фрагмент кода демонстрирует двух контекстных менеджеров:

  • Тот, который входит в систему при вызове функции и когда она выходит.
  • Тот, который перехватывает аргументы функции.
from contextlib import contextmanager

@contextmanager
def simple_context_manager(function):
    try:
        print("calling function")
        yield function
    finally:
        print("function call has ended")

class SimpleContextManager:
    def __init__(self, cb):
        self.cb = cb

    def _intercept(self, *args, **kwargs):
        print(f"calling with {args} {kwargs}")
        return print(*args, **kwargs)

    def __enter__(self):
        print("intercept start")
        return self._intercept

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("intercept end")

def main():
    with simple_context_manager(print) as print_func:
        print_func("hi")

    with SimpleContextManager(print) as print_func:
        print_func("hi")
        print_func("hi", end="\n\n", sep=",")
        print_func("hi")

if __name__ == '__main__':
    main()

Что такое кэширование? Короче..

Кэширование используется для хранения результата дорогого вычисления где -то в памяти или на постоянном устройстве хранения, чтобы оптимизировать программу.

У нас есть функция compute_fibonacci, которая довольно медленная. Версия, которая использует кэш, реализовала в классе CachedComputefitefonacci. Обратите внимание, как код занимает некоторое время, чтобы вывести результат для первого оператора Call of Print (cached_compute_fibonacci (35)), но второй печатный

def compute_fibonacci(number):
    if number <= 1:
        return number
    return compute_fibonacci(number-1) + compute_fibonacci(number-2)


class CachedComputeFibonacci:
    def __init__(self):
        self._cache = {}

    def __call__(self, *args, **kwargs):
        number = args[0]
        if number in self._cache:
            return self._cache[number]
        result = compute_fibonacci(number)
        self._cache[number] = result
        return result

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

def main():
    # Non cached
    print(compute_fibonacci(10))

    # Cached
    with CachedComputeFibonacci() as cached_compute_fibonacci:
        print(cached_compute_fibonacci(35))
        print(cached_compute_fibonacci(35))



if __name__ == '__main__':
    main()

Регистрация может быть полезна для отладки и аудита.

def compute_fibonacci(number):
    if number <= 1:
        return number
    return compute_fibonacci(number-1) + compute_fibonacci(number-2)


class LoggedComputeFibonacci:
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print(f"calling compute_fibonacci with args={args} kwargs={kwargs}")
        result = compute_fibonacci(args[0])
        print(f"compute_fibonacci={result}")
        return result

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

def main():
    # Logging
    with LoggedComputeFibonacci() as logging_compute_fibonacci:
        print(logging_compute_fibonacci(35))
        print(logging_compute_fibonacci(36))



if __name__ == '__main__':
    main()

Если вы обнаружите, что дублируете одну и ту же логику Try/Catch в нескольких местах вашего кода, возможно, вы можете извлечь его в диспетчер контекста для обработки ошибок:

from contextlib import contextmanager

@contextmanager
def my_error_handler():
    try:
        yield
    except ZeroDivisionError:
        print("abort abort")

def main():
    # error handling
    with my_error_handler():
        print("0 / 0 =", 0 / 0)



if __name__ == '__main__':
    main()

По моему мнению, код определенно более чище.

Спасибо за чтение, и я надеюсь, что вы что -то узнали!

Оригинал: “https://dev.to/nuculabs_dev/context-managers-and-cross-cutting-concerns-in-python-4ae4”