前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python3–装饰器

python3–装饰器

作者头像
老七Linux
发布2018-05-09 17:20:42
1.1K0
发布2018-05-09 17:20:42
举报

python装饰器

python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象(函数的指针)。装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。

python装饰器有很多经典的应用场景,比如:插入日志、性能测试、事务处理、权限校验等。装饰器是解决这类问题的绝佳设计。并且从引入中的列子中我们也可以归纳出:装饰器最大的作用就是对于我们已经写好的程序,我们可以抽离出一些雷同的代码组建多个特定功能的装饰器,这样我们就可以针对不同的需求去使用特定的装饰器,这时因为源码去除了大量泛化的内容而使得源码具有更加清晰的逻辑。

说明:

  1. 方法用来检测对象是否可被调用,可被调用指的是对象能否使用()括号的方法调用。 def a(): pass callable(a)
  2. 可调用对象,在实际调用也可能调用失败;但是不可调用对象,调用肯定不成功。
  3. 类对象都是可被调用对象,类的实例对象是否可调用对象,取决于类是否定义了__call__方法
实例一:
代码语言:javascript
复制
import datetime

def hello(fun):
    def preHello():
        print("#####start#####")  
        fun()                         
        print("#####end#####")          
    return preHello

@hello
def myTime():
    now = datetime.datetime.now()          
    print(now)                              

myTime() 

输出:
#####start#####
2018-04-18 21:36:02.621744
#####end#####

分析:

代码语言:javascript
复制
1、装饰器是什么?
装饰器,顾名思义,就是用来“装饰”的。
@XXX

也就是如上的@hello

# 1. @hello = myTime = hello(myTime)
# 2. 调用hello()

# 装饰器通过@进行使用 ,相当于把myTime() 函数作为参数,传给hello()
# @hello  相当于    myTime = hello(myTime)
# 当你调用myTime()的时候,是不是就相当于调用了hello(myTime())()
如下为执行步骤:
代码语言:javascript
复制
import datetime

def hello(fun):     
    def preHello():     ##2
        print("#####start#####")    ##6
        fun()       ##7     ##10
        print("#####end#####")      ##11
    return preHello     ##3

@hello          ##1 ##4
def myTime():
    now = datetime.datetime.now()       ##8
    print(now)      ##9

myTime()        ##5     ##12
装饰器定义:

每一层 def 获得一个 (xxx)

代码语言:javascript
复制
def deco_with_arg(第一个括号内的参数):  # 一般是装饰器的参数
    def deco(第二个括号内的参数):  # 一般是 func 本身
        def wrapper(第三个括号内的参数):  # 一般是 func 调用时的参数
            # 调用 func
        return wrapper
    return deco
带参数装饰器:
代码语言:javascript
复制
def startEnd(fun):
    def wraper(name):
        print("!!!!!!!start!!!!!!!!")
        fun(name)
        print("!!!!!!!end!!!!!!!!")

    return wraper

# 返回值 wraper函数
# hello()  相当于执行  wraper()

@startEnd
def hello(name):
    print("hello {0}".format(name))

hello("aaaa")

# 1. a = startEnd(hello)
# 2. hello = a
# 3. 调用hello()

# 装饰器的作用:在不改变源代码的情况下,给现有的函数增加新的功能
# 装饰器通过@进行使用 ,相当于把hello() 函数作为参数,传给startEnd()
# @startEnd  相当于    hello = startEnd(hello)
# 当你调用hello()的时候,是不是就相当于调用了startEnd(hello())()
通俗的讲法:

日常生活中有这种情况,商场刚装修完,都是大玻璃门,怕有人撞上,就要在上面用油漆写“小心琉璃”……但是玻璃太多,都写上字太费劲,干脆就用油漆画个叉就达到目的了。用油漆简单装饰一下,完成省得有人撞上和指路的功能。

“装饰器”就是做个装修标记,并且有它的功能,比如“小心玻璃”和“指路”,看到这个标记你就知道它要表达什么意思了,按着它的指示来肯定没错。

在Python里装饰器的定义:在程序运行时,增加动态功能的方式,称之为“装饰器”,装饰器本质上也是一个Python函数。

那么问题来了,有可能初学者对这个定义里的函数不理解,不得不多讲一点。

函数,“数”好理解,“函”本意就是一种平级之间的信,比如两个单位传达或者反馈信息。

假设有这样一种情况,你要出去旅游购物,又怕被黑,在出发前,找到好朋友诸葛不亮出主意,他给了你一个密函,告诉你到地方再打开。等你到地方打开一看,上写5个大字“报价砍一半”。接下来买什么东西,都按这个原则来,这个就是最基本的函数了。

写成公式:购买价格=对方报价乘0.5,这个公式就是函数公式。用标准定义来说“设在某变化过程中有两个变量x、y,如果对于x在某一范围内的每一个确定的值,y都有唯一确定的值与它对应,那么就称y是x的函数,x叫做自变量。”来段简单的代码感受一下这个“讲价函数”。

为了方便初学者,本文所有函数名称都使用全拼。

代码语言:javascript
复制
def Jianjia(x):  
   y =0.5  
   x = x * y  
   return x  
print(Hanshu(100))  
print(Hanshu(50))  

