函数 之装饰器

引言闭包函数在看装饰器之前,我们先来搞清楚什么是闭包函数。python是一种面向对象的编程语言,在python中一切皆对象,这样就使得变量所拥有的属性,函数也同样拥有。这样我们就可以理解在函数内创

大家好,又见面了,我是你们的朋友全栈君。

引言

  以前你有没有这样一段经历:很久之前你写过一个函数,现在你突然有了个想法就是你想看看,以前那个函数在你数据集上的运行时间是多少,这时候你可以修改之前代码为它加上计时的功能,
但是这样的话是不是还要大体读读你之前的这个的代码,稍微搞清楚一点它的逻辑,才敢给它添加新的东西。这样是不是很繁琐,要是你之前写的代码足够乱足够长,再去读它是不是很抓狂...。
实际工作中,我们常常会遇到这样的场景,可能你的需求还不只是这么简单。那么有没有一种可以不对源码做任何修改,并且可以很好的实现你所有需求的手段呢?答案当然是有,这就是今天我
们要介绍的python装饰器。有了装饰器,你除了不用担心前面提到的问题,并且还可以很好的处理接下来要做的事:那就是现在你又有了一个新的需求,比如为另一个函数添加计时功能,这时就
非常简单了,把要装饰的函数丢给装饰器就好了,它会自动给你添加完功能并返回给你。是不是很神奇?下面我们将一层层剥开它的神秘面纱。

 闭包函数

  在看装饰器之前,我们先来搞清楚什么是闭包函数。python是一种面向对象的编程语言,在python中一切皆对象,这样就使得变量所拥有的属性,函数也同样拥有。这样我们就可以理解在函数内创建一个函数的行为是完全合法的。这种函数被叫做内嵌函数,这种函数只可以在外部函数的作用域内被正常调用,在外部函数的作用域之外调用会报错,例如:

       <span role="heading" aria-level="2">函数 之装饰器

而如果内部函数里引用了外部函数里定义的对象(甚至是外层之外,但不是全局变量),那么此时内部函数就被称为闭包函数。闭包函数所引用的外部定义的变量被叫做自由变量。闭包从语法上看非常简单,但是却有强大的作用。闭包可以将其自己的代码和作用域以及外部函数的作用结合在一起。下面给出一个简单的闭包的例子:

def count():
    a = 1
    b = 1
    def sum():
        c = 1
        return a + c  # a - 自由变量
    return sum

 总结:什么函数可以被称为闭包函数呢?主要是满足两点:函数内部定义的函数;引用了外部变量但非全局变量。

装饰器定义及原则

有了闭包函数的概念,我们再去理解装饰器会相对容易一些。python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象(函数的指针)。装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。从上面这段描述中我们需要记住装饰器的几点属性,以便后面能更好的理解:

       实质: 是一个函数


    参数:是你要装饰的函数名(并非函数调用)


    返回:是装饰完的函数名(也非函数调用)


    作用:为已经存在的对象添加额外的功能


    特点:不需要对对象做任何的代码上的变动        

定义   

装饰器本质还是一个函数,它是用来对函数函数进行装饰的函数,在不改变原来函数调用的基础上给原来的函数添加新的功能

原则:开放封闭原则

开放:对于原来的函数没有的功能(要添加的新的功能是开放的)
为什么要对扩展开放呢?
    任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。


封闭:对于原来的函数已经实现的功能是封闭的(不能改变原来的函数)

为什么要对修改封闭呢?
    就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对已经写好的函数进行了修改,很有可能影响其他已经在使用该函数的用户。

装饰器完美的遵循了这个开放封闭原则。

装饰器的主要功能及固定框架

装饰器的主要功能

在不改变原函数调用方式的基础上在原函数的前、后添加新的功能

装饰器的固定框架

<span role="heading" aria-level="2">函数 之装饰器
<span role="heading" aria-level="2">函数 之装饰器

def timer(func):
    def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return inner

装饰器固定形态

语法糖

介绍

