函数 之装饰器

引言闭包函数在看装饰器之前,我们先来搞清楚什么是闭包函数。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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Linux下oracle创建表空间及用户「建议收藏」

    Linux下oracle创建表空间及用户「建议收藏」最近在测试flink的oracle-cdc,公司领导在没用的测试环境搭了一个oracle供我测试,一开始我是拒绝的,毕竟oracle除了crud,也不会别的,奈何拒绝不了,只能边学变做。1,登录sys用户sqlplus/assysdba2,查询用户表空间文件的路径,然后在此目录下创建新的表空间selectnamefromv$datafile;3,创建表空间,永久性表空间:一般保存表、视图、过程和索引等的数据CREATETABLESPACExxxxLOGGINGDATAFI

  • LeetCode OJ:Basic Calculator(基础计算器)

    LeetCode OJ:Basic Calculator(基础计算器)

  • 什么是泛型,怎么使用泛型分析

    什么是泛型,怎么使用泛型分析1、泛型1:什么是泛型?泛型就是广泛的类型,也是一种数据类型,而这种数据类型可以是任意类型,编写过程中当中不能确定类型,创建对象时必须指定具体类型,不指定也可以,但是会出现一些错误。泛型是编译器行为,只能在编译期有效,如果能够跨过编译器,那么泛型就没有任何意义了。2、泛型类怎么定义泛型类?在类后面加<&g…

  • PotPlayer快捷键查询[通俗易懂]

    PotPlayer快捷键查询[通俗易懂]快捷键指令————————–常用—————————C播放->播放速度->加速+Z播放->播放速度->正常/之前的速度X播放->播放速度->减速-`…

  • 项目活动图 – 举例说明

    项目活动图 – 举例说明关键路径:从开始到结束的所有路径中,时间最长的一条为关键路径。(特点:在关键路径上,所有任务的松弛时间都为0);松弛时间:第一种方法:最晚开始时间-最早开始时间;第二种方法:关键路径的总时间-包含该任务的任务路径花的时间;(前提条件:必须不影响该任务以后的任务路径)举例:关键路径:A-B-D-G-I-K-L=22由前向后推可得出…

  • typescript 接口_接口是什么

    typescript 接口_接口是什么介绍TypeScript的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。接口是对象的状态(属性)和行为(方法)的抽象(描述)接口初探声明接口

发表回复

您的电子邮箱地址不会被公开。

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