python 装饰器

一、 装饰器定义

装饰器本质就是函数,功能是为其他函数添加附加功能

原则:

1)不修改被修饰函数的源代码

2)不修改被修饰函数的调用方式

二、装饰器知识储备

装饰器 = 高阶函数 + 函数嵌套 + 闭包

三、高阶函数

1、高阶函数定义:

     1)函数接受的参数是一个函数名

      2)函数的返回值是一个函数名

      3)满足上述条件任意一个都可以称之为高阶函数

2、高阶函数的示范

     1)把函数当做参数传给高阶函数

import time
#在源代码的基础上不修改源代码,但是修改了函数的调用方式
def foo():
    time.sleep(2)
    print('你好啊中国')

def test(func):
    print(func)
    start_time=time.time()
    func()
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))

#foo()
test(foo)

#修改了函数的调用方式

    2)函数返回值是函数名

import time
# 不能添加新功能,但是没有改变函数的调用方式
def foo():
    time.sleep(2)
    print('你好啊中国')

def test(func):
   return func

foo= test(foo) #foo的内存地址
foo() #执行foo函数

从1)2)合并得出:

1)不修改被修饰函数的源代码

2)不修改被修饰函数的调用方式

所以发现单单是一个高阶函数是不能实现装饰器的

import time

def foo():
    time.sleep(3)
    print('你好啊中国')

# 不能修改foo源代码,不修改foo函数的调用方式
def timer(func):
    start_time=time.time()
     func()
     stop_time = time.time()
     print('函数运行时间是  %s' % (stop_time-start_time))
     return func


foo=timer(foo)
foo()

#结果
你好啊中国
函数运行时间是 3.00001216
你好啊中国

#上面代码发现多运行一次foo,发现这是不行的
#所以发现单单是一个高阶函数是不能实现装饰器的


3、高阶函数的总结

    1)函数接受的参数是一个函数名

           作用:在不修改函数源代码的前提下,为函数添加新功能

           不足:会改变函数的调用方式

      2)函数的返回值是一个函数名

           作用:不修改函数的调用方式

           不足:不能添加新功能

四、函数嵌套

   函数嵌套就是在函数里面在定义函数

def father(name):
    print('from father %s' %name)
    def son():
        print('我的爸爸是%s' %name)
        def grandson():
            print('我的爷爷是%s' %name)
        grandson()
    print(locals())
    son()

father('李刚')

#结果

from father 李刚
{'name': '李刚', 'son': <function father.<locals>.son at 0x01329AE0>}
我的爸爸是李易峰
我的爷爷是李易峰

五、闭包

在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

#闭包函数的实例
# outer是外部函数 a和b都是外函数的临时变量
def outer( a ):
     b = 10
     # inner是内函数
     def inner():
        #在内函数中 用到了外函数的临时变量
        print(a+b)
     # 外函数的返回值是内函数的引用
     return inner
 
if __name__ == '__main__':
     # 在这里我们调用外函数传入参数5
    #此时外函数两个临时变量 a是5 b是10 ,并创建了内函数,然后把内函数的引用返回存给了demo
    # 外函数结束的时候发现内部函数将会用到自己的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数
    demo = outer(5)
    # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量
    # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数
     demo() # 15

     demo2 = outer(7)
     demo2()#17

结合这段简单的代码和定义来说明闭包:
1) 外函数返回了内函数的引用:

对于闭包,在外函数outer中 最后return inner,我们在调用外函数 demo = outer() 的时候,outer返回了inner,inner是一个函数的引用,这个引用被存入了demo中。所以接下来我们再进行demo() 的时候,相当于运行了inner函数。

同时我们发现,一个函数,如果函数名后紧跟一对括号,相当于现在我就要调用这个函数,如果不跟括号,相当于只是一个函数的名字,里面存了函数所在位置的引用。

2) 外函数把临时变量绑定给内函数:

按照我们正常的认知,一个函数结束的时候,会把自己的临时变量都释放还给内存,之后变量都不存在了。一般情况下,确实是这样的。但是闭包是一个特别的情况。外部函数发现,自己的临时变量会在将来的内部函数中用到,自己在结束的时候,返回内函数的同时,会把外函数的临时变量送给内函数绑定在一起。所以外函数已经结束了,调用内函数的时候仍然能够使用外函数的临时变量。

