Автор оригинала: Vishal Gowda.
Большинство людей уклоняются от кухни, а не потому, что кулинария не весело, но потому Уборка после утомительного Отказ
Что если я сказал вам, что вы можете получить готовить без необходимости Явно Уборка? Надеюсь, что следует зацеплять!
С целью этого учебника мы собираемся сделать бутерброд. Указанный бутерброд будет содержать следующие ингредиенты:
- Хлеб
- Бекон
- Майонез
- Латук
Для краткости мы собираемся относиться к печати действий как эквивалент на самом деле. Мы можем определить наши задания, как так:
# the boilerplate tasks def fetch_ingredient(name): print "Fetching {0}...".format(name) return def shelve_ingredient(name): print "Shelving {0}...\n".format(name) # ignore the \n return
# don't pay too much attention to this function def pluralize_token(token, quantity): if quantity > 1: token += 's' return token # the core task at hand def add_ingredient(name, quantity, token='piece'): token = pluralize_token(token, quantity) print "Adding {0} {1} of {2}...\n".format( quantity, token, name ) return
Довольно самоуверенно, правда? Давайте двигаться дальше.
Теперь, если вам пришлось сделать этот бутерброд, вы, вероятно, в конечном итоге выполняете эти задачи в порядке:
fetch_ingredient.
add_ingredient.
Shelve_ingredient.
По сути, это будет выглядеть что-то подобное:
def make_me_a_sandwich(): fetch_ingredient('bread') add_ingredient('bread', 2, 'slice') fetch_ingredient('bacon') add_ingredient('bacon', 2, 'strip') fetch_ingredient('mayo') add_ingredient('mayo', 2, 'dab') fetch_ingredient('lettuce') add_ingredient('lettuce', 2) # can't forget to clean the kitchen shelve_ingredient('lettuce') shelve_ingredient('mayo') shelve_ingredient('bacon') shelve_ingredient('bread')
Фу! Это похоже на столько котельной, чтобы сделать что-то так просто.
Мы в основном хотим покончить с скучными задачами и сосредоточиться на нашей одной фундаментальной задаче – add_ingredient
Отказ
Войти, context_managers
Отказ Они содержат волшебный соус, который тезисывает управление ресурсами, который помогает вам написать чистый код.
Python Context Manager
Один из самых распространенных контекстных менеджеров (которые я уверен, что вы, должно быть, уже имели дело), будет открыть
Отказ
with open('sample.txt', 'r') as source: # observe that we move into a nested block to work with source print source.read() # now that we're outside the block if we tried some kind of file i/o # we'd get an exception, because the resource was already closed.
Конечно, здесь есть немного черной магии. Как мне даже начать что-то подобное?
Использование с
, мы можем назвать все, что возвращает менеджер контекста (например, встроенный Open ()
Функция). Мы назначаем его переменной, используя ... как
Отказ Важно, Переменная существует только внутри блока с отступом ниже с оператором Отказ Думать о с
В качестве создания мини-функции: мы можем свободно использовать переменную в отпуске, но после того, как этот блок заканчивается, переменная выходит из приспособления. Когда переменная выходит из приспособления, она автоматически вызывает специальный метод, который содержит код для очистки ресурса.
Так что в основном, используя контекстные менеджеры, вы можете выполнить код на входящий и Выход из блока.
Есть несколько способов создания собственного менеджера контекста. Наиболее распространенным способом было бы определить класс, который реализует эти два метода:
__enter__
: В этом вы размещаете код, который вы будете использовать, чтобы получить/открыть ресурс и в основном сделать его готов к потреблению/использованию. Примечание: Убедитесь, что эта функция возвращает рассматриваемое ресурс!
__exit__
: Здесь вы бы написали логику, чтобы восстановить/закрыть/очистить после использования ресурса.
Что будет выглядеть что-то вроде этого:
class Ingredient(object): def __init__(self, name, quantity=1, token=None): self.name = name self.quantity = quantity self.token = token or 'piece' def fetch_ingredient(self): print "Fetching {0.name}...".format(self) return def add_ingredient(self): token = self.pluralize_token() print "Adding {0.quantity} {1} of {0.name}...\n".format(self, token) return def pluralize_token(self): token = self.token if self.quantity > 1: token += 's' return token def shelve_ingredient(self): print "Shelving {0.name}...\n".format(self) return def __enter__(self): self.fetch_ingredient() return self def __exit__(self, *args): self.shelve_ingredient() return
Как видите, мы можем добиться инкапсуляции, определив методы на ресурсе, а также манируют очистку на __exit__
Отказ
На этот раз мы сделаем сэндвич, используя Ингредиент
Отказ
def make_me_another_sandwich(): with Ingredient(name="bread", quantity=2, token="slice") as bread: bread.add_ingredient() with Ingredient(name="bacon", quantity=2, token="strip") as bacon: bacon.add_ingredient() with Ingredient(name="mayo", quantity=2, token="dab") as tomato: tomato.add_ingredient() with Ingredient(name="lettuce", quantity=1) as lettuce: lettuce.add_ingredient() print '-' * 28 print "That's one helluva sandwich!" print '-' * 28 print print "...Woah! Kitchen's all clean."
Довольно круто, а?
Есть еще один удобный способ сделать контекстные менеджеры, используя более функциональный подход. На самом деле, есть целый стандартный библиотечный модуль только для этого. Мы просто украшаем функцию, используя @contextManager
И VOILA!
Чтобы использовать его, украсить функцию генератора, которая вызывает данную ровно один раз. Все до вызова к уродам считается кодом для Введите (). Все после того, как код для Выход ()
Давайте переопределим Ингредиент
используя это.
from contextlib import contextmanager @contextmanager def ingredient(name): fetch_ingredient(name) yield # return control to inner block where add_ingredient is called shelve_ingredient(name) # usage with ingredient('bread'): add_ingredient('bread', 2, 'slice')
Надеюсь, у вас должен быть хорошее понимание того, какие контекстные менеджеры и как они работают. То, что я показал, был надуманным еще простым иллюстрацией. Что касается реальных случаев использования, контекстные менеджеры используются для удаления раздува (в целом – сделайте другие разработчики простыми) и обеспечить очистку при обработке ресурсов (думаю, что дескрипторы файлов, соединения сокетов и т. Д.). Если вы хотите узнать больше, вы определенно должны дать Contextlib модуль заглянуть.
Спасибо за прочтение!