函数 之装饰器

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

相关推荐

  • 女生学java开发难吗?女生适合学java吗?

    女生学java开发难吗?女生适合学java吗?女生学java开发?Java开发看上去是一项系统性很强、入门很难的“高大上”学科,前端、代码这些普通人基本不会接触到的名词,吓怕了众多初学者。大部分人对于Java程序员都有一个既定印象,那就是程序员都是男生。女程序员可以说是“稀有物种”,因为Java工作对于逻辑的要求很高,而这方面男生相对于女生比较有优势。现在女生从事程序员工作的也越来越多,在某些方面相对于男生也有优势。所以,小编就来给大家分析分析,女生学java开发难吗?女生适合学java吗?女生适合从事java吗?在很多人的潜意识里,认为女

  • 集群和负载均衡_分布式负载均衡

    集群和负载均衡_分布式负载均衡基于集群的动态反馈负载均衡策略基于动态反馈机制的集群负载均衡算法研究目前应用最为广泛的集群计算技术主要分为三大类:高可用性集群技术、高性能计算集群技术和负载均衡集群技术。德国的CarlAdamPetri于1962年在他的博士论文《自动机通信》中提出了Petri网的概念,它是一种适合于描述异步、并发、分布式系统的图形数学工具。动态WRR调度算法…

    2022年10月13日
  • C语言数组初始化

    C语言数组初始化转载博客代码编译运行环境:VS2017+Win32+Debug1.字符数组的初始化方式C语言中表示字符串有两种方式,数组和指针,字符数组是我们经常使用的方式。变量的定义包括指明变量所属类型、变量名称、分配空间以及初始化。可以看出,变量的初始化是变量定义的一部分。除了const变量需要显示初始化以外,其它变量如果在定义时未显示初始化,编译器会为变量以默认…

  • v-if与v-show的区别

    v-if与v-show的区别相同点:v-if与v-show都可以动态控制dom元素显示隐藏不同点:v-if显示隐藏是将dom元素整个添加或删除,而v-show隐藏则是为该元素添加css–display:none,dom元素还在。都修改为false后,第一个div是直接被移除掉了需要注意的是,当一个元素默认在css中加了display:none属性,这时通过if-show修改为true是无法让元素显示的。…

  • FIONREAD 判断 socket有多少数据可读[通俗易懂]

    FIONREAD 判断 socket有多少数据可读[通俗易懂]Ioctl(sockfd,FIONREAD,&npend); /*checkFIONREADsupport*/检查sockfd表示的文件描述符中有多少数据可以读取

  • JS : clearInterval()失效,无法暂停setInterval()的原因[通俗易懂]

    JS : clearInterval()失效,无法暂停setInterval()的原因[通俗易懂]环境:我在函数A调用函数B,函数B里写了setInterval()。原因:页面加载时我调用了2次函数A,导致调用了2次setInterval(),导致有setInterval_id有2个值。clearInterval()只关闭了其中一个setInterval_id,另一个setInterval_id还会启动setInterval()。改为只调用一次函数A即可。…

    2022年10月27日

发表回复

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

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