在我编写的实例中,我两次调用外部函数outer,分别传入的值是5和7。内部函数只定义了一次,我们发现调用的时候,内部函数是能识别外函数的临时变量是不一样的。python中一切都是对象,虽然函数我们只定义了一次,但是外函数在运行的时候,实际上是按照里面代码执行的,外函数里创建了一个函数,我们每次调用外函数,它都创建一个内函数,虽然代码一样,但是却创建了不同的对象,并且把每次传入的临时变量数值绑定给内函数,再把内函数引用返回。虽然内函数代码是一样的,但其实,我们每次调用外函数,都返回不同的实例对象的引用,他们的功能是一样的,但是它们实际上不是同一个函数对象。

3)闭包中内函数修改外函数局部变量:

在闭包内函数中,我们可以随意使用外函数绑定来的临时变量,但是如果我们想修改外函数临时变量数值的时候发现出问题了!

在基本的python语法当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法:

     1、 global 声明全局变量

     2 、全局变量是可变类型数据的时候可以修改 

在闭包内函数也是类似的情况。在内函数中想修改闭包变量(外函数绑定给内函数的局部变量)的时候:

 1 、在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。

 2 、在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。

 

#修改闭包变量的实例
# outer是外部函数 a和b都是外函数的临时变量
def outer( a ):
    b = 10  # a和b都是闭包变量
    c = [a] #这里对应修改闭包变量的方法2
    # inner是内函数
    def inner():
        #内函数中想修改闭包变量
        # 方法1 nonlocal关键字声明
        nonlocal  b
        b+=1
        # 方法二,把闭包变量修改成可变数据类型 比如列表
        c[0] += 1
        print(c[0])
        print(b)
    # 外函数的返回值是内函数的引用
    return inner

if __name__ == '__main__':

    demo = outer(5)
    demo() # 6  11

#结果 6 11

从上面代码中我们能看出来,在内函数中,分别对闭包变量进行了修改,打印出来的结果也确实是修改之后的结果。以上两种方法就是内函数修改闭包变量的方法。

还有一点需要注意:使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量

#coding:utf8
def outer(x):
    def inner(y):
        nonlocal x
        x+=y
        return x
    return inner


a = outer(10)
print(a(1)) //11
print(a(3)) //14

#结果 11  14

闭包作用:

1)装饰器!!!装饰器是做什么的??其中一个应用就是,我们工作中写了一个登录功能,我们想统计这个功能执行花了多长时间,我们可以用装饰器装饰这个登录模块,装饰器帮我们完成登录函数执行之前和之后取时间。

2)面向对象!!!经历了上面的分析,我们发现外函数的临时变量送给了内函数。大家回想一下类对象的情况,对象有好多类似的属性和方法,所以我们创建类,用类创建出来的对象都具有相同的属性方法。闭包也是实现面向对象的方法之一。在python当中虽然我们不这样用,在其他编程语言入比如javaScript中,经常用闭包来实现面向对象编程

3)实现单利模式!! 其实这也是装饰器的应用。单利模式毕竟比较高大,,需要有一定项目经验才能理解单利模式到底是干啥用的,我们就不探讨了。

六、装饰器

 1)基本的装饰器

import time

#装饰器架子
"""
def timer(func):
    def wrapper():
        print("func 内存地址",func)
        func()   # 有了内存地址就可以直接执行func函数
    return wrapper
"""

# 增加执行时间的功能
def timer(func):
    def wrapper():
        print("func 内存地址",func)
        start_time = time.time()
        func()   # 有了内存地址就可以直接执行func函数
        stop_time = time.time()
        print("执行完的时间%s" % (stop_time-start_time))

    return wrapper


"""
# test()函数
def test():
    time.sleep(3)
    print("test函数运行完毕")

#test = timer(test)  # 返回wrapper的内存地址
#test() # z执行的是rapper函数

"""

# @timer 就相当于 test = timer(test)
@timer
def test():
    time.sleep(3)
    print("test函数运行完毕")


test()

#结果 
func 内存地址 <function test at 0x023C9B70>
test函数运行完毕
执行完的时间3.000962257385254

2)返回值的装饰器

import time

def timer(func):
    def wrapper():
        start_time = time.time()
        res = func()   # 有了内存地址就可以直接执行func函数
        stop_time = time.time()
        print("执行完的时间%s" % (stop_time-start_time))
        return res

    return wrapper


# @timer 就相当于 test = timer(test)
@timer
def test():
    time.sleep(3)
    print("test函数运行完毕")
    return "我终于运行完成并返回值给你了"


res = test()  # 其实就是运行wrapper()函数
print(res)