<span role="heading" aria-level="2">函数 之装饰器
<span role="heading" aria-level="2">函数 之装饰器

语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。

举个例子:在C语言里用a[i]表示*(a+i),用a[i][j]表示*(*(a+i)+j),看来语法糖不是“现代语言”才有啊,连我们的老大哥C也有,而且这种写法简洁明了,也更好懂了。

实际上从面向过程到面向对象也是一种语法糖啊,C语言可以通过它的指针、类型转换,结构实现面向对象的编程风格,但是C++更进一步的推广了这种风格,更好用了,不过到了C#把OO的风格发挥得淋漓尽致。OO的编程风格对于面向过程来说是不是一种语法糖呢?如果生硬地照此理解,只有计算机硬件指令才不算语法糖,而其他一切利用编译器、汇编器将代码抽象,和自然语言更相近的手段都算语法糖。

现在很多很多编程思想,编程理论层出不穷,当然,对于学习来说我们是要抓住技术的核心,但对于工程来说如何提高工程质量,如何提高工程效率也是我们要关注的,既然这些语法糖能辅助我们以更好的方式编写代码为什么要“抵制“呢?

我想语法糖和其他编程思想一样重要,什么duck type,人本接口,最小接口,约定优于配置,其实不都是一些思想上的“语法糖“?

不过也并不是没有反对的声音,这其中就有图灵奖的第一个获得者: Alan Perlis。.net从2.0到3.0到3.5底层CLR并没有做更新,没有添加任何新的IL指令,所以C#从2.0到3.0中所有的特性都是语法糖,就算从1.0到2.0,也只有一个泛型不是语法糖,其余的新特性也都是如此,但是每一次C#发布新特性的时候我们不得不为之而鼓舞,为之而喝彩。新的语法可以酿造更好的编程风格,以前一些难以实现的方面现在也轻而易举了。

需要声明的是“语法糖”这个词绝非贬义词,它可以给我带来方便,是一种便捷的写法,编译器会帮我们做转换;而且可以提高开发编码的效率,在性能上也不会带来损失。

View Code

语法糖的作用

语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。

语法糖示例:

 1 import time 2 def wrapper(func): 3         def inner(): 4                start=time.time() 5                func() 6                end=time.time() 7                print(end-start) 8         return inner 9 @wrapper10 def  kkk():#相当于kkk=wrapper(kkk)11     print('aaaaa')12 kkk()            

带参数的装饰器

假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?

一个一个的取消掉? 没日没夜忙活3天。。。

过两天你领导想通了,再让你加上又得忙乎几天超级麻烦啦  可不可以一次性搞定呢?答案是肯定的怎么做呢 ,仔细看下面的代码哦

<span role="heading" aria-level="2">函数 之装饰器
<span role="heading" aria-level="2">函数 之装饰器

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''执行函数之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''执行函数之后要做的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()

View Code

多个装饰器装饰一个函数

有些时候,我们也会用到多个装饰器装饰同一个函数的情况

<span role="heading" aria-level="2">函数 之装饰器
<span role="heading" aria-level="2">函数 之装饰器

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()

View Code

装饰器的具体应用

1.定义无参装饰器函数为被装饰器添加统计运行时间的功能

<span role="heading" aria-level="2">函数 之装饰器
<span role="heading" aria-level="2">函数 之装饰器

#定义闭包无参函数,为程序增加统计时间功能
import time
def timer(func):         #定义timer函数,func变量值为login
    def wrapper():      
        start_time=time.time()   #设置函数起始时间
        func()                   #函数名加()调用函数,即调用login(),然后调用结束后继续在wrapper函数中运行
        stop_time=time.time()
        print("run time is %s"%(stop_time-start_time))
    return wrapper   #返回wrapper函数名,然后再次进入wrapper函数
 
