__decorated__ для питоновских декораторов - PullRequest
5 голосов
/ 14 августа 2010

Начиная с 2.4 (2.6 для классов), python позволяет вам украшать функцию другой функцией:

def d(func): return func

@d
def test(first): pass

Это удобный синтаксический сахар.Вы можете делать всякие аккуратные вещи с декораторами, не беспорядок.Однако, если вы хотите узнать оригинальную функцию, которая была украшена, вы должны прыгать через обручи (например, Cls.method.__func__.__closure__[0].cell_contents или хуже).

Я обнаружил, что желаю лучшего способа и обнаружил, что были некоторыеобсуждение python-dev о добавлении переменной с именем __decorated__ в функцию [new], возвращаемую декоратором.Тем не менее, похоже, что это никуда не делось.

Будучи приключенческим человеком и имея довольно большой опыт работы с Python около 4 лет, я подумал, что буду искать реализацию __decorated__ в исходном коде компилятора python,просто чтобы посмотреть, как это происходит.

Если честно, я никогда не углублялся в C под капотом, поэтому мои первые часы просто пытались понять, как работает лежащий в основе C-код.Итак, во-первых, , какие будут лучшие ресурсы, чтобы разобраться, что мне нужно изменить / добавить для __decorator__?

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

  1. Не добавляйте __decorator__.
  2. Добавьте __decorator__, но установите для него значение Нет.
  3. Добавьте __decorator__ и установите его на исходную функцию в любом случае.

Так что, если это произойдет, Как вы думаете, что будет лучшим вариантом?

ОБНОВЛЕНИЕ:

Кто-то еще привлек мое внимание сценарий, который я пропустил.Что происходит, когда декоратор не возвращает ни оригинальную функцию, ни функцию, которая оборачивает оригинал?На этом этапе ничто не содержит ссылку на исходную функцию, и она будет собирать мусор.(Спасибо Нечетное мышление !)

Так что в этом случае, я думаю, что я все еще пошел бы с третьим вариантом.Объект, возвращаемый декоратором, получит имя __decorated__, которое ссылается на исходную функцию.Это будет означать, что он не будет собирать мусор.

Мне кажется странным, что функция из определения класса полностью исчезнет, ​​потому что вы украсили ее.На мой взгляд, это еще одна причина использовать атрибут __decorated__ для каждого декоратора.Тем не менее, более вероятно, что моя интуиция ошибочна и что нынешнее поведение - это то, чего большинство людей ожидают. Есть мысли?

ps это продолжение более раннего, более общего вопроса , который у меня был.Я также получил дополнительную информацию о первой части с отдельным постом .

Ответы [ 2 ]

2 голосов
/ 14 августа 2010

Что ж, независимо от каких-либо обсуждений того, является ли это хорошей идеей, я бы выбрал вариант № 3, потому что он наиболее последовательный: он всегда показывает, что функция была украшена наличием атрибута, и доступ к Значение атрибута всегда возвращает функцию, поэтому вам не нужно проверять ее по None.

Вы также должны учитывать это: что бы вы предложили сделать с ручным оформлением? например,

def test(first): pass
test = d(test)

Что касается первой части вопроса, я не очень много смотрел на исходный код интерпретатора Python, поэтому не смог бы указать вам на что-нибудь особенно полезное.

1 голос
/ 14 августа 2010

Я не стесняюсь использовать какой-то отдельный атрибут "private" с подчеркиванием, например _mydecor

возможное мульти-декораторское решение:

def decorate(decoration): 
    def do_decor(func): 
        if hasattr(func, '_mydecor'): 
            func._mydecor.add(decoration) 
        else: 
            func._mydecor = set([decoration]) 
        return func 
    return do_decor 

def isDecorated(func, decoration): 
    return (decoration in getattr(func, '_mydecor', set())) 

@decorate('red') 
@decorate('green') 
def orangefunc(): pass 

print isDecorated(orangefunc, 'green') # -> True 
print isDecorated(orangefunc, 'blue')  # -> False 
...