Статьи

Python: Six, ABC и functools.wraps

За последнюю неделю я просмотрел некоторый код на Python в  Stackstorm  (размещенный на  GitHub ), и благодаря этому я узнал несколько интересных новинок . Может быть, «новый» — не правильный термин, но эти вещи были новыми для меня, и я хотел поделиться ими с вами.

Код, на который я ссылаюсь, находится в нескольких местах Stackstorm,  первый элемент :

@six.add_metaclass(abc.ABCMeta)
class Access(object):

Две вещи здесь:

1.  six.add_metaclass  — делает  Access  метаклассом таким образом, который совместим как с Python2, так и с Python3. В целом, шесть пакетов имеют целью помочь разработчикам перенести их код с Python 2 на Python 3, написав код, совместимый с обоими. Отсюда и название: шесть = 2 * 3. Чтобы узнать больше:  https://pypi.python.org/pypi/six

2.  abc.ABCMeta  — а еще один пакет встроенного, его цель состоит в том, чтобы обеспечить поддержку  A ВТОРЕФЕРАТ  B азных  C Девушек. Делая абстракцию класса, вы гарантируете, что никто не сможет создать из него объекты. Это полезно для (как минимум) трех целей:

а. Поддерживать логику, которая является общей для нескольких классов в одном месте

б. заставить пользователей реализовывать методы, аннотированные  @ abc.abstractmethod  (вы также можете заставить наследующие классы создавать определенные члены класса, используя аннотацию @ abc.abstractproperty ). Для получения дополнительной информации в отношении см.  Документы .

с. создавая синглтон. Я до сих пор не убежден в полезности создания синглтона в Python (поскольку у нас есть модули), но, если вы захотите это сделать, вы можете либо создать метакласс и вызвать его методы как ClassName.method (), либо расширить метакласс но  не  реализовывать абстрактный метод или абстрактный свойства. Без реализации абстрактных методов / свойств наследующий класс также станет абстрактным. Этот метод используется в нескольких разных местах в StackStorm. Если вы хотите увидеть несколько примеров, ищите классы, наследуемые от  Access .

3.  Это  заняло у меня некоторое время, чтобы обернуть голову (для этого требуется ум «функционального программирования»):

def decorate(f):
        @functools.wraps(f)
        def callfunction(*args, **kwargs):

Называя  @ functools.wraps (е)  мы используем декоратор внутри декоратора. Не тривиально …

Чтобы понять основную идею «functools.wraps», проще пройти через следующие пункты / процесс (по крайней мере, для меня проще…):

  • Когда вы декорируете функцию, вы создаете новую функцию, которая оборачивает «оригинальную» и возвращает обертку

  • Обертка используется для того, чтобы иметь возможность выполнять все виды задач до / после применения «исходной» функции. Примеры: подготовка аргументов для «исходной функции», определение времени выполнения функции, добавление журналов печати до и после и т. Д.

  • Скажем, у вас есть обернутая функция f, возвращенная из декоратора, затем пользователь увидит  описание  (метаданные) декоратора вместо f, исходной функции.

  • Чтобы избежать этой путаницы,   на рисунке появляется @ functools.wraps (f) : аннотируя функцию-обертку с помощью @ functools.wraps (f) — вы убедитесь, что пользователь обертки увидит то же описание / метаданные как исходная функция. Под «описанием» я подразумеваю сигнатуру функции (имя функции и имена аргументов, которые она принимает).

Надеюсь, это полезно!