python学习笔记17:装饰器

装饰器:
1、定义:本质是函数。(装饰其他函数)为其他函数添加附加功能。
器:函数。装饰:装饰其他函数。基本语法都是用def去定义。

2、原则:
{配合下面的例子理解}
a.不能修改被装饰的函数的源代码。
b.不能修改被装饰的函数的调用方式。
装饰器对它装饰的函数是完全透明的。即对函数本身来说,感受不到装饰器的存在。

为已有函数增加功能
实现方式一:
在已有的函数中,分别加入该功能。
弊端:工作量太大。

def test1():
    pass

def test2():
    pass


'''调用函数'''
test1()
test2()


def test1():     #原功能写一遍、繁琐冗余
    pass
print('logging') #增加的功能


def test2():     #原功能写一遍、繁琐冗余
    pass
print('logging') #增加的功能

打印结果:
logging
logging

为已有函数增加功能
实现方式二:
重新定义一个函数,其他函数来调用。

优点:较上一个方法,避免冗余代码。

弊端:已有程序已在生产环境运行较稳定。若新功能需要涉及多个功能,需要在多处函数里修改,
若使用这个方式,对原代码做了改动,容易使稳定的环境出现隐患。
所以,函数一旦写好了,原则上就不能动它的源代码了。

源代码相关的函数的调用方式同理不能被改动。如test1()、test2(),写好了就不要再修改了。
所以就需要装饰器。


def logger():             #原函数基础上,重新定义了一个函数
    print('logging')      #增加的功能(在一定意义上,修改了原函数)

def test1():
    pass
logger()                  # 直接调用

def test2():
    pass
logger()                  # 直接调用

打印结果:
logging
logging

使用装饰器的方法:@装饰器名
下方的test1()是个函数。装饰器timmer用来装饰它,从这个例子里可以体现装饰器的原则

'''
感受装饰器的好处:
举例:
'''
import time
'''装饰器'''
def timmer(func):
    def warpper(*args,**kwargs):
        start_time=time.time()
        func()
        stop_time=time.time()
        print('the func run time is %s'%(stop_time-start_time))
    return warpper


@timmer
def test1():
    time.sleep(3)
    print('in the test1')

test1()

打印结果:
in the test1
the func run time is 3.000286817550659

实现装饰器知识储备:
1、函数即"变量"
2、高阶函数
3、嵌套函数

公式:高阶函数+嵌套函数==》装饰器

函数即变量:
定义一个函数,就相当于将函数体,定义给了函数名。

函数和变量一样,都有内存回收机制(内部存在定时器,一段时间后就会清理内存中没被引用的数据,将空间回收),只要变量名不存在,其内容空间就会被清空回收。

例如:用关键字def定义的函数有函数名,匿名函数没有函数名,在内存中存放函数体时,没有函数名称的内容就会被定时回收。如果给匿名函数指定一个函数名,那将匿名函数的函数体,比喻为一栋大厦的某个房间,那指定了函数名,就相当于有了门牌号,函数体就有地儿可存,除非删除门牌号,否则就不会被清空。

另,所有被存放到相应“门牌号”下(内存空间)的内容,无论是变量还是函数体,都是以字符串形式存储的。

无论是变量还是函数,使用时一定经过两步,定义调用

python是一种解释型语言,先定义再调用。所以定义函数的顺序,并不影响调用。
因为在调用之前,定义的函数已经被python解释到了,被解释到了,内存当中就存在了,也就说这个时候就能用了。

def foo():
    print('in the foo')
    bar()

#foo()   # 此时调用函数会报错,原因是没定义bar()这个函数

def foo():
    print('in the foo')
    bar()
def bar():
    print('in the bar')

def foo():
    print('in the foo')
    bar()

foo()


打印结果:
in the foo
in the bar

def foo():            # 先定义的这个函数,只是被python解释器以字符串先放在了内存中,即内存中已存放了该函数体
    print('in the foo')
    bar()

