理解装饰器:如果未指定参数,则返回类型是函数

3
我在两个独立的函数中使用了一个装饰器:一个带有装饰器参数的函数和另一个没有参数的函数。当不传递可选参数时,返回类型是一个函数(具体来说,是装饰器中的inner_function)。然而,当传递可选参数时,它按预期工作。
你能解释一下这里发生了什么以及为什么在这些情况下行为不同吗?
def cache_disk(cache_folder="./cache"):
    def wrapper(f):
        def inner_function(*args, **kwargs):
            result = f(*args, **kwargs)
            return result
        return inner_function
    return wrapper

@cache_disk
def func1(data):
    return [d for d in data]

@cache_disk(cache_folder='./cache/')
def func2(data):
    return [d for d in data]


data = [1,2,3]
print(func1(data))
print(func2(data))

结果:

<function inner_function at 0x7f1f283d5c08>
[1, 2, 3]
2个回答

3

请注意:

@decorator  # no arguments
def func(...):
    ...

等价于:

def func(...):
    ...

func = decorator(func)  # one 'level' of calls

并且:

@decorator(...):  # arguments
def func(...):
    ...

等同于:

def func(...):
    ...

func = decorator(...)(func)  # two 'levels' of calls

在第一种情况下,装饰器只有一个参数,即func本身。在第二种情况下,装饰器的参数是@行中的...,并且由装饰器返回的函数将以func作为参数调用。
在你的例子中,
@cache_disk
def func1(data):
    ...

装饰器cache_disk接受一个可调用参数(func,成为args[0]),并返回wrapper。然后当你调用:

print(func1(data))

wrapper接收一个参数(data, 该参数变为 f),并返回inner_function

因此,您有三个选择:

  1. 使用@cache_disk()(注意括号)装饰func1,向wrapper传递不带参数的cache_disk本身和func
  2. 更改cache_disk的行为,具体取决于它是否传递了单个可调用参数或其他内容;或者
  3. 正如@o11c在评论中指出的那样,使用例如cache_disk.wrapper = cache_disk()为不带参数的版本提供方便的别名,然后用@cache_disk.wrapper进行装饰。

值得注意的是,有些装饰器库使用 @x@x.y 来区分不同情况。 - o11c
1
在这种情况下,@o11c是不是允许@cache_disk.wrapper?还是有其他事情正在发生? - jonrsharpe
是的,就这么简单。cache_disk.wrapper = cache_disk() - o11c
虽然现在我再想一下,最常见的形式是什么?那应该是外部的。所以可能是反向的,这需要更多的工作来设置分配。 - o11c

1

如果您想要默认值,您需要调用该函数并返回装饰器:

@cache_disk()
def func1(data):
    return [d for d in data]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接