Именованные ключевые слова в декораторах? - PullRequest
0 голосов
/ 13 июня 2010

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

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

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

Ответы [ 3 ]

5 голосов
/ 13 июня 2010

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

Первый называется curry и используется в ответе 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

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

5 голосов
/ 13 июня 2010

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

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

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

3 голосов
/ 13 июня 2010

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

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 ...
...