【问题标题】:Why do we need wrapper function in decorators?为什么我们需要装饰器中的包装函数?
【发布时间】:2018-01-02 06:51:06
【问题描述】:

如果我创建如下装饰器:

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

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

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

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

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

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

【问题讨论】:

  • 你试过了吗?这两个代码块对我来说产生了完全不同的结果。
  • 真的吗?您是否尝试在第二个代码块中调用just_some_fun()?是的,当my_decoratorjust_some_fun 调用时,东西会被打印出来,但是当你调用 just_some_fun() 它会随着TypeError: 'NoneType' object is not callable 而崩溃。
  • “两个装饰器的工作方式相同”——你对这个断言有什么支持吗?
  • 打印是一种副作用,与返回对象不同。在不使用print() 的情况下尝试类似的操作,例如修改字符串。你将不得不返回一些东西。

标签: python python-3.x python-decorators


【解决方案1】:

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

您的第二版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)

【讨论】:

    【解决方案2】:

    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

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

    【讨论】:

    • 感谢您清楚地解释(通过示例)为什么必须返回装饰函数(否则根本无法调用)!
    【解决方案3】:

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

    类型 1

    返回一个返回Nonepass的小函数

    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

    这只是去掉了装饰器的使用,只是稍微调整了一下。如果我们不 return ,以及根本不使用可调用对象。

    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
    

    【讨论】:

      猜你喜欢
      • 2018-04-17
      • 2010-09-21
      • 2018-11-20
      • 2019-01-18
      • 2011-01-02
      • 2012-03-22
      • 2018-02-23
      • 2011-03-29
      相关资源
      最近更新 更多