#结果 
test函数运行完毕
执行完的时间3.000967502593994
我终于运行完成并返回值给你了

3) 函数闭包带有返回参数的

import time

def timer(func):
    def wrapper(name,age):
        start_time = time.time()
        res = func(name,age)   # 有了内存地址就可以直接执行func函数
        stop_time = time.time()
        print("执行完的时间%s" % (stop_time-start_time))
        return res

    return wrapper


# @timer 就相当于 test = timer(test)
@timer
def test(name,age):
    time.sleep(3)
    print("test函数运行完毕,name is %s,age is %s" % (name,age))
    return "我终于运行完成并返回值给你了"


res = test("rxz",29)  # 其实就是运行wrapper()函数
print(res)

#结果
test函数运行完毕,name is rxz,age is 29
执行完的时间3.0009922981262207
我终于运行完成并返回值给你了




import time

def timer(func):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = func(*args,**kwargs)   # 有了内存地址就可以直接执行func函数
        stop_time = time.time()
        print("执行完的时间%s" % (stop_time-start_time))
        return res

    return wrapper


# @timer 就相当于 test = timer(test)
@timer
def test(*args,**kwargs):
    time.sleep(3)
    print("test函数运行完毕,name is %s,age is %s" % (args,kwargs))
    return "我终于运行完成并返回值给你了"


#res = test("rxz",29)  # 其实就是运行wrapper()函数
res = test("rxz",29,"girl",job ="IT")  # 其实就是运行wrapper()函数
print(res)

#结果
test函数运行完毕,name is ('rxz', 29, 'girl'),age is {'job': 'IT'}
执行完的时间3.000976800918579
我终于运行完成并返回值给你了

补充: 解压序列

1)左边值必须和右边的值是一一对应的关系,否则会报错

>>> a,b,c={1,2,3}  #a=1,b=2,c=3
>>> a
1
>>> b
2
>>> c
3

>>> a,b,c,d,e='hello'   #解压序列,左边值必须和右边的值是一一对应的关系,否则会报错
>>> a 
'h'
>>> b
'e'
>>> c
'l'
>>> d
'l'
>>> e
'o'

2)取列表的第一个值和最后一个值

>>> l=[10,3,2,3,5,1,2,3,5,8,9]
>>> a,*_,c=l   #取第一个值和最后一个值
>>> a
10
>>> c
9

3)a,b的值互换位置

#原始方法
#a,b的值互换位置

>>> a=1
>>> b=2

>>> x=a
>>> x
1
>>> a=b
>>> b=x
>>> a,b
(2, 1)



# Python方法
#交换f1和f2的值

>>> f1=1
>>> f2=2


>>> f1,f2=f2,f1
>>> f1
2
>>> f2
1

八、装饰器的综合练习

import time

current_dic ={"username":None,"login":False}

user_list =[]

# 增加认证类型
def auth(auth_type ="filedb"):
    # 所有函数都加上验证功能
    def auth_func(func):
        def wrapper(*args, **kwargs):
            print("认证类型是%s" % auth_type)
            if auth_type =="filedb":
                with open("filedb","r+",encoding="utf-8",) as f:
                    for line in f:
                        line = line.strip('\n')
                        s = eval(line)
                        user_list.append(s)

                if current_dic["username"] and current_dic["login"]:
                    res = func(*args, **kwargs)
                    return res
                username = input("请输入用户名").strip()
                password = input("请输入密码").strip()
                for user_dic in user_list:
                    if username == user_dic["username"] and password == user_dic["password"]:
                        current_dic["username"] = username
                        current_dic["login"] = True
                        res = func(*args, **kwargs)
                        return res
            elif auth_type =="ldap":
                print("暂时未实现")
            else:
                print("用户名或者密码不正确")

        return wrapper
    return auth_func


@auth(auth_type ="filedb") #  auth_func = 运行auth函数  --->auth_func的内存地址
def index():
    print("欢迎来到我的世界")

@auth(auth_type ="ldap")
def home(name):
    print("欢迎登陆到%s主页面" % name)

@auth(auth_type ="sucessful")
def shopping_car(name):
    print("购物车了有[%s,%s,%s]" % ("衣服","化妆","玩具"))


index()
home("gsh")
shopping_car("zhaowei")




#结果
请输入用户名rxz
请输入密码123456
欢迎来到我的世界
认证类型是ldap
暂时未实现
认证类型是sucessful
用户名或者密码不正确

 

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值