zoukankan      html  css  js  c++  java
  • 大聊Python----装饰器

    什么是装饰器

      装饰器其实和函数没啥区别,都是用def去定义的,其本质就是函数,而功能就是装饰其他的函数,说白了就是为其他函数提供附加功能

    装饰器有什么作用

      比如你是一个公司的员工,你所写的程序里有100个函数,但是你所写的程序都已经上线运行了,突然有一天你的产品经理来找你,让你在咱们的APP上新增一段功能!那你说该怎么做这件事情?问题是你的程序都已经在运行了 ,不能修改你程序的源代码,否则会出现意想不到的效果!所以你想新增一项功能,但是不能修改你的源代码!那该怎么办呢?

      装饰器对待被修饰的函数是完全透明的状态!也就是函数感觉不到装饰器的存在,装饰器没有动函数的源代码,也不影响函数的运行。

    先看一下代码

    import time
    
    def timmer(func): # 装饰器
        def warpper(*args,**kwargs):
            start_time = time.time() # 开始的时间
            func()
            stop_time = time.time() # 结束的时间
            print('the fun run time is %s'%(stop_time - start_time))
        return warpper
    
    @timmer # 被装饰的函数
    def test1():
        time.sleep(3) # 延时3秒
        print("in the test1")
    
    test1()

    结果展示:

    实现装饰器知识储备

    1、函数即“变量

    机制

    函数调用顺序:其他高级语言类似,Python 不允许在函数未声明之前,对其进行引用或者调用

    错误示范:

    def foo():
        print 'in the foo'
        bar()
         
    foo()
     
    报错:
    in the foo
     
    Traceback (most recent call last):
      File "<pyshell#13>", line 1, in <module>
        foo()
      File "<pyshell#12>", line 3, in foo
        bar()
    NameError: global name 'bar' is not defined
    def foo():
        print 'foo'
        bar()
    foo()
    def bar():
        print 'bar'
         
    报错:NameError: global name 'bar' is not defined

    正确示范:(注意,python为解释执行,函数foo在调用前已经声明了bar和foo,所以bar和foo无顺序之分)

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

    2、高阶函数

      a、就是把函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其增添功能)

      示例:

    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 rime is %s"%(stop_time - start_time))
    
    test1(bar)

      结果:

      b、返回值中包含函数名(不修改函数的调用方式)

      示例:

    import time
    
    def bar():
        time.sleep(3)
        print("in the bar")
    def test2(func):
        print(func)
        return func
    
    bar = test2(bar)
    print(bar) # run bar

      效果:

    <function bar at 0x00000000007C48C8>
    <function bar at 0x00000000007C48C8>

    3、嵌套函数

    局部作用域和全局作用域的访问顺序

    x=0
    def grandpa():
        # x=1
        def dad():
            x=2
            def son():
                x=3
                print(x)
            son()
        dad()
    grandpa()

    显示效果为:

    3


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

    先看个例子

    import time
    def timer(func):
        def deco():
            start_time = time.time()
            func()
            stop_time = time.time()
            print("the func's run time is %s "%(stop_time - start_time))
        return deco
    
    @timer
    def test1():
        time.sleep(3)
        print("the test1 is running!")
    
    @timer
    def test2():
        time.sleep(3)
        print("the test2 is running!")
    
    test1()
    test2()

    结果显示:

    the test1 is running!
    the func's run time is 3.000171422958374
    the test2 is running!
    the func's run time is 3.000171661376953

    若想给test2传递参数,如下例,该怎么做呢?

    import time
    def timer(func):
        def deco():
            start_time = time.time()
            func()
            stop_time = time.time()
            print("the func's run time is %s "%(stop_time - start_time))
        return deco
    
    @timer
    def test1():
        time.sleep(3)
        print("the test1 is running!")
    
    @timer
    def test2(name):
        time.sleep(3)
        print("the test2 is running!",name)
    
    test1()
    test2("alex")

    通过执行,会出现下面的错误

    意思是说,deco()缺少了一个元素!

    那该怎么解决这个问题呢?

    咱们先来捋顺下思路!

    通过@timer可知test2() =timer(test2) = deco ,test2(name) = deco(name)

    所以可以看出要给deco传递实参,所以做了下面

    def timer(func):
        def deco(name):
            start_time = time.time()
            func(name)
            stop_time = time.time()
            print("the func's run time is %s "%(stop_time - start_time))
        return deco

    通过执行,会看到下面的结果!

    这回好了,test2不出错了,反而test1报错了,那个该怎么办呢?

    看下test1出错的原因是test1的deco缺少了一个实参,那么问题来了,test1该怎么处理呢?

    其实很简单,使用*args,和**kwargs尽可以完美的解决这个问题!

    现在来看看经过改正的程序

    import time
    def timer(func):
        def deco(*args,**kwargs): 
            start_time = time.time()
            func(*args,**kwargs)
            stop_time = time.time()
            print("the func's run time is %s "%(stop_time - start_time))
        return deco
    
    @timer
    def test1():
        time.sleep(3)
        print("the test1 is running!")
    
    @timer
    def test2(name):
        time.sleep(3)
        print("the test2 is running!",name)
    
    test1()
    test2("alex")

    结果显示:

    the test1 is running!
    the func's run time is 3.000171661376953
    the test2 is running! alex
    the func's run time is 3.000171422958374

    装饰器之高潮

    进入高潮之前,我们先来点前戏

    先看下面的代码

    import time
    user , passwd = "sutaoyu" , "sutaoyu01"
    
    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 and password33[0m")
        return wrapper
    
    @auth
    def index():
        print("welcome to index Page!")
    
    @auth
    def home():
        print("welcome to index Home!")
    
    @auth
    def bbs():
        print("welcome to index BBS!")
    
    
    index()
    home()
    bbs()

    其输出的结果为:

    输入正确时:

    输入错误时:

     

    现在当我们把前面的代码稍微改一下,装饰器代码不动,只改变下面两个地方

    def index():
        print("welcome to index Page!")
        return "Page"
    
    print(index())

    此时看输出的结果:

    会发现无结果并为None,那是因为什么呢?

    因为装饰器里的wrapper没有返回值所以,我们给他提供返回值即可!

    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")
                return func(*args,**kwargs)
            else:
                exit("33[31;1mInvalid username and password33[0m")
        return wrapper

    运行下程序,看看结果

    可以看出,问题已经解决!

    现在高潮部分即将来临:

    我能不能让我的home在认证的时候用本地的认证,但是bbs认证的时候用远程的ldap?

    答案是肯定的!

    先看下代码!

    import time
    user , passwd = "123" , "123"
    
    def auth(auth_type):
        print("auth_typr:",auth_type)
        def outer_wrapper(func):
            def wrapper(*args,**kwargs):
                print("wrapper func args:",*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 and password33[0m")
                elif auth_type == "ldap":
                    print("搞毛啊!!!!!")
            return wrapper
        return outer_wrapper
    
    # @auth
    def index():
        print("welcome to Index page!")
        return "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()

    运行的结果为:

    可以看出,我们的认真已经成功!

  • 相关阅读:
    安装黑苹果的config.plist
    navicat for mysql 导出数据的坑
    js中的深层复制
    js写的一个HashMap
    js前台数据校验
    nginx对上传文件大小的限制
    tomcat用户配置,内存配置,pid配置
    redis安装及使用
    程序端口被占用分析
    zookeeper+dubbo-admin开发dubbo应用
  • 原文地址:https://www.cnblogs.com/zhuifeng-mayi/p/9241906.html
Copyright © 2011-2022 走看看