装饰器的最初由来
首先,我们在讨论装饰器之前得知道 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)()
装饰器有装饰还不行,还需要调用
另附:函数的名字就相当于一个变量,这个变量存放的是一个集装箱(一个内存地址:存放代码)