装饰器中的命名关键字?

发布于 2024-09-05 23:52:22 字数 494 浏览 6 评论 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

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

I've been playing around in depth with attempting to write my own version of a memoizing decorator before I go looking at other people's code. It's more of an exercise in fun, honestly. However, in the course of playing around I've found I can't do something I want with decorators.

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

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

If I want to do that I have to do this:

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

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

Why can't I use keyword arguments with decorators in this manner? What am I doing wrong and can you show me how I should be doing it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

软甜啾 2024-09-12 23:52:22

您需要定义一个返回装饰器的函数:

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

当您编写@addTwo时,addTwo的值直接用作装饰器。但是,当您编写 @addValue(4) 时,首先通过调用 addValue 函数来计算 addValue(4)。然后将结果用作装饰器。

You need to define a function that returns a decorator:

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

When you write @addTwo, the value of addTwo is directly used as a decorator. However, when you write @addValue(4), first addValue(4) is evaluated by calling the addValue function. Then the result is used as a decorator.

心如狂蝶 2024-09-12 23:52:22

您想要部分应用函数addValue - 给出val参数,但不给出func。通常有两种方法可以做到这一点:

第一种称为“柯里化”(currying),并在 interjay 的回答中使用:f(a,b) -> 代替具有两个参数的函数。 res,您编写第一个参数的函数,该函数返回另一个采用第二个参数的函数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

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

You want to partially apply the function addValue - give the val argument, but not func. There are generally two ways to do this:

The first one is called currying and used in interjay's answer: instead of a function with two arguments, f(a,b) -> res, you write a function of the first arg that returns another function that takes the 2nd arg g(a) -> (h(b) -> res)

The other way is a functools.partial object. It uses inspection on the function to figure out what arguments a function needs to run (func and val in your case ). You can add extra arguments when creating a partial and once you call the partial, it uses all the extra arguments given.

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

Partials are usually a much simpler solution for this partial application problem, especially with more than one argument.

情未る 2024-09-12 23:52:22

具有任何种参数的装饰器——命名/关键字参数、未命名/位置参数或每种参数中的一些参数——本质上是你在@name上调用的参数 行而不仅仅是在那里提及——需要双层嵌套(而您刚刚提到的装饰器只有单层嵌套)。如果您想在 @ 行中调用它们,那么即使对于无参数的装饰器也是如此 - 这是最简单的、不执行任何操作的双嵌套装饰器

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

:请

@double()
def whatever ...

注意括号(在本例中为空,因为不需要也不想要任何参数):它们意味着您正在调用 double,它返回middling< /code>,它装饰whatever

一旦您了解了“调用”和“只是提及”之间的区别,添加(例如可选)命名参数并不难:

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

可以使用 as:

@doublet()
def whatever ...

或 as:

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

或等效为:

@doublet(45)
def whatever ...

Decorators with any kinds of arguments -- named/keyword ones, unnamed/positional ones, or some of each -- essentially, ones you call on the @name line rather than just mention there -- need a double level of nesting (while the decorators you just mention have a single level of nesting). That goes even for argument-less ones if you want to call them in the @ line -- here's the simplest, do-nothing, double-nested decorator:

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

You'd use this as

@double()
def whatever ...

note the parentheses (empty in this case since there are no arguments needed nor wanted): they mean you're calling double, which returns middling, which decorates whatever.

Once you've seen the difference between "calling" and "just mentioning", adding (e.g. optional) named args is not hard:

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

usable either as:

@doublet()
def whatever ...

or as:

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

or equivalently as:

@doublet(45)
def whatever ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文