为什么装饰器中需要包装函数?

问题描述 投票:0回答:3

如果我创建一个如下所示的装饰器:

def my_decorator(some_fun):
    def wrapper():
        print("before some_fun() is called.")
        some_fun()
        print("after some_fun() is called.")
    return wrapper

@my_decorator
def just_some_fun():
    print("Wheee!")

另一个装饰器可以定义为:

def my_decorator(some_fun):
    print("before some_fun() is called.")
    some_fun()
    print("after some_fun() is called.")

@my_decorator
def just_some_fun():
    print("some fun")

两个装饰器的工作方式相同。在装饰器中使用“包装器”功能有什么好处?我没明白目的。

python python-decorators
3个回答
19
投票

拥有包装函数的目的是函数装饰器接收要装饰的函数对象,并且它必须返回装饰后的函数。

您的

my_decorator
的第二个版本没有明确的
return
语句,因此它返回
None
。当通过
my_decorator
装饰器语法调用
@

before some_function() is called.
some fun
after some_function() is called.

被打印,然后

None
被分配给名称
just_some_fun
。因此,如果您将
print(just_some_fun)
添加到该代码的末尾,它将打印
None

如果我们摆脱

@
语法糖并使用正常的函数调用语法重新编写代码,可能会更容易理解发生了什么:

def my_decorator(some_fun):
    print("before some_function() is called.")
    some_fun()
    print("after some_function() is called.")

def just_some_fun():
    print("some fun")

just_some_fun = my_decorator(just_some_fun)

13
投票

Python 中的装饰器是可调用对象,在最简单的情况下是带有一个参数的函数,该参数是某个函数或类。装饰器应该再次返回它所采用的相同类型(因此,如果它采用函数,它应该返回函数)。重点是装饰器被调用的时间。

当您导入 Python 文件或直接运行它时,Python 解释器会遍历内容并收集有关定义了哪些类和函数的信息,如果在某些代码(而不是声明)上遇到它,它将执行它。

如果解释器在装饰器上遇到它需要装饰函数,则调用装饰器并用装饰器的返回值替换装饰函数。

假设您有以下代码:

@my_decorator
def my_function()
  print("My_function")

相当于这个调用:

def my_function()
    print("My_function")    

my_function = my_decorator(my_function)

如果装饰器是这样的

def my_decorator(func):
    print("decorated)
    return 42

那么

my_function
甚至不是一个函数,它会是一个整数(你可以尝试
print(my_function)

所以当你将装饰器定义为

def my_decorator2(some_fun):
    print("before")
    some_fun()
    print("after")

然后这个装饰器不返回任何内容(在Python中这意味着它返回

None
)。

@my_decorator2
def decorated():
  print("inside")

打印

before
inside
after

但是调用

decorated()
会引发异常
'NoneType' object is not callable
,因为
decorated
被替换为
None

您应该始终创建返回一些有用的东西的装饰器,例如函数或类(通常是内部的“包装”函数)。有时从装饰器返回函数/类之外的其他东西可能很有用,但它通常会混淆您的代码并将其转换为完全不可维护的东西。


-1
投票

它已经解释了为什么要使用包装函数,出于好奇我只是给出 示例如果我们不需要包装函数我们可以做什么。

类型1

返回一个小函数,返回
None
pass

def decorator_func(to_be_decorated_function):
    print("Logging IN: Currrently  in function")
    to_be_decorated_function()
    print("Logging OUT: Currrently  in function")

    def a(): None  # or def a(): pass

    return (a)

@decorator_func
def to_be_decorated_function():
    print('October 16, 2000')

to_be_decorated_function()
# equivalent to 
#to_be_decorated_function = decorator_func(to_be_decorated_function)

类型2

这只是删除了装饰器的使用,并且只是稍微调整一下。如果我们不返回,或者根本不使用可调用对象怎么办。

def decorator_func(to_be_decorated_function):
    print("Logging IN: Currrently  in function")
    to_be_decorated_function()
    print("Logging OUT: Currrently  in function")

@decorator_func
def to_be_decorated_function():
    print('October 16, 2000')

to_be_decorated_function  # notice I'm just using the object and not callable function
# equivalent to
#decorator_func(to_be_decorated_function)

两种类型的输出

Logging IN: Currrently  in function
October 16, 2000
Logging OUT: Currrently  in function
© www.soinside.com 2019 - 2024. All rights reserved.