详解装饰器身世以及函数是否加括号拓展

装饰器的最初由来

首先,我们在讨论装饰器之前得知道 Python函数 加括号和不加括号的区别:

不带括号时,调用的是这个函数本身 ,即这个函数的集装箱(整个函数体),它是一个函数对象,不须等该函数执行完成

带括号时(有参数或无参),调用的是函数的执行结果,须等该函数执行完成的结果

了解这个接下来,什么是装饰器?

拓展原函数功能的函数,在不改变原函数名的情况下,给函数增加新的功能

话不多说,上例子~

def pets(puppy):  # 外部
    def bark():   # 内部
        print('汪汪汪~~~~~~')
        return puppy()
    return bark
#@pets  # 装饰器
def dog():  # 被装饰函数
    print('我饿了!')

【注】这里我们先把装饰器注释

首先直接调用dog函数,然后我们再调用pets函数试试(系统会提示需要设置实参)注:参数为外部函数名

dog()   # 输出结果:我饿了!
pets(dog)   # 输出结果:(空空如也)
# 把dog函数的整体搬到了puppy函数里(传值)# puppy = dog

问题:pets(dog)输出结果为什么什么都没有呢?

  • 首先我们看pets函数,在其有效只执行一条语句,即return bark ;

  • 把bark这个集装箱(函数名)返回给了pets函数

    即:pets(dog) == bark

    测试:
    bark()#NameError: name 'bark' is not defined证明局部外部执行结束后即死亡(在外部不能被调用)
    于是将bark替换成pets(dog)

    pets(dog)()
    #汪汪汪~~~~~~
    #我饿了!
    

    ~这其实就有一个名字——装饰器

于是,我们的程序员在改进当中,给装饰器创建了一个别样(又萌又好看)的形式——语法糖的形式
@pets     #等价于 pets(dog)()

对于装饰器的身世小伙伴们是否有了一定的认识~~~☺

对于函数加不加括号的拓展

还是上面的例子,搬来~

def pets(puppy):  # 外部
 def bark():   # 内部
     print('汪汪汪~~~~~~')
     return puppy()
 return bark     #不能返回函数值
#@pets  # 装饰器
def dog():  # 被装饰函数
 print('我饿了!')

这里注意:装饰器绝对不能返回函数值的,它要求给外函数返回的是内函数的集装箱

Why?

def pets(puppy):  # 外部
    def bark():   # 内部
        print('汪汪汪~~~~~~')
        return puppy()  # puppy() == dog()
    return bark()   #加上括号
#@pets  # 装饰器
def dog():  # 被装饰函数
    print('我饿了!')

假如我们带上括号,代表的是调用!!!
即调用了bark函数,然后把函数的值返回给了pets函数
而bark函数的值又是什么呢?
是返回的puppy函数!puppy函数其实就是dog函数传参来的!![即puppy() == dog()]
但是,dog函数并没有返回值,Python里有一句话:

Python里如果没有写return语句的话,它的返回值默认为None

于是,puppy()= dog()= None,然后返回给bark()再返回给pets()→pets()为None
此时的运行结果为TypeError: 'NoneType' object is not callable(类型错误:空值类型不能被调用)

pets(dog)()   #pets(dog)() == None()错
#None不能为函数

所以,装饰器是绝对不能返回函数值的!!!

但是,有一种情况是装饰器有函数值(不算错)但是后面调用时性质就会发生改变

pets(dog)   #pets(dog)()
#汪汪汪~~~~~~
#我饿了!

此时如果装饰器没有返回函数值,这又回到了最开始那个输出(空空如也)的地方!我们用print语句测试一下究竟是什么没有值:

print(pets(dog))
#<function pets.<locals>.bark at 0x000001948F77E700>  
#是一个存放dark函数内存地址

再一次解释了开头的问题板块

用抽象来说:

@pets + dog()== pets(dog)()
装饰器有装饰还不行,还需要调用

另附:函数的名字就相当于一个变量,这个变量存放的是一个集装箱(一个内存地址:存放代码)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值