@timer        #相当于timer(login)-->赋值给timer函数名
def login():
    #读取注册用户的信息,用户名,密码,输错次数,写入字典中
    user={}
    with open("DB1",encoding="utf8") as f:
        for line in f:
            username_list=line.strip().split("|")      #username_list--->['egon', '123', '2']
            user[username_list[0]]={"name":username_list[0],
                     "pwd":username_list[1],
                     "times":username_list[2]}
    # print(user)  #-->{'egon': {'name': 'egon', 'pwd': '123', 'times': '2'}, 'xuyaping': {'name': 'xuyaping', 'pwd': '123', 'times': '0'}, 'xyy': {'name': 'xyy', 'pwd': '123', 'times': '1'}}
  
    #读取黑名单用户,将黑名单用户加入列表中
    with open("black_lockname",encoding="utf8") as f1:
        black_list=[]
        for line in f1:
            black_list.append(line.strip())
    # print(black_list)
  
  
    while True:
        username = input("please input your username:").strip()
        passwd = input("please input your passwd:").strip()
        #用户在黑名单中
        if username in black_list:
            print("该用户为黑名单用户,请滚")
            break
  
        # 用户为注册用户
        elif username in user:
            user[username]["times"]=int(user[username]["times"])
            if user[username]["times"]<3 and passwd==user[username]["pwd"]:
                print("登录成功")
                user[username]["times"]=0
                #将修改后的信息重新写入DB1中
                with open("DB1","w",encoding="utf8") as f3:
                    for i in user:
                        f3.write(i + "|" + user[i]["pwd"] + "|" + str(user[i]["times"]) + "\n")
                break
  
            else:
                user[username]["times"]+=1
                print("登录错误")
                # 将修改后的信息重新写入DB1中
                with open("DB1", "w", encoding="utf8") as f3:
                    for i in user:
                        f3.write(i + "|" + user[i]["pwd"] + "|" + str(user[i]["times"]) + "\n")
                if user[username]["times"]==3:
                    black_list.append(username)
                    print("账户被锁定")
                    # 将修改后的信息重新写入black_lockname中
                    with open("black_lockname","w",encoding="utf8") as f4:
                        for j in black_list:
                            f4.write(j+ "\n")
                    break
  
        #用户不是注册用户
        else:
            print("该用户没有注册")
            break
 
login()

View Code

2.定义有参装饰器为被装饰器添加认证功能用户的信息可以源于file或者l三次验证失败锁定用户

<span role="heading" aria-level="2">函数 之装饰器
<span role="heading" aria-level="2">函数 之装饰器

#定义闭包有参函数,为程序增加验证功能
def auth2(auth_type):      
    def auth(func):   #func参数此时被赋值为login
        def wragger(*args,**kwargs):   #wragger函数携带变量auth_type的值
            if auth_type=="file":
                func()     #运行函数login
            elif auth_type=="ldap":
                print("你他妈还想不想玩了?")
 
        return wragger
    return auth
 
@auth2(auth_type="file")   #相当于运行函数auth2(file),返回auth,auth(login)赋值给login函数名
def login():
    #读取注册用户的信息,用户名,密码,输错次数,写入字典中
    user={}
    with open("DB1",encoding="utf8") as f:
        for line in f:
            username_list=line.strip().split("|")      #username_list--->['egon', '123', '2']
            user[username_list[0]]={"name":username_list[0],
                     "pwd":username_list[1],
                     "times":username_list[2]}
    # print(user)  #-->{'egon': {'name': 'egon', 'pwd': '123', 'times': '2'}, 'xuyaping': {'name': 'xuyaping', 'pwd': '123', 'times': '0'}, 'xyy': {'name': 'xyy', 'pwd': '123', 'times': '1'}}
  
    #读取黑名单用户,将黑名单用户加入列表中
    with open("black_lockname",encoding="utf8") as f1:
        black_list=[]
        for line in f1:
            black_list.append(line.strip())
    # print(black_list)
  
  
    while True:
        username = input("please input your username:").strip()
        passwd = input("please input your passwd:").strip()
        #用户在黑名单中
        if username in black_list:
            print("该用户为黑名单用户,请滚")
            break
  
        # 用户为注册用户
        elif username in user:
            user[username]["times"]=int(user[username]["times"])
            if user[username]["times"]<3 and passwd==user[username]["pwd"]:
                print("登录成功")
                user[username]["times"]=0
                #将修改后的信息重新写入DB1中
                with open("DB1","w",encoding="utf8") as f3:
                    for i in user:
                        f3.write(i + "|" + user[i]["pwd"] + "|" + str(user[i]["times"]) + "\n")
                break
  
            else:
                user[username]["times"]+=1
                print("登录错误")
                # 将修改后的信息重新写入DB1中
                with open("DB1", "w", encoding="utf8") as f3:
                    for i in user:
                        f3.write(i + "|" + user[i]["pwd"] + "|" + str(user[i]["times"]) + "\n")
                if user[username]["times"]==3:
                    black_list.append(username)
                    print("账户被锁定")
                    # 将修改后的信息重新写入black_lockname中
                    with open("black_lockname","w",encoding="utf8") as f4:
                        for j in black_list:
                            f4.write(j+ "\n")
                    break
  
        #用户不是注册用户
        else:
            print("该用户没有注册")
            break
 
