python3–装饰器

python装饰器

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

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

说明:

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

实例一:

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#####

分析:

1、装饰器是什么?
装饰器,顾名思义,就是用来“装饰”的。
@XXX

也就是如上的@hello

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

# 装饰器通过@进行使用 ,相当于把myTime() 函数作为参数,传给hello()
# @hello  相当于    myTime = hello(myTime)
# 当你调用myTime()的时候,是不是就相当于调用了hello(myTime())()
如下为执行步骤:
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)

def deco_with_arg(第一个括号内的参数):  # 一般是装饰器的参数
    def deco(第二个括号内的参数):  # 一般是 func 本身
        def wrapper(第三个括号内的参数):  # 一般是 func 调用时的参数
            # 调用 func
        return wrapper
    return deco
带参数装饰器:
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叫做自变量。”来段简单的代码感受一下这个“讲价函数”。

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

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),写出来的流程是这样:

def Goumai_1():  
   print('询价')  
   print('猪')  
   print('购买成功')  

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

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

Goumai_1()  
Goumai_2()  
Goumai_3()  

执行结果是这样:

询价  
猪  
购买成功  
询价  
大象  
购买成功  
询价  
长颈鹿  
购买成功  

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

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

def Goumai(Dongxi):  
   print('询价')  
   Dongxi()  
   print('购买成功!')  

def Zhu():  
   print('猪')  

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

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

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

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

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

def Goumai(Dongxi):  
    def Dongzuo():  
       print('询价')  
       Dongxi()  
       print('购买成功!')  
    return Dongzuo

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

比如下文中的,漂亮吗?

@Goumai

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

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

代码如下:

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() 

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

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()  

执行后的结果:

询价  
请输入对方报价:100  
猪:50.0  
购买成功!  
询价  
请输入对方报价:50  
大象:25.0  
购买成功!  
询价  
请输入对方报价:25  
长颈鹿:12.5  
购买成功!

嵌套装饰器:

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")

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CreateAMind

CS231n课程笔记翻译:Python Numpy教程

译者注:本文智能单元首发,翻译自斯坦福CS231n课程笔记Python Numpy Tutorial,由课程教师Andrej Karpathy授权进行翻译。本篇...

13930
来自专栏章鱼的慢慢技术路

《算法图解》第一章笔记与课后练习

41640
来自专栏Crossin的编程教室

【每周一坑】田忌赛马

本周的题目取自著名的历史典故:田忌赛马 背景资料如下 田忌经常与齐国众公子赛马,设重金赌注。田忌的上宾孙膑发现他们的马脚力都差不多,马分为上、中、下三等,于是对...

324100
来自专栏小樱的经验随笔

鸽巢原理(抽屉原理)的详解

抽屉原理 百科名片 ? 桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面放两个苹果。这一现象就是我们所说的“抽屉原理”...

64270
来自专栏owent

POJ PKU Let's Go to the Movies 解题报告

题目链接:http://acm.pku.edu.cn/JudgeOnline/problem?id=3513

9320
来自专栏数据分析

[数据分析工具] Pandas 功能介绍(二)

条件过滤 我们需要看第一季度的数据是怎样的,就需要使用条件过滤 ? 体感的舒适适湿度是40-70,我们试着过滤出体感舒适湿度的数据 ? 最后整合上面两种条件,在...

38760
来自专栏数据结构与算法

42:出书最多

42:出书最多 查看 提交 统计 提问 总时间限制: 1000ms 内存限制: 65536kB描述 假定图书馆新进了m(10 ≤ m ≤ 999)本图书,它们...

29350
来自专栏黑泽君的专栏

面向对象的概述

============================================================================= 1:...

9220
来自专栏章鱼的慢慢技术路

《算法图解》第一章笔记与课后练习_二分查找算法

24640
来自专栏数据结构与算法

P2871 [USACO07DEC]手链Charm Bracelet

题目描述 Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of ...

350120

扫码关注云+社区

领取腾讯云代金券