Шаг 125.
Python: сборник рецептов.
Функции. Замена классов с одним методом функциями

    На этом шаге мы рассмотрим пример такого случая.

Задача

    У вас есть класс, который определяет только один метод, кроме __init__(). Однако для упрощения вашего кода вы бы хотели заменить его на простую функцию.

Решение

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

from urllib.request import urlopen

class UrlTemplate:
    def __init__(self, template):
        self.template = template

    def open(self, **kwargs):
        return urlopen(self.template.format_map(kwargs))


# Пример использования. Скачать данные об акциях с Yahoo
yahoo = UrlTemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
for line in yahoo.open(names='IBM,AAPL,FB', fields='sl1c1v'):
    print(line.decode('utf-8'))

    Этот класс может быть заменен намного более простой функцией:

def urltemplate(template): 
    def opener(**kwargs):
        return urlopen(template.format_map(kwargs)) 
    return opener

#  Пример использования
yahoo = urltemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}') 
for line in yahoo(names='IBM,AAPL,FB', fields='sl1c1v'): 
    print(line.decode('utf-8'))


Обсуждение

    Во многих случаях единственной причиной использовать класс с одним методом является необходимость сохранять дополнительное состояние для использования в методе. Например, единственное назначение класса UrlTemplate заключается в сохранении значения template, чтобы оно могло быть использовано в методе open().

    Использование вложенной функции (замыкания), как показано выше, часто будет намного более элегантным решением. Замыкание - это просто функция, но с дополнительным окружением переменных, которые используются внутри функции. Ключевое преимущество замыкания в том, что оно запоминает окружение, в котором было определено. Поэтому в примере функция opener() запоминает значение аргумента template() и использует его в последующих вызовах.

    Всякий раз, когда вы пишете код и встречаетесь с задачей прикрепления дополнительного состояния к функции, вспоминайте о замыканиях. Они часто являются более минималистичным и элегантным решением, нежели альтернатива (превращение функции в полноценный класс).

    На следующем шаге мы рассмотрим передачу дополнительного состояния с функциями обратного вызова.




Предыдущий шаг Содержание Следующий шаг