输出结果,你猜是不是50和25?我猜你猜对了。

def是固定格式,Hanshu就是函数的名称,(x)就是参数。

接下来,就要旅游购物了,好在有个机器人替我购物,它只能听懂Python语言,我要告诉它购买什么和详细的购买程序。

我要买三样东西:猪、大象、长颈鹿,流程是询价,对应东西,再购买。

有三个购买函数(Goumai_1,Goumai_2,Goumai_3),写出来的流程是这样:

代码语言:javascript
复制
def Goumai_1():  
   print('询价')  
   print('猪')  
   print('购买成功')  

def Goumai_2():  
   print('询价')  
   print('大象')  
   print('购买成功')  

def Goumai_3():  
   print('询价')  
   print('长颈鹿')  
   print('购买成功')  

Goumai_1()  
Goumai_2()  
Goumai_3()  

执行结果是这样:

代码语言:javascript
复制
询价  
猪  
购买成功  
询价  
大象  
购买成功  
询价  
长颈鹿  
购买成功  

现在我发现,这样写太复杂了。因为“询价”和“购买成功”动作是一样的,而且我要想在“购买成功”上加一个感叹号,需要加三次才能成功,如果买1000个东西,就要加1000次感叹号了,想想就要累死了。

于是我优化了一下程序,把购买东西Goumai(Dongxi)定义成了一个函数,执行的结果和上一个程序还是一样的,而且“购买成功”还加了一个感叹号,只操作了一次,可以显示三次啊。

代码语言:javascript
复制
def Goumai(Dongxi):  
   print('询价')  
   Dongxi()  
   print('购买成功!')  

def Zhu():  
   print('猪')  

def Daxiang():  
   print('大象')  

def Changjinglu():  
   print('长颈鹿')  

Goumai(Zhu)  
Goumai(Daxiang)  
Goumai(Changjinglu) 

但是问题又来了,每次我还要告诉机器人“购买(猪)”,“购买(大象)”,“购买(长颈鹿)”,能不能干脆点“购买”两个字都不说?直接在“猪、大象、长颈鹿”上做个标记,机器人看到标记就执行购买动作呢?答案当然是可以了。

这时“购买”就变成了一个固定的动作,而不是三个步骤,为了让机器人理解,我在“购买”函数里直接定义了这个动作函数。

代码语言:javascript
复制
def Goumai(Dongxi):  
    def Dongzuo():  
       print('询价')  
       Dongxi()  
       print('购买成功!')  
    return Dongzuo

这个时候装饰器才正式出场,装饰器就是用@来表示,加上动作函数名称。

比如下文中的,漂亮吗?

代码语言:javascript
复制
@Goumai

可以理解成用@符号把“购买”这个两个字像用口香糖粘在了物品名称上一样,这回有点像“装饰”的意思了吧。

机器人看到@的标签,就会按@里的动作来执行。

代码如下:

代码语言:javascript
复制
def Goumai(Dongxi):  
   def Dongzuo():  
       print('询价')  
       Dongxi()  
       print('购买成功!')  
   return Dongzuo

@Goumai  
def Zhu():  
   print('猪')  

@Goumai  
def Daxiang():  
   print('大象')  

@Goumai  
def Changjinglu():  
   print('长颈鹿')  

Zhu()  
Daxiang()  
Changjinglu() 

再结合一下刚才的讲价函数,先询价,再讲价,再购买,就可以写成这样:

代码语言:javascript
复制
def Goumai(Dongxi):  
       def Dongzuo():  
       print('询价')  
       Dongxi()  
       print('购买成功!')  
       return Dongxi  
   return Dongzuo  

def JangJia(x):  
   y =0.5  
   x = int(x) * y  
   return x 

@Goumai  
def Zhu():  
   Baojia = input('请输入对方报价:')  
   Huanjia = JangJia(Baojia)  
   print('猪:' + str(Huanjia))

@Goumai  
def Daxiang():  
   Baojia = input('请输入对方报价:')  
   Huanjia = JangJia(Baojia)  
   print('大象:' + str(Huanjia)) 

@Goumai  
def Changjinglu():  
   Baojia = input('请输入对方报价:')  
   Huanjia = JangJia(Baojia)  
   print('长颈鹿:' + str(Huanjia))  

Zhu()  
Daxiang()  
Changjinglu()  

执行后的结果:

代码语言:javascript
复制
询价  
请输入对方报价:100  
猪:50.0  
购买成功!  
询价  
请输入对方报价:50  
大象:25.0  
购买成功!  
询价  
请输入对方报价:25  
长颈鹿:12.5  
购买成功!
嵌套装饰器:
代码语言:javascript
复制
def startEnd(author):
    def a(fun):
        def b(name):
            print("this author is {0}".format(author))
            print("!!!!!!!start!!!!!!")
            fun(name)
            print("!!!!!!!!end!!!!!!!")

        return b

    return a

@startEnd("zhdya")
def hello(name):
    print("hello {0}".format(name))

# hello = startEnd("zhdya")(hello)
# hello("AAAAA")
hello("AAAAA")
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017/04/19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • python装饰器
    • 实例一:
      • 如下为执行步骤:
    • 装饰器定义:
      • 带参数装饰器:
    • 通俗的讲法:
      • 嵌套装饰器:
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档