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

为什么我不能以这种方式将关键字参数与装饰器一起使用?我做错了什么,你能告诉我我应该怎么做吗?

4

3 回答 3

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 函数进行评估。然后将结果用作装饰器。

于 2010-06-13T14:39:44.150 回答
5

您想部分应用该功能addValue- 提供val参数,但不提供func. 通常有两种方法可以做到这一点:

第一个称为currying并在 interjay 的回答中使用:您编写第一个 arg 的函数而不是具有两个参数f(a,b) -> res的函数,该函数返回另一个采用第二个 arg 的函数g(a) -> (h(b) -> res)

另一种方式是functools.partial对象。它使用对函数的检查来确定函数需要运行哪些参数(在您的情况下为funcval)。您可以在创建分部时添加额外的参数,一旦调用分部,它就会使用给定的所有额外参数。

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

对于这个偏应用问题,偏函数通常是一个更简单的解决方案,尤其是在有多个参数的情况下。

于 2010-06-13T15:08:05.197 回答
3

具有任何类型参数的装饰器- 命名/关键字参数,未命名/位置参数,或其中的一些 - 本质上,您在线调用@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 ...
于 2010-06-13T15:15:28.700 回答