2010-06-13 2 views
0

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

def addValue(func, val): 
    def add(x): 
     return func(x) + val 
    return add 

@addValue(val=4) 
def computeSomething(x): 
    #function gets defined 

Если я хочу сделать, что я должен сделать это:

def addTwo(func): 
    return addValue(func, 2) 

@addTwo 
def computeSomething(x): 
    #function gets defined 

Почему я не могу использовать именованные аргументы с декораторов таким образом? Что я делаю неправильно, и вы можете показать мне, как я должен это делать?

ответ

5

Вы должны определить функцию, которая возвращает декоратор:

def addValue(val): 
    def decorator(func): 
     def add(x): 
      return func(x) + val 
     return add 
    return decorator 

Когда вы пишете @addTwo значение addTwo непосредственно используются в качестве декоратора. Однако, когда вы пишете @addValue(4), сначала addValue(4) оценивается путем вызова функции addValue. Затем результат используется как декоратор.

+0

Значит, вы говорите, что в декораторах на Python есть нетерпеливая оценка? Какой облом. – wheaties

5

Вы хотите частично применить функцию addValue - дать val аргумент, но не func. Есть два основных способа сделать это:

Первый называется Карринг и используемые в ответ interjay в: вместо функции с двумя аргументами, f(a,b) -> res, вы пишете функцию первого арг, который возвращает другую функцию, которая занимает 2-й арг g(a) -> (h(b) -> res)

Другой способ - это объект functools.partial. Он использует проверку функции, чтобы выяснить, какие аргументы должна выполнять функция (func и val в вашем случае). Вы можете добавить дополнительные аргументы при создании частичного, и как только вы вызываете частичное, он использует все дополнительные аргументы.

from functools import partial 
@partial(addValue, val=2) # you can call this addTwo 
def computeSomething(x): 
    return x 

Partials, как правило, гораздо более простое решение для этой частичной применения проблемы, особенно с более чем одним аргументом.

+0

functools.partial, очень приятно. Я знал, что есть простой способ добиться успеха в Python без тройного отступа. Большое спасибо. – wheaties

3

Декораторов с любых видами аргументов - именованные/ключевыми словами из них, безымянных/позиционных из них, или некоторые из каждого - по существу, те, которые вы звонка звонков на @name линии, а не просто упомянуть, там - нужен double уровень гнездования (в то время как декораторы, о которых вы только что упомянули, имеют один уровень гнездования).Это касается даже аргумент меньше тех, если вы хотите вызова их в @ линии - вот самый простой, сделай ничего, дважды вложенная декоратор:

def double(): 
    def middling(): 
    def inner(f): 
     return f 
    return inner 
    return middling 

Вы бы использовать это как

@double() 
def whatever ... 

записных скобки (пусто в этом случае, так как нет никаких необходимых аргументов и не хотели): они означают, что вы вызовdouble, который возвращает middling, который украшает whatever.

После того, как вы увидели разницу между "вызовом" и "просто упомянуть", добавив, (например, необязательные), названные арг не трудно:

def doublet(foo=23): 
    def middling(): 
    def inner(f): 
     return f 
    return inner 
    return middling 

использоваться либо как:

@doublet() 
def whatever ... 

или как:

@doublet(foo=45) 
def whatever ... 

или, что эквивалентно, как:

@doublet(45) 
def whatever ... 
Смежные вопросы