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

Как сделать бутерброд с помощью Python Context Manager

Enter, Python Context Manager! Они содержат волшебный соус для нашего бутерброда, который также абстрагирует управление ресурсами и помогает вам в письменной форме чистого кода.

Автор оригинала: 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

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 модуль заглянуть.

Спасибо за прочтение!