реклама
Бургер менюБургер меню

Кеннет Рейтц – Автостопом по Python (страница 36)

18

·

# Python 3

····py33: python3-memcached

····py34: python3-memcached

····py35: python3-memcached

·

whitelist_externals=

····redis-server

····memcached

····uwsgi

commands=

····normal: py.test []

····uwsgi: uwsgi

···········-pyrun {envbindir}/py.test

···········-pyargv — kUWSGI — cache2=name=werkzeugtest,items=20 — master

Примеры стиля из Werkzeug

В главе 4 мы уже рассмотрели большую часть принятых соглашений по стилю. Первый пример стиля в этом разделе демонстрирует элегантный способ угадать типы на основе строки, второй показывает, что вы можете использовать параметр VERBOSE при определении длинных регулярных выражений, поэтому другие пользователи смогут понять, что делает выражение, не затратив на это много времени.

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

Поиск в словарях с помощью ключей в Python использует соотнесение хэшей, как и поиск в множестве. Python не имеет операторов switch case. (Их отклонили как непопулярные в PEP 3103 (https://www.python.org/dev/peps/pep-3103/).) Вместо этого пользователи Python используют инструкцию if/elif/else или (как показано здесь) питонское решение — поиск в словаре.

Обратите внимание, что в первый раз попытка преобразования выполняется к более ограниченному типу int, перед тем как попытаться выполнить преобразование к типу float.

Питонским решением также является использование оператора try/except для infer type.

Эта часть необходима, поскольку код находится в файле werkzeug/routing.py, а анализируемая строка является частью URL. Здесь проверяется наличие кавычек, при обнаружении они удаляются.

text_type преобразовывает строки в формат Unicode таким образом, что они остаются совместимыми и с Python 2, и с Python 3. Этот код практически аналогичен функции u(), показанной в разделе «HowDoI» в начале главы 5.

Если вы используете в своем коде длинные регулярные выражения, не забывайте про параметр re.VERBOSE[69] — сделайте их более понятными для других людей. Пример регулярных выражений показан во фрагменте файла werkzeug/routing.py:

import re

····_rule_re = re.compile(r'''

····(?P<static>[^<]*) # static rule data

····<

····(?:

········(?P<converter>[a-zA-Z_][a-zA-Z0-9_]*) # имя преобразователя

········(?:\((?P<args>.*?)\))? # аргументы преобразователя

········\: # разделитель переменных

····)?

····(?P<variable>[a-zA-Z_][a-zA-Z0-9_]*) # имя переменной

····>

''', re.VERBOSE)

Примеры структуры из Werkzeug

В первых двух примерах, связанных со структурой, демонстрируются питонские способы использования динамической типизации. Мы предупреждали, что присваивание переменной разных значений может приводить к появлению проблем (см. подраздел «Динамическая типизация» раздела «Структурируем проект» главы 4), но не упомянули преимущества такого присваивания. Одно из них заключается в том, что вы можете использовать любой тип объекта, который ведет себя предсказуемо. Это называется утиной типизацией. Утиная типизация исповедует следующую философию: «Если что-то выглядит, как утка[70], крякает, как утка, то это и есть утка».

В обоих примерах используется возможность вызвать объекты, которые не являются функциями: вызов cached_property.__init__() позволяет проинициализировать экземпляры класса, чтобы их можно быть применять как обычные функции, а вызов Response.__call__() позволяет объекту класса Response вызвать как функцию самого себя.

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

Werkzeug применяет утиную типизацию для того, чтобы создать декоратор @cached_property. Когда мы говорили о свойстве, описывая проект Tablib, то упоминали, что оно похоже на функцию. Обычно декораторы являются функциями, но поскольку тип ничем не навязывается, они могут быть любым вызываемым объектом: свойство на самом деле является классом. (Вы можете сказать, что оно задумывалось как функция, поскольку его имя не начинается с прописной буквы, а в PEP 8 говорится, что имена классов должны начинаться c прописной буквы.) При использовании нотации, похожей на вызов функции (property()), будет вызван метод property.__init__() для инициализации и возврата экземпляра свойства — класс, для которого соответствующим образом определен метод __init__(), работает как вызываемая функция. Кря.

В следующем фрагменте кода содержится полное определение свойства cached_property, которое является подклассом класса property. Документация класса cached_property говорит сама за себя. Когда это свойство будет использоваться для декорирования BaseRequest.form в коде, который мы только что видели, instance.form будет иметь тип cached_property и с точки зрения пользователя будет вести себя как словарь, поскольку для него определены методы __get__() и __set__(). При получении доступа к BaseRequest.form в первый раз он считает данные формы (если она существует), а затем запишет их в instance.form.__dict__, чтобы к ним можно было получить доступ в дальнейшем:

class cached_property(property):

····"""Декоратор, который преобразует функцию в ленивое свойство.

········Обернутая функция в первый раз вызывается для получения результата,

········затем полученный результат используется при следующем обращении к value::

············class Foo(object):

················@cached_property

················def foo(self):

····················# выполняем какие-нибудь важные расчеты

····················return 42

········Класс должен иметь '__dict__' для того, чтобы это свойство работало.

········"""

········# деталь реализации: для подкласса, встроенного

········# в Python свойства-декоратора

········# мы переопределяем метод __get__ так, чтобы получать кэшированное

········# значение.

········# Если пользователь хочет вызвать метод __get__ вручную, свойство будет

········# работать как обычно, поскольку логика поиска реплицируется

········# в методе __get__ при вызове вручную.

········def __init__(self, func, name=None, doc=None):

············self.__name__ = name or func.__name__

············self.__module__ = func.__module__

············self.__doc__ = doc or func.__doc__

············self.func = func