zoukankan      html  css  js  c++  java
  • Python(装饰器)

     

    eval 内容从字符串中提取出来,用Python运行一遍
    __name__得到函数名字

    闭包函数

    定义:
    函数内部定义的函数称为内部函数
    该内部函数包含对外部作用域,而不是对全局作用域的名字的引用
    那么该内部函数称为闭包函数

    闭包其实就是不管在哪里调用,都自带变量,不会有调用全局变量,而全局变量更改过了的错误产生

     

    特点:
    1、闭包函数自带作用域
    2、延迟计算/惰性计算,调用出来了函数地址,想用的时候加括号

    f.__closure__[1].cell_contents
    。__closure__能够查看闭包函数包含的变量,返回为none的时候表明其不是闭包函数

     

    另:通常需要把自己return出来,使自己能够在外部使用

     

     

     

    开放、封闭原则:

    对扩展是开放的,对修改是封闭的

         

    装饰器

    装饰器本质可以是任意可调用的对象,被装饰的对象也可以是任意可调用对象

    功能

    在不修改装饰器对象源代码,以及调用方式的前提下,为其添加新功能

    1、不修改源代码
    2、不修改调用方法
    3、添加新功能

       

    语法

    在被装饰对象的正上方写上 @[装饰器名字],

     

     

     

    添加被装饰函数的原生备注(在装饰器上添加@wraps)如下:

     

    import time
    import random
    from functools import wraps
    
    def timmer(func):
        @wraps(func)
        def wrapper():
            # wrapper.__doc__=func.__doc__
            start_time = time.time()
            func()
            stop_time=time.time()
            print('run time is %s' %(stop_time-start_time))
    
        # wrapper.__doc__=func.__doc__
        return wrapper
    
    @timmer
    def index():
        'index function'
        time.sleep(3)
        print('welecome to index page')
    
    print(index.__doc__)
    #调用被装饰函数的注释
    #============================================================================================
    # def zs(func):          #这里出过错,没有定义形参,使得函数内部调用了yuan(),但是yuan()是后面定义的
    #     def zs1():
    #         import time
    #         import random
    #         s_time=time.time()
    #         func()
    #         time.sleep(random.randrange(0,10))
    #         tp_time=time.time()
    #         print("bingo time:",tp_time-s_time)
    #     return zs1
    #
    # def yuan():
    #     print('hello!')
    #
    # print(zs)
    # yuan=zs(yuan)
    # print(yuan)
    # yuan()      #先定义后执行,定义阶段就把后面的装饰器里的func指定到原来的yuan了,所以不会和后面的yuan冲突
    #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

     

    #定义闭包函数的基本形式
    
    
    # def 外部函数名():
    #     内部函数需要的变量
    #     def 内部函数():
    #         引用外部变量
    #     return 内部函数
    
    # def deco():
    #     x=1
    #     def wrapper():
    #         print(x)
    #
    #     return wrapper
    #
    # wrapper=deco()
    #
    # print(wrapper)
    
    #装饰器的原理
    #装饰器修订
    import time
    import random
    #装饰器
    def timmer(func):
        def wrapper(*args,**kwargs):                             #能接收任意参数
            start_time = time.time()
            res=func(*args,**kwargs)                             #返回值
            stop_time=time.time()
            print('run time is %s' %(stop_time-start_time))
            return res
        return wrapper
    #被装饰函数
    
    @timmer
    def index():
        time.sleep(random.randrange(1,5))
        print('welecome to index page')
    @timmer
    def home(name):
        time.sleep(random.randrange(1,3))
        print('welecome to %s HOME page' %name)
        return 123123123123123123123123123123123123123123
    
    
    index()
    dic={'x':1}            #函数内改可变类型的全局变量能够直接改,而不可变类型不能直接改,需要global,
    l=[1,2,3]               #换句话说,函数内可以随便定义不可变类型的变量,不用关心是否和全局冲突
    salary=1000
    def foo():
        # print(dic)
        dic['x']=2
        dic['y']=3
        l.append(4)
        global salary
        salary=0
    foo()
    print(dic)
    print(l)
    print(salary)
    #===运行结果====================
    D:Python36python.exe D:/py/test/kong2.py
    {'x': 2, 'y': 3}
    [1, 2, 3, 4]
    0
    login_dic={                   #全局的可变类型,可以在函数内直接改,不可变类型,需要申明global,因为不可变类型更改值需要重新开内存空间,本身就相当于一个新变量
        'user':None,              #如上一个例子
        'status':False,
    }
    def auth(func):
        def wrapper(*args,**kwargs):
            if login_dic['user'] and login_dic['status']:
                res = func(*args, **kwargs)
                return res
    
            name=input('your name: ')
            password=input('your password: ')
            with open(db_path,'r',encoding='utf-8') as f:
                user_dic=eval(f.read())
    
            if name in user_dic and password == user_dic[name]:
                    print('login ok')
                    login_dic['user']=name
                    login_dic['status']=True
                    res=func(*args,**kwargs)
                    return res
            else:
                print('login err')
    
        return wrapper
    
    
    @auth #auth(index)
    def index():
        print('welecome to index')
    
    @auth
    def home(name):
        print('welecome %s to home page' %name)
    
    
    index()
    
    home('egon')

     

    #有参装饰器,在一般装饰器之外添加针对装饰函数的参数,增加了新参数,所以再加包一层
    def deco(auth_type='file'):
        def auth(func):
            def wrapper(*args,**kwargs):
                if auth_type == 'file':
                    print('文件的认证方式')
                elif auth_type == 'ldap':
                    print('ldap认证方式')
                elif auth_type == 'mysql':
                    print('mysql认证方式')
                else:
                    print('不知到的认证方式')
            return wrapper
        return auth
    
    @deco(auth_type='abc') #@auth #index=auth(index)
    def index():
        print('welecome to index')
    
    @deco(auth_type='ldap')
    def home(name):
        print('welecome %s to home page' %name)
    
    index()
    
    home('egon')
    '''
    编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
    '''
    from urllib.request import urlopen
    import os
    
    cache_path=r'C:UsersAdministratorPycharmProjectspython5期day8cache.txt'
    def make_cache(func):
        def wrapper(*args,**kwargs):
            if os.path.getsize(cache_path):
                #有缓存
                print('33[45m=========>有缓存33[0m')
                with open(cache_path,'rb') as f:
                    res=f.read()
    
            else:
                res=func(*args,**kwargs) #下载
                with open(cache_path,'wb') as f: #制作缓存
                    f.write(res)
    
            return res
    
        return wrapper
    
    @make_cache
    def get(url):
        return urlopen(url).read()
    func_dic={}                              ##直接使用地址调用函数能够跳过装饰器
    def make_dic(key):                   
        def deco(func):
            func_dic[key]=func
        return deco
    
    @make_dic('index')                  ##可以用在隐藏函数
    def f1():
        print('from f1')
    #-------------------------
    print(func_dic)
    while True:
        cmd=input('>>>: ').strip()
        if cmd in func_dic :
            func_dic[cmd]()
    
    #======执行情况==============
    
    D:Python36python.exe D:/py/test/kong2.py
    {'index': <function f1 at 0x000002DE9900C9D8>}
    >>>: index
    from f1
    >>>: 

    装饰器


    由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

    >>> def now():
    ...     print('2015-3-25')
    ...
    >>> f = now
    >>> f()
    2015-3-25
    

    函数对象有一个__name__属性,可以拿到函数的名字:

    >>> now.__name__
    'now'
    >>> f.__name__
    'now'
    

    现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

    本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

    def log(func):
        def wrapper(*args, **kw):
            print('call %s():' % func.__name__)
            return func(*args, **kw)
        return wrapper
    

    观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

    @log
    def now():
        print('2015-3-25')
    

    调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

    >>> now()
    call now():
    2015-3-25
    

    @log放到now()函数的定义处,相当于执行了语句:

    now = log(now)
    

    由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

    wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

    如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

    def log(text):
        def decorator(func):
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator
    

    这个3层嵌套的decorator用法如下:

    @log('execute')
    def now():
        print('2015-3-25')
    

    执行结果如下:

    >>> now()
    execute now():
    2015-3-25
    

    和两层嵌套的decorator相比,3层嵌套的效果是这样的:

    >>> now = log('execute')(now)
    

    我们来剖析上面的语句,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

    以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper'

    >>> now.__name__
    'wrapper'
    

    因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

    不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

    import functools
    
    def log(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('call %s():' % func.__name__)
            return func(*args, **kw)
        return wrapper
    

    或者针对带参数的decorator:

    import functools
    
    def log(text):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator
    

    import functools是导入functools模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。

    小结

    在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。

    decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。

    请编写一个decorator,能在函数调用的前后打印出'begin call''end call'的日志。

    再思考一下能否写出一个@log的decorator,使它既支持:

    @log
    def f():
        pass
    

    又支持:

    @log('execute')
    def f():
        pass
  • 相关阅读:
    android websocket推送
    proguardgui.bat来混淆已有的jar包
    android raw与assets区别
    Eclipse开发Android报错Jar mismatch! Fix your dependencies
    gc overhead limit exceeded
    如何签名apk,并让baidu地图正常显示
    Eclipse--Team--SVN--URL修改
    监听EditText
    android 注销
    从Android手机中取出已安装的app包,导出apk
  • 原文地址:https://www.cnblogs.com/zihe/p/7008723.html
Copyright © 2011-2022 走看看