zoukankan      html  css  js  c++  java
  • python基础16_闭包_装饰器

    不了解是否其他语言也有类似 python 装饰器这样的东西。

    最近才发现ECMAScript6也是有生成器函数的,也有 yield  generator  

    装饰器的基础知识是闭包:

    # 闭包:嵌套函数,内部函数调用外部函数的变量
    def outer():
        a = 1
        def inner():
            print(a)
        # __closure__ 可以用来判断闭包
        # print(inner.__closure__)  # 打印 cell at ... 则表明它是一个闭包
        return inner  # 返回内部函数的内存地址,因为此函数用到了外部函数的变量,所以外部函数的变量也不会因函数的调用结束而消失
    inn = outer()
    inn()
    
    from urllib.request import urlopen
    # ret = urlopen('http://www.xiaohua100.cn/index.html').read()
    # print(ret)
    
    # def get_url():
    #     url = 'http://www.xiaohua100.cn/index.html'
    #     ret = urlopen(url).read()
    #     print(ret)
    #
    # get_url()
    
    # 闭包的简单示例:
    # def get_url():
    #     url = 'http://www.xiaohua100.cn/index.html'
    #     def get():
    #         ret = urlopen(url).read()
    #         print(ret)
    #     return get    # 得到 url 结果
    #
    # get_func = get_url()   # 将结果赋给变量,保存下来。
    # get_func()
    #

    装饰器的作用与原则

    import time
    
    
    def say_hello():
        time.sleep(1)
        print('hello, everyone...')
    
    
    # 定义一个闭包函数来增加功能
    def tmr(f):  # 装修器函数
        def inner():
            start = time.time()
            f()  # 被装饰函数
            end = time.time()
            print(end - start)
    
        return inner  # 传参数的名字,不加括号
    
    
    fun = tmr(say_hello)  # 使用闭包函数
    fun()
    
    
    # 装饰器的作用 —— 不想修改函数的调用方式 但是还想在原来的函数前后添加功能
    # timmer就是一个装饰器函数,只是对一个函数 有一些装饰作用
    
    # 原则: 开放封闭原则
    #   开放 : 对扩展是开放的
    #   封闭 : 对修改是封闭的
    
    def deco(f):
        def inner(*args, **kwargs):
            print('装饰器在函数之前')
            ret = f(*args, **kwargs)
            print("装饰器在函数之后")
    
        return inner
    
    
    @deco
    def say_hi():
        time.sleep(1)
        print('Hi, good morning ...')
    
    
    say_hi()
    #!/usr/bin/env python
    # coding:utf-8
    
    # 复习一下函数作为参数传递。
    # def f11():
    #     print(12345)
    #
    #
    # def f12(xxx):
    #     xxx()
    #
    #
    # f12(f11)
    
    ## 装修器:本质就是函数,功能是为其他函数添加附加功能
    # 原则:
    # 1. 不修改被装饰函数的源代码
    # 2. 不修改被修饰函数的调用方式
    
    ## 装饰器 = 高阶函数 + 函数嵌套 + 闭包
    
    def outer(func):
        def inner():
            print('log')
            ret = func()
            print('after')
            return ret
        return inner
    
    
    
    @outer
    def f1():
        print('F1')
        return '砍你哦.'
    
    @outer
    def f2():
        print('F2')
    
    
    @outer
    def f100():
        print('F100')

    在另一个文件中调用上面定义的函数,看看装饰器是否启作用:

    #!/usr/bin/env python
    # coding:utf-8
    
    import outer01
    
    outer01.f1()
    outer01.f2()
    outer01.f100()

    装饰器简单原理:

    #!/usr/bin/env python
    # coding:utf-8
    
    def outer(func):
        def inner():
            print('before')
            func()
            print('after')
        return inner
    
    
    ## 格式 @ + 函数名
    ## 功能:
    #   1. 自动执行outer函数并且将其下面的函数名f1当作参数传递。
    #   2. 将outer函数的返回值,重新赋值给f1
    @outer
    def f1():
        print('F1')
    
    # 一旦函数被装饰器装饰,函数就会被重新赋值成装饰器函数的内层函数
    f1()

    换一个文件调用函数:

    #!/usr/bin/env python
    # coding:utf-8
    
    
    import outer01
    
    res = outer01.f1() # 也拿到了原函数的返回值。
    
    print(res)

    带有参数的装饰器:

    #!/usr/bin/env python
    # coding:utf-8
    
    import time
    
    
    def timmer(func):
        def wrapper(*args, **kwargs):
            start_time = time.clock()
            res = func(*args, **kwargs)
            stop_time = time.clock()
            print('运行时间是:%s' % (stop_time - start_time))
            return res
    
        return wrapper
    
    
    @timmer
    def test(name, age):
        time.sleep(1)
        print('test执行完毕 %s %s' % (name, age))
        return 'test complete.'
    
    
    def test2(name, age, gender):
        time.sleep(1.2)
        print('测试2 %s %d %s' % (name, age, gender))
    
    
    res = test('alex', 22)
    print(res)
    
    test2('jerry', 88, 'male')

    带参数的装饰器:

    #!/usr/bin/env python
    # coding:utf-8
    
    user_list=[
        {'name':'alex','passwd':'123'},
        {'name':'tom','passwd':'456'},
        {'name':'jerry','passwd':'245'},
        {'name':'kang','passwd':'135'},
    ]
    
    current_dic = {'username':None,'login':True}
    
    
    def auth(auth_type='filedb'):
        def auth_func(func):
            def wapper(*args, **kwargs):
                print('认证类型是:',auth_type)
                if auth_type =='filedb':
                    if current_dic['username'] and current_dic['login']:
                        res = func(*args, **kwargs)
                        return res
                    username=input('请输入用户名:')
                    passwd=input('请输入密码:')
                    for user_dic in user_list:
                        if username ==user_dic['name'] and passwd == user_dic['passwd']:
                            current_dic['username'] = username
                            current_dic['login'] = True
                            res = func(*args, **kwargs)
                            return res
                    else:
                        print('用户名或密码错误.')
                elif auth_type =='ldap':
                    print('LDAP方式')
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('不知道什么方式.')
                    res = func(*args, **kwargs)
                    return res
            return wapper
        return auth_func
    
    
    @auth()
    def login():
        print("欢迎登录。" )
    
    
    @auth(auth_type='ldap')
    def shopping_cart(name):
        print('%s的购物车里有%s %s %s' % (name,'衣服','电池','牛奶'))
    
    
    @auth(auth_type='dsdsds')
    def home(name):
        print("%s 的主页" % name)
    
    
    login()
    shopping_cart('alex')
    home('alex')

    小练习: 模拟网站增加登录状态验证:

    #!/usr/bin/env python
    # coding:utf-8
    
    user_dic={'username':None,'login':False}
    
    def auth_func(func):
        def wapper(*args, **kwargs):
            if user_dic['username'] and user_dic['login']:
                res = func(*args, **kwargs)
                return res
            username=input('请输入用户名:')
            passwd=input('请输入密码:')
            if username =='user' and passwd == '123':
                user_dic['username'] = username
                user_dic['login'] = True
                res = func(*args, **kwargs)
                return res
            else:
                print('用户名或密码错误.')
        return wapper
    
    
    @auth_func
    def login():
        print("欢迎登录。" )
    
    
    @auth_func
    def shopping_cart(name):
        print('%s的购物车里有%s %s %s' % (name,'衣服','电池','牛奶'))
    
    
    @auth_func
    def home(name):
        print("%s 的主页" % name)
    
    
    login()
    shopping_cart('alex')
    home('alex')

      functools 模块中的 wraps

    from functools import wraps
    
    def wraper(f):
        @wraps(f)         # 此内置装饰器保证被装饰的函数的 __name__ 和 __doc__依然不变
        def inner(*args,**kwargs):
            '''
            装饰器内部函数 inner
            '''
            print('装饰在函数之前')
            ret = f(*args,**kwargs)
            print('装饰在函数之后')
            return ret
        return inner
    
    @wraper
    def holiday(day):
        '''
        这是一个放假函数
        '''
        print('公司决定放假{}天'.format(day))
        return 'very happy.'
    
    # 获取函数名 和 函数文档
    print(holiday.__name__)     # 装饰器添加之后,结果变成了 inner
    print(holiday.__doc__)      # 取函数的注释文档
    ret = holiday(7)
    print(ret)

    用装饰器为函数加上调用记录功能,记录每次调用的函数名写入文件

    # 用装饰器为函数加上调用记录功能,记录每次调用的函数名写入文件
    import time
    
    def recd(func):
        def inner(*args,**kwargs):
            with open('log','a',encoding='utf8') as f:
                f.write(time.strftime('%Y-%m-%d %X',time.localtime()) + '  ')
                f.write(func.__name__+'
    ')
            ret = func(*args,**kwargs)
            return ret
        return inner
    
    @recd
    def shop_add():
        print('增加了一件商品')
    
    @recd
    def shop_del():
        print('删除了某个商品')
    
    # print(time.asctime())
    # print(time.strftime('%Y-%m-%d %X',time.localtime()))
    
    shop_add()
    shop_del()

     假设有2个装饰器,装饰同一个函数。

    那么执行顺序是先执行第一个装饰器的前半部分,再执行第二个装饰器的前半部分,再执行被装饰的函数主体。

    主体执行完成后,再执行第二个装饰器的后半部分,最后执行第一个装饰器的后半部分。

    #多个装饰器装饰一个函数
    def wrapper1(func):
        def inner1():
            print('wrapper1 ,before func')
            ret = func()
            print('wrapper1 ,after func')
            return ret
        return inner1
    
    def wrapper2(func):
        def inner2():
            print('wrapper2 ,before func')
            ret = func()
            print('wrapper2 ,after func')
            return ret
        return inner2
    
    def wrapper3(func):
        def inner3():
            print('wrapper3 ,before func')
            ret = func()
            print('wrapper3 ,after func')
            return ret
        return inner3
    
    @wrapper3
    @wrapper2
    @wrapper1
    def f():
        print('in f')
        return '哈哈哈'
    
    print(f())
  • 相关阅读:
    简单的嵌套循环
    七、 二进制位运算
    六、字符串格式化--------列表常用操作
    JavaScript取消默认控件并添加新控件(DOM编程艺术第11章)
    JavaScript 字符串拼接 & setInterval()实现简单动画
    伪站创建代码-山东理工
    CSS常用样式
    CSS基础知识
    HTML5其他标签应用
    HTML表单的应用
  • 原文地址:https://www.cnblogs.com/frx9527/p/decorator.html
Copyright © 2011-2022 走看看