def bar():            # 这部分也和上面一样,被python解释器以字符串的形式放在了内存中,即内存中已存放了该函数体
    print('in the bar')

foo()                 # 最后,通过函数名调用的时候,用到了已经放在内存中的函数。满足函数先定义再调用的特点。所以可以运行。

打印结果:
in the foo
in the bar

高阶函数&嵌套函数&匿名函数

变量可以指向函数(调用函数的时候,把这个函数的结果指向一个变量),
函数的参数能接收变量(把一个变量当成参数传给函数),
那么一个函数就可以接收另一个函数作为参数,这样的函数就叫做高阶函数。传的是函数名。

'''abs本身就是一个函数,是系统的内置函数,作用是返回参数的绝对值'''
def add(a,b,f):
    return f(a)+f(b)


res = add(3,-6,abs)
print(res)


打印结果:
9

高阶函数(结合函数即变量的知识理解):
a.把一个函数名当作实参传给另一个函数(在不修改被装饰函数情况下,为其添加功能)
b.返回值中包含函数名(不修改函数的调用方式)

def bar():
    print('in the bar')

def test1(func):
    print(func)    # 打印出来的是bar的内存地址
    func()   # test1(bar)传给test1(func),即表示func=bar,将函数名bar赋值给func,此时func所指向的内存地址就是bar的内存地址,则func()=bar()

test1(bar)   # 打印出来的是个内存地址(因为bar是个门牌号即函数名)



打印结果:
<function bar at 0x000001F4F44CB9D8>
in the bar

a举例:
test1此时是高阶函数,装饰了bar这个函数,给bar加了功能,但没改bar这个函数本身

import time
def bar():
    time.sleep(3)
    print('in the bar')

def test1(func):
    start_time = time.time()    # 统计运行程序的开始时间,这一步是指bar()运行开始的时间
    func()    # 这一步运行的是bar()这个函数
    stop_time = time.time()     # 统计运行程序的结束时间,这一步是指bar()运行结束的时间
    print('the func run time is %s'%(stop_time-start_time))

test1(bar)



打印结果:
in the bar
the func run time is 3.000504970550537

b举例:

import time
def bar():
    time.sleep(3)
    print('in the bar')

def test2(func):
    print(func)
    return func

# print(test2(bar))   # 此处打印的目的是看test2这个函数的返回值,此处不能用bar()作为实参的原因是,不符合高阶函数的要求了。传的应该是函数名
bar = test2(bar)    # 获取到函数的返回结果(前提是高阶函数中return了返回值)。# 高阶函数有返回值,且返回值为被调用函数的函数体,才能通过这种方式将整个函数的运行结果赋值给原函数名
bar()    # run bar   没有修改函数的调用方式


打印结果:
<function bar at 0x000001FB2829B9D8>
in the bar

嵌套函数
在一个函数体内,用def再声明一个函数,来调用,叫做函数的嵌套


def foo():
    print('in the foo')
    def bar():      # 局部变量不能在外部调用,只能在内部调用
        print('in the bar')
    bar()

foo()

#def test1():
#    test2()
#test1()   # 这种方式,不叫函数的嵌套

打印结果:
in the foo
in the bar

嵌套函数的作用域
局部作用域和全局作用域的访问顺序,是从最里层开始找,所以下面的例子中,最终结果是最里层的3

x= 0  #  全局变量
def grandpa():
    # x=1
    def dad():
        x=2   # 局部变量
        def son():
            x=3    # 局部变量
            print(x)
        son()
    dad()
    
grandpa()  # 运行grandpa这个函数时,遇到了子函数dad,如果不通过dad()调用该函数使其运行,就相当于什么都没运行

打印结果:
3

匿名函数:有的函数是不用起名的,这样的函数被称作匿名函数

calc = lambda x:x*3

print(calc(3))


打印结果:
9

本节小结
1.装饰器本质
2.实现装饰器的知识储备

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值