zoukankan      html  css  js  c++  java
  • Python3学习之路~4.3 装饰器

    定义:本质是函数,装饰其他函数就是为其他函数添加附加功能。

    原则

    1. 不能修改被装饰函数的源代码
    2. 不能修改被装饰函数的调用方式

    实现装饰器知识储备:

    1. 函数即“变量”
    2. 高阶函数
      • 把一个函数名当做实参传递给另一个函数(在不修改函数源代码的情况下为其添加功能)
      • 返回值中包含函数名(不修改函数的调用方式)
    3. 嵌套函数

    高阶函数+嵌套函数——>装饰器

    什么是函数即“变量”?如下图,x,y,test都可以看做是内存中的门牌号,一个值如果没有门牌号就会被回收。匿名函数没有函数名,它如果不显示地用一个变量存放,就会被立即回收。

    根据上图理解下面几段代码:

    # 1.foo函数调用bar函数,bar函数未定义
    def foo():
        print('in the foo')
        bar()
    foo() # 报错:name 'bar' is not defined
    
    # 2.在foo函数调用之前,先定义bar函数,再定义foo函数
    def bar():
        print('in the bar')
    def foo():
        print('in the foo')
        bar()
    foo() # 运行成功
    
    # 3.在foo函数调用之前,先定义foo函数,再定义bar函数
    def foo():
        print('in the foo')
        bar()
    def bar():
        print('in the bar')
    foo() # 运行成功
    
    # 4.在foo函数调用bar函数之前未定义bar函数
    def foo():
        print('in the foo')
        bar()
    foo() # 报错:name 'bar' is not defined
    def bar():
        print('in the bar')
    View Code

    假如有如下一段代码:

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

    现在你要在不修改源代码的前提下为其增加一个功能:计算它的运行时间。此时你需要运用高阶函数的第一个知识点:把一个函数名当做实参传递给另一个函数。

    import time
    def bar():
        time.sleep(3)
        print('in the bar')
    
    def test1(func):
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s'%(stop_time-start_time))
    
    test1(bar) 
    # 输出:
    # in the bar
    # the func run time is 3.000171661376953

    上述代码还有缺陷,那就是改变了函数的调用方式。因为在实际的项目中可能会有多处都调用了这个函数,如果改变了函数的调用方式,意味着要在多处修改,你会被骂死。

    所以现在你要修改这段代码,使之不改变原来函数的调用方式。此时你需要运用高阶函数的第二个知识点:返回值中包含函数名。看如下的例子

    import time
    def bar():
        time.sleep(3)
        print('in the bar')
    
    def test2(func):
        print(func) #假设这个是新功能
        return func #返回func函数的内存地址
    
    bar=test2(bar) #覆盖原来的bar
    bar()

    将上述例子应用到你的代码中

    import time
    def bar():
        time.sleep(3)
        print('in the bar')
    
    def test1(func):
        start_time = time.time()
        return func #函数结束,后面代码不再执行
        stop_time = time.time()
        print('the func run time is %s'%(stop_time-start_time))
    
    bar=test1(bar)
    bar() # 输出:in the bar,并没有增加功能

    你会发现,上述代码并没有实现增加新的功能,但是思想是对的。那么我们该如何实现呢?此时需要使用嵌套函数。

    import time
    def bar():
        time.sleep(3)
        print('in the bar')
    
    def test1(func):
        def deco():
            start_time = time.time()
            func()
            stop_time = time.time()
            print('the func run time is %s'%(stop_time-start_time))
        return deco
    
    bar = test1(bar)
    bar()
    # 输出:
    # in the bar
    # the func run time is 3.0001718997955322

    以上代码在同时满足不改变函数的源代码不改变函数的调用方式两个前提条件的情况下,实现了为bar函数增加了一个新功能。

    其实,这是开发中一个常用的玩法,叫语法糖,官方名称“装饰器”。上面的代码可以更简单

    def timer(func):
        def deco():
            start_time = time.time()
            func()
            stop_time = time.time()
            print('the func run time is %s'%(stop_time-start_time))
        return deco
    
    @timer #在被装饰函数前面加上这个
    def bar():
        time.sleep(3)
        print('in the bar')
    
    #bar = timmer(bar) #去掉这行代码
    bar()
    # 输出:
    # in the bar
    # the func run time is 3.0001718997955322

    至此,我们利用高阶函数+嵌套函数实现了装饰器。

    下面利用装饰器装饰一个带参数的函数

    import time
    
    def timer(func):
        def warpper(x):
            start_time =time.time()
            func(x)
            stop_time =time.time()
            print("the func run time is %s"%(stop_time-start_time))
        return warpper
    
    @timer
    def test1(x):
        time.sleep(3)
        print("in the test1,x=%s"%x)
    
    test1(1)
    # 输出:
    # in the test1,x=1
    # the func run time is 3.000171422958374
    View Code

    写一个既可以装饰带参数、又可以装饰不带参数的函数的装饰器

    import time
    
    def timer(func):
        def warpper(*args,**kwargs):
            start_time =time.time()
            func(*args,**kwargs)
            stop_time =time.time()
            print("the func run time is %s"%(stop_time-start_time))
        return warpper
    
    @timer
    def test1():
        time.sleep(3)
        print("in the test1")
    
    @timer
    def test2(x):
        time.sleep(3)
        print("in the test2,x=%s"%x)
    
    test1()
    test2(1)
    
    # 输出:
    # in the test1
    # the func run time is 3.0001718997955322
    # in the test2,x=1
    # the func run time is 3.000171422958374
    View Code

    来一个更高级的,一个网站有3个页面,index page、home page和bbs page,现在要给home page和bbs page加上登录认证。如下

    user,passwd='alex','123456'
    
    def auth(func):
        def wrapper(*args,**kwargs):
            username=input('Username:').strip()
            password=input('Password:').strip()
            if user == username and passwd == password:
                print('33[32;1mUser has passed authentication33[0m')
                func(*args,**kwargs)
            else:
                exit('33[31;1mInvalid username or password33[0m')
        return wrapper
    
    def index():
        print('Welcome to index page')
    
    @auth
    def home():
        print('Welcome to home page')
    
    @auth
    def bbs():
        print('Welcome to bbs page')
    
    index()
    home()
    bbs()
    View Code 

    现在要让home page和bbs page的认证方式不同,一个为本地认证,一个为远程认证

    user,passwd='alex','123456'
    def auth(auth_type):
        def out_wrapper(func):
            def wrapper(*args,**kwargs):
                if auth_type == 'local':
                    username = input('Username:').strip()
                    password = input('Password:').strip()
                    if user == username and passwd == password:
                        print('33[32;1mUser has passed authentication33[0m')
                        func(*args,**kwargs)
                    else:
                        exit('33[31;1mInvalid username or password33[0m')
                elif auth_type == 'ldap':
                    print('此处代表ldap的认证方式,不会。。。假设认证成功。。。')
                    func(*args, **kwargs)
            return wrapper
        return out_wrapper
    
    def index():
        print('Welcome to index page')
    
    @auth(auth_type='local')
    def home():
        print('Welcome to home page')
    
    @auth(auth_type='ldap')
    def bbs():
        print('Welcome to bbs page')
    
    index()
    home()
    bbs()
    View Code

    好了,现在你应该学会装饰器了。赶快操练一下吧。

  • 相关阅读:
    ExtJs之表单(form)
    tf.where
    kuiper流式计算完整实例演示
    centos下搭建kuiper以及kuiper-manager
    Centos搭建EMQX和EMQ-Dashboard(踩坑精华版)
    代码生成器
    [MIT新技术大会]Jeff Bezos把EC2、S3和土耳其机器人描述为亚马逊“11年来的大规模万维网计算”方面的结晶,强调把后台基础设施作为服务
    《商业周刊》封面文章《谷歌和云的智慧》,讲到谷歌的新战略是“把惊人的计算能力放到众人手里”
    C# 连接 Sqlserver2005 Analysis Service的总结
    POJ_1064 二分搜索
  • 原文地址:https://www.cnblogs.com/zhengna/p/9205012.html
Copyright © 2011-2022 走看看