装饰器报错 ‘NoneType‘ object is not callable

本文通过实战案例,详细解析Python装饰器的工作原理,包括装饰器的执行顺序、如何正确使用装饰器以及装饰器对函数的影响。同时,文章分享了作者在学习过程中遇到的常见错误及解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import time
def decorator(func):
    def wrapper():
        print(time.time())
        func()
    return wrapper()

@decorator
def f1():
    print("This is a function")

f1()

今天学习python的装饰器功能

但是我在执行上面的代码是就会报错,教程中没有任何问题。

然后就去网上查资料,发下了下面这张帖子,说是在调用被装饰的方法时不需要加括号,然后我就很不理解,不加括号不应该是调用对象吗,加了括号才是执行方法啊。(看帖子中的评论,应该和版本有关,我的是3.6)

参考链接:https://blog.csdn.net/wth_97/article/details/82347803

然后又去查原因,发现了这篇帖子,我怀疑是不是被装饰后就不是一个方法了,没办法调用了

参考链接:https://www.cnblogs.com/wangeraaa/p/10236180.html

好吧,既然这样就输出一下他的类型

​
import time
def decorator(func):
    def wrapper():
        print(time.time())
        func()
    return wrapper()
@decorator
def f1():
    print("This is a function")
print(type(f1))

​

代码改动不大,输出结果如下

1588937731.6673582
This is a function
<class 'NoneType'>

输出的类型为 NoneType,而且f1方法也执行了

继续测试,我把print(type(f1))方法删除了,执行一遍

1588937875.9700713
This is a function

没有调用,依然执行了。纳尼???

得出一个结论,装饰器方法会自动执行,

三秒后我想到了另一个结论,被装饰的f1无法被调用,因为只输出了一遍结果,是自动执行的那次调用,我下面的那次调用无效

好吧继续折腾~~~

我看到很多资料都是将f1方法传参,我也传参

def decorator(func):
    def wrapper(name):
        print(time.time())
        func(name)
    return wrapper()

@decorator
def f1(name):
    print(name)
    print("This is a function")

输出报错了

wrapper() missing 1 required positional argument: 'name'

少参数。。。。

好吧再折腾

对比网上的代码,教程视频的代码,我发现原因了,我写错了。。。。return wrapper不应该加个括号,而是直接返回wrapper。

该代码,结果,把括号去掉

import time


def decorator(func):
    def wrapper(name):
        print(time.time())
        func(name)
    return wrapper

@decorator
def f1(name):
    print(name)
    print("This is a function")

f1('李博')

f1('李博')

输出如下

1588939093.2899694
李博
This is a function
1588939093.2899694
李博
This is a function

Process finished with exit code 0

哎,发现自己真的蠢,前面口口声声的说,带括号是执行函数不带括号是调用对象,这里肯定是要return wrapper对象的,而不是要这个函数的返回结果啊。之前说到的自动执行时这里return时执行的。哎

好吧,是我写错代码了,这次总结到这里了。。。。

 

等等,还有一件事~~~

为什么是返回一个函数对象,装饰器本质就是执行装饰器的代码块,然后调用参数传进来的函数,这里又返回了一个函数对象,啊,狸猫换太子

编写代码 我输出了f1对象,print(f1)

<function decorator.<locals>.wrapper at 0x000001F2351F1A60>

这里可以看导  decorator.  和 wrapper 我们并没有看到f1

好吧,还是有一丝怀疑,我们输出的明确一点  print(f1.__name__)

wrapper

明白了,装饰器就是这个原理啊,一个语法糖而已

也算是因祸得福了,教程中和其他的文章中都没看到这个知识点。自己也算是摸索出来了。

python学了很久了,有时候学着学着就想起来javascript语言,越来越庆幸自己学过javascript了,有了javascript学起python方便了不少,比如说闭包,和javascript很相似。

 

 

扩展一下,来个多装饰器的版本

import time
def decorator(func):
    def wrapper():
        print('装饰器1')
        print(func.__name__)
        func()
    return wrapper
def decorator2(func):
    def wrapper2():
        print('装饰器2')
        print(func.__name__)
        func()
    return wrapper2
@decorator2
@decorator
def f1():
    print("This is a function")
f1()

猜猜输出结果是什么,我当时猜错了。。。我把执行顺序猜错了

装饰器2
wrapper
装饰器1
f1
This is a function

装饰器运行顺序是自下向上,可以看输出类型,装饰器2输出的对象名称是wapper,可以知道在此之前就被装饰器1掉包过了

执行顺序是自上向下,这个简单了,看输出顺序就好了

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值