login()

View Code

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/124537.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 朋友圈集赞图片生成器_朋友圈集赞神器

    朋友圈集赞图片生成器_朋友圈集赞神器大家好这是一款朋友圈积攒截图小程序里面内涵三款样式生成,一款图文,一款分享,一款查看的样式也就是我们威信朋友圈所用到的样式就包含了那些可以用户自由的添加哈!赞的数量那些可以用户自定义的哈另外所需的内容也是用户自定义的安装方法的话和往常一样!直接威信开发者工具打开源码然后设置一个合法域名上传审核就可以了合法域名在压缩包里面,搭建解压了就可以看到了下面让我们来看看小编的测试演示图:小程序源码下载地址:(已更新)朋友圈集赞万能截图生成器威信小程序源码下载-小程序文.

    2022年9月5日
    2
  • apache 负载均衡策略_负载均衡slb

    apache 负载均衡策略_负载均衡slb参考文章:http://www.2cto.com/os/201109/102368.html在观看本文章之前,请先观看apache负载均衡之配置:http://blog.csdn.net/a787031584/article/details/64907389apache负载均衡策略有三种:第一种:轮询策略:即根据http请求数(requests)来均衡的分配给所代理的服

    2022年9月8日
    0
  • 基于51单片机的毕业设计作品_毕业设计能拿公司项目吗

    基于51单片机的毕业设计作品_毕业设计能拿公司项目吗以下是学长亲手整理的C51单片机相关的毕业设计选题,都是经过学长精心审核的题目,适合作为毕设,难度不高,工作量达标,对毕设有任何疑问都可以问学长哦!相对容易工作量达标题目新颖,含创新点httpshttpshttpshttpshttpshttps。……

    2022年10月3日
    0
  • 网站部署ssl证书_阿里云ssl证书部署教程

    网站部署ssl证书_阿里云ssl证书部署教程阿里云续费SSL证书下载证书文件在正式服务器上部署IIS部署阿里云部署步骤:步骤一:下载文件1、登录SSL证书控制台。2、在左侧导航栏,单击SSL证书。3、定位到已签发的SSL证书,单击操作列下的下载。4、在证书下载面板,单击IIS服务器类型后的下载5、解压缩已下载的SSL证书(IIS)压缩包。此时,证书已保存在本地计算机中,导入到服务器端即可步骤二:导入到服务器中1、在服务器按Win+R键,打开运行。2、输入mmc,单击确定3、为本地计算机添加证书管理单元。3.1、在

    2022年8月30日
    0
  • Linux 查看Redis 的版本

    Linux 查看Redis 的版本1.redis-server–version和redis-server-v查询结果2.redis-cli–version和redis-cli-v查询结果注:因为redis的server与cli同时安装,所以二者查出的结果基本一致。…

    2022年5月27日
    43
  • 伴随矩阵的秩和原矩阵的秩的关系[通俗易懂]

    伴随矩阵的秩和原矩阵的秩的关系[通俗易懂]

    2022年5月20日
    300

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号