Привет,
В этой короткой статье я хотел бы поговорить о контекстных менеджерах. Я лично считаю, что в основе они просто форма декораторов. Если вы не знаете, что декоратор, проверьте Декоратор Статья Википедии.
Декораторы могут быть использованы для реализации проблем перекрестных вырезов. У нас есть компонента, и нам нужны регистрация и безопасность, мы могли бы написать логику для ведения журнала и обработки безопасности в компоненте, но некоторые люди считают, что компонент 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”