Pull to refresh

Comments 16

Уточнение:
Лучше всё таки передавать в декорируемую функцию не только позиционные, но и именованные аргументы.
То есть, вместо
decorated(*x)
    ...
    fn(*x)
Писать
decorated(*x, **y)
    ...
    fn(*x, **y)
Иначе функции после декорирования могут «вести себя неоднозначно».
Дополню: де-факто используют имена *args, **kwargs.
Думаю, что стоит в примерах использовать функцию functools.wraps. Это хорошая практика, и стоит к ней приучать.
Я вот хоть убейте не пойму, нафиг это надо. Какой в этом смысл, если декорируется _объявление_ функции? Т.е. я не могу, например, импортнуть функцию из другого файла и задекорировать ее. Также я не могу декорировать разными декораторами вызов одной и той же функции. Что мне мешает нужное поведение реализовать в самом телефункции? В чем радость от «внутри самой функции ничего не изменилось»?

Более того, если ты уже объявил задекорированную функцию, ты уже несможешь ее «передекорировать»… Темный лес :-((
> Т.е. я не могу, например, импортнуть функцию из другого файла и задекорировать ее.
Верно, потому что декоратором называется оборачивание функции во время объявления. Вы можете обернуть функцию в любое другое время:
from module import func
func = decorator(func)

> Также я не могу декорировать разными декораторами вызов одной и той же функции.
Это опять же следует из определения. Раз декоратор является частью объявления функции, вы его не можете отменить. Точно так же как вы не можете например сделать так, чтобы одна функция возвращала разные значения. Но вы опять же можете обернут любым количеством разных функция.

> Что мне мешает нужное поведение реализовать в самом телефункции?
Например то, что это поведение слабо связано с логикой работы функции, или это поведение вам нужно для большого числа функций. Или даже для неизвестного числа функций (как non_atomic_requests на иллюстрации).
В принципе понятно, но, чувствую, пока не столкнешься с развесистым проектом, до конца не прочувствуешь. Просто вот это один из самых «длинных» моих скриптов: github.com/dccharacter/AHRS/blob/master/myVisualisation.py. Т.е. я так понимаю я просто не дорос до декораторов всяких.
Это опять же следует из определения. Раз декоратор является частью объявления функции, вы его не можете отменить. Точно так же как вы не можете например сделать так, чтобы одна функция возвращала разные значения. Но вы опять же можете обернут любым количеством разных функция.
Не совсем. Я здесь где‐то видел пример вытягивания оригинальной функции из пачки вложенных декораторов с помощью какого‐то хака. И хак, насколько я помню, предполагал, что все декораторы реализованы на замыканиях. А ведь есть ещё встроенные функции (написанные на C) и классы в качестве декораторов. Так что так лучше не делать.

Но есть причины, по которым оригинальную функцию доставать может быть нужно. Например, мне в powerline, понадобились для документации и автоматической проверки настроек значения по‐умолчанию оригинальной функции и названия аргументов. Пришлось сочинять свой аналог functools.wraps, который применяет сам functools.wraps, после чего сохраняет оригинальную функцию как значение атрибута powerline_origin и своё расширение для Sphinx, которое умеет с этим делом работать. Впрочем, ни первое, ни последнее практически не представляет проблемы: вместо велосипедостроения в последнем случае я просто использую своего наследника одного из классов autodoc и вызываю его же функцию setup. Желания найти статью и взять оттуда хак у меня не возникло.
Видимо не только у Вас появлялась подобная необходимость.
Начиная с 3.2 wraps сохраняет исходную функцию в аттрибуте __wrapped__.
> Я здесь где‐то видел пример вытягивания оригинальной функции из пачки вложенных
> декораторов с помощью какого‐то хака.
Мы так можем дойти и до того, что результат возвращаемой функции можно поменять, изменив байт-код.
Можно. В Python со всем, что написано на Python, можно делать что угодно, зачастую даже не изменяя байт‐код. Я как‐то обходил попытку IPython (до версии 0.11, после они такой фигнёй не страдали) запретить использовать юникод в rewrite prompt (это стрелочка вида ---->, показываемая если вы включили автовызов функций и набрали в строке, к примеру str: без скобок) с помощью своего класса со своими методами __str__ и __add__.

С помощью аналогичных хаков легко меняется и результат практически всех функций: утиная типизация и возможность переопределения операторов делают своё дело.

Кстати, что именно значит «поменять результат возвращаемой функции» и «одна функция, возвращающая разные значения»? Я так понял, что имеется ввиду возможность заставить функцию выдать результат, неожиданный для её автора, противоречащий тому, что функция должна делать. Ничего невозможного тут нет.

Декоратор, хоть и выглядит частью определения функции, ею не является и отменить его можно. В Python вообще можно сделать практически всё, что никогда не придёт в голову разработчику на более строгом языке. Это не нужно учитывать, если вы пишете библиотеку декораторов, но это нужно помнить, столкнувшись с недостатками чужого кода. А также делая заявления о невозможности чего‐либо. Если в C вы легко натолкнётесь на очередной системе на пользователя с hardened ядром и запретом изменять исполняемый код в памяти без специального разрешения, таким образом обосновав невозможность залезть в чужую функцию, то в Python такого нет и написать хак можно всегда, пока не задействованы C расширения.
Python вообще удивителен в этом плане: возможностей для создания хаков гораздо больше, чем в любом другом известном мне языке, а отношение к ним более терпимое. Конечно, такие трюки — последнее, что вам следует использовать, но в примере выше между «ставьте пропатченую версию IPython», «мы не поддерживаем версии ≤0.11» и «у нас есть хак для IPython <0.11» я уверенно буду выбирать последнее (пока разработчики Gentoo не объявят что‐то из >¹0.11 стабильной версией). Соответственно и не буду давать забывать о таких возможностях другим: иногда они очень полезны.

¹ Знаки ≤, < (без =) и > (тоже без =) не случайны: сама 0.11 является переходной версией и потому не поддерживается.
UFO just landed and posted this here
Понял, вопросы по теме статьи задавать нельзя. Надо картинки из футурамы постить.
Кстати, почему не пришли оповещения от хабры о камментах?
Понял, вопросы по теме статьи задавать нельзя. Надо картинки из футурамы постить.
Кстати, почему не пришли оповещения от хабры о камментах?
Sign up to leave a comment.

Articles