zoukankan      html  css  js  c++  java
  • python--闭包函数、装饰器

    先来点补充。

    x=1
    def foo():
        print(x)
    x=2
    
    foo()
    结果:
    2
    x=1
    def foo():
        global x
        x=10000000000000
        print(x)
    
    foo()
    print(x)
    结果:
    10000000000000
    10000000000000
    x=1
    def f1():
        x=2
        def f2():
            x=3
            def f3():
                # global x
                nonlocal x
                x=1000000000
            f3()
            print('f2内部的打印',x)
        f2()
        print('f1内部的打印', x)
    
    f1()
    print(x)
    结果:
    f2内部的打印 1000000000
    f1内部的打印 2
    1
    def foo():
        x = 1
        def wrapper():
            nonlocal x
            x = 1000
            print(x)
        return wrapper
    
    f = foo()
    print(f.__closure__)
    结果:
    (<cell at 0x0000000002147498: int object at 0x00000000601CB440>,)

    闭包函数

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

    name='alex'
    def func():
        name='egon'
        def bar():
            print(name)
        return bar
    
    b=func()
    print(b)
    结果:
    <function func.<locals>.bar at 0x0000000001E90730>
    name='alex'
    def func():
        name='egon'
        x=1000000000000000000000
        def bar():
            print(name)
            print(x)
            a=1
            b=2
        return bar
    #
    f=func()
    
    print(f.__closure__[0].cell_contents)
    print(f.__closure__[1].cell_contents)
    结果:
    egon
    1000000000000000000000
    name='alex'
    def func():
        name='egon'
        x=1000000000000000000000
        def bar():
            print(name)
            print(x)
            a=1
            b=2
        print(bar.__closure__)
    
    func()
    结果:
    (<cell at 0x0000000002167498: str object at 0x00000000021EE030>, <cell at 0x00000000021674C8: int object at 0x00000000021F7350>)

    闭包函数的特点:

      • 自带作用域
      • 延迟计算
    name='alex'
    def func():
        def bar():
            print(name)
        return bar
    
    f=func()
    print(f.__closure__)
    
    f()
    结果:
    None
    alex
    money=1000
    def tell_ino(name):
        print('%s have money %s' %(name,money))
    
    tell_ino('egon')
    
    def f1():
        money=10
        tell_ino('egon')
    
    f1()
    结果:
    egon have money 1000
    egon have money 1000
    money=1000
    def f1():
        money=10
        def tell_ino(name):
            print('%s have money %s' %(name,money))
        tell_ino('egon')
    
    f1()
    结果:
    egon have money 10

    #包一层

    def wrapper():
        money=1000
        def tell_info():
            print('egon have money %s' % money)
        return tell_info
    tell_info
    =wrapper() print(tell_info) def foo(): money=100 tell_info() foo() 结果: <function wrapper.<locals>.tell_info at 0x00000000023F0730> egon have money 1000

    #包两层

    def aaa():
        name = 'egon'
        def wrapper():
            money = 1000
            def tell_info():
                print('egon have money %s' % money)
                print('my name is %s' % name)
            return tell_info
        return wrapper
    
    wrapper = aaa()
    tell_info = wrapper()
    print(tell_info.__closure__[0].cell_contents)
    print(tell_info.__closure__[1].cell_contents)
    结果:
    1000
    egon


    我们可以看到,wrapper中引用了外围作用域变量name,但name信息存在于wrapper的定义之外。我们称namewrapper环境变量

    同样的,tell_info中引用了外围作用域变量moneymoney信息存在于tell_info的定义之外。我们称moneytell_info环境变量;一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。
    在Python中,所谓的闭包是一个含有环境变量的函数对象。环境变量被保存在函数对象的__closure__属性中。

    '''

    def tell_info(name):
        print('%s have money %s' % (name,money))
    
    def foo():
        money = 100
        tell_info('egon')
    
    foo()
    结果:
    NameError: name 'money' is not defined

    报错NameError: name 'money' is not defined

    原因:
        函数的作用域关系在函数定义阶段就已经固定,与调用位置无关
        无论函数在何处调用,都需要回到定义阶段去找对应的作用域关系
        此例:虽然tell_info('egon')是在foo内调用并且引用money,但仍需要回到定义
        tell_info的阶段去找作用域关系,而定义时tell_info引用的money就是全局的money
        如果全局不存在则抛出异常NameError

    '''

    #定义闭包函数的基本形式:

     def 外部函数名():
         内部函数需要的变量
         def 内部函数():
             引用外部变量
         return 内部函数

    def deco():
        x = 1
        def wrapper():
            print(x)
    
        return wrapper
    
    wrapper = deco()
    
    print(wrapper)
    结果:
    <function deco.<locals>.wrapper at 0x00000000027B0730>
    
    def deco1():
        y = 2
        def deco():
            x = 1
            def wrapper():
                print(x)
                print(y)
    
            return wrapper
        return deco
    
    deco = deco1()
    wrapper = deco()
    
    wrapper()
    结果:
    1
    2
    from urllib.request import urlopen
    
    print(urlopen('http://www.xiaohua100.cn/').read())
    print(urlopen('https://www.python.org').read())
    
    def get(url):
        return urlopen(url).read()
    
    print(get('http://www.xiaohua100.cn/'))
    结果:
    b'xefxbbxbf<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">...
    b'<!doctype html>
    <!--[if lt IE 7]>   <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9">   <![endif]-->
    <!--[if IE 7]>      <html class="no-js ie7 lt-ie8 lt-ie9">...
    b'xefxbbxbf<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">...
    from urllib.request import urlopen
    def index(url):
        # url='http://www.xiaohua100.cn/'
        def get():
            return urlopen(url).read()
        return get
    
    xiaohua=index('http://www.xiaohua100.cn/')
    
    python=index('https://www.python.org')
    
    baidu=index('http://www.baidu.com')
    
    print(python())
    结果:
    b'<!doctype html>
    <!--[if lt IE 7]>   <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9">   <![endif]-->
    <!--[if IE 7]>      <html class="no-js ie7 lt-ie8 l
    t-ie9">...

    装饰器

    '''
    一:开放封闭原则,对扩展是开放的,对修改是封闭的

    二:装饰器,装饰器本质可以是任意可调用对象,被装饰的对象也可以是任意
        可调用对象,
        装饰器的功能是:
            在不修改被装饰对象源代码以及调用方式的前提下为期添加新功能

            原则:
                1.不修改源代码
                2.不修改调用方法
            目标:添加新功能
    '''

    import time
    import random
    #装饰器
    def timmer(func):
        # func=index
        def wrapper():
            start_time = time.time()
            func() #index()
            stop_time = time.time()
            print('run time is %s' % (stop_time-start_time))
        return wrapper
    #被装饰函数
    def index():
        time.sleep(random.randrange(1, 5))
        print('welcome to index page')
    
    def home():
        time.sleep(random.randrange(1, 3))
        print('welcome to HOME page')
    
    index = timmer(index) #index=wrapper
    home = timmer(home)
    
    index() #wrapper()
    home()
    结果:
    welcome to index page
    run time is 1.0000572204589844
    welcome to HOME page
    run time is 1.0000572204589844

    #装饰器的语法:在被装饰对象的正上方的单独一行,@装饰器名字

    import time
    import random
    #装饰器
    def timmer(func):
        def wrapper():
            start_time = time.time()
            func()
            stop_time = time.time()
            print('run time is %s' % (stop_time-start_time))
        return wrapper
    #被装饰函数
    @timmer #index=timmer(index)
    def index():
        time.sleep(random.randrange(1, 5))
        print('welcome to index page')
    @timmer #home=timmer(home)
    def home():
        time.sleep(random.randrange(1, 3))
        print('welcome to HOME page')
    
    index() #wrapper()
    home()
    结果:
    welcome to index page
    run time is 4.0002288818359375
    welcome to HOME page
    run time is 1.0000574588775635

    #加多个装饰器

    定义阶段外部函数的执行顺序是自下而上
    调用阶段内部函数的执行顺序是自上而下

    import time
    import random
    #装饰器
    def timmer(func):
        def wrapper():
            start_time = time.time()
            func()
            stop_time = time.time()
            print('run time is %s' % (stop_time-start_time))
        return wrapper
    def auth(func):
        def deco():
            name = input('name: ')
            password = input('password: ')
            if name == 'egon' and password == '123':
                print('login successful')
                func() #wrapper()
            else:
                print('login err')
        return deco
    
    #被装饰函数
    @auth #index=auth(wrapper) #index=deco                      #index=auth(wrapper) #index=deco
    @timmer #index=timmer(index) #index=wrapper
    def index():
        # time.sleep(random.randrange(1,5))
        time.sleep(3)
        print('welcome to index page')
    
    @timmer
    @auth
    def home():
        time.sleep(random.randrange(1, 3))
        print('welcome to HOME page')
    
    index() #deco()
    home()
    结果:
    name: egon
    password: 123
    login successful
    welcome to index page
    run time is 3.000171661376953
    name: egon
    password: 123
    login successful
    welcome to HOME page
    run time is 5.638322591781616

    #装饰器修订

    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('welcome to index page')
    @timmer
    def home(name):
        time.sleep(random.randrange(1, 3))
        print('welcome to %s HOME page' % name)
        return 123123123123123123123123123123123123123123
    
    res1 = index()
    print('index return %s' % res1)
    res2 = home('egon') #wraper()
    print('home return %s' % res2)
    结果:
    welcome to index page
    run time is 1.0000572204589844
    index return None
    welcome to egon HOME page
    run time is 2.0001142024993896
    home return 123123123123123123123123123123123123123123
    作业:
    一:编写函数,(函数执行的时间是随机的)
    二:编写装饰器,为函数加上统计时间的功能
    三:编写装饰器,为函数加上认证的功能
    
    四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
        注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
    
    五:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
    
    六:为题目五编写装饰器,实现缓存网页内容的功能:
        具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
    
    七:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
    答案:
    # 一:编写函数,(函数执行的时间是随机的)
    # import time
    # import random
    # def index():
    #     time.sleep(random.randrange(1,5))
    #     print('welcome to index page')
    #
    # index()
    
    # 二:编写装饰器,为函数加上统计时间的功能
    # import time
    # import random
    # def timmer(func):
    #     # func=index
    #     def wrapper():
    #         start_time = time.time()
    #         func() #index()
    #         stop_time = time.time()
    #         print('running time is %s'%(stop_time -start_time ))
    #     return wrapper
    #
    # @timmer
    # def index():
    #     time.sleep(random.randrange(1,5))
    #     print('welcome to index page')
    #
    # index()
    
    
    # 三:编写装饰器,为函数加上认证的功能
    # import time
    # import random
    # def auth(func):
    #     #func=register
    #     def wrapper():
    #         username=input('name: ')
    #         password=input('password: ')
    #         if username =='kaylee' and password =='123':
    #             print('login successful')
    #             func()
    #         else:
    #             print('login error')
    #     return wrapper
    #
    # @auth
    # def register():
    #     time.sleep(random.randrange(1,5))
    #     print('welcome to register page')
    #
    # register()
    
    
    # 四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
    #     注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
    # user_dic={'egon':'123',
    #           'kaylee':'234',
    #           'lily':'345'}
    # with open('decorate2.txt','w',encoding='utf-8') as f:
    #     f.write(str(user_dic))
    
    # decorate2_path=r'C:UsersAdministratorPycharmProjectsuntitleddecorate2.txt'
    # # with open('decorate2.txt','r',encoding='utf-8') as f:
    # #     res=f.read()
    # #     user_dic=eval(res)
    #
    # login_dic={'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('name: ')
    #         psw=input('password: ')
    #         with open(decorate2_path,'r',encoding='utf-8') as f:
    #             user_dic=eval(f.read())
    #
    #         if name in user_dic and psw == user_dic[name]:
    #             print('login ok')
    #             login_dic['user']=name
    #             login_dic['status']=True
    #             res=func(*args,**kwargs)
    #             return res
    #         else:
    #             print('login error')
    #
    #     return wrapper
    #
    # @auth
    # def index():
    #     print('welcome to index')
    # @auth
    # def home(name):
    #     print('welcome to %s home page'%name)
    #
    # index()
    # home('egon')
    
    
    
    # 五:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
    # from urllib.request import urlopen
    #
    # def get(url):
    #         return urlopen(url).read()
    #
    # print(get('http://www.baidu.com'))
    
    
    
    # 六:为题目五编写装饰器,实现缓存网页内容的功能:
    #     具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
    # from urllib.request import urlopen
    # import os
    #
    # cache_path=r'C:UsersAdministratorPycharmProjectsuntitledcache.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()
    #
    # print('=========>first')
    # print(get('https://www.python.org'))
    # print('=========>second')
    # print(get('https://www.python.org'))
    # print('=========>third')
    # print(get('https://www.python.org'))
    
    
    #七:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
    func_dic={}
    def deco(key):
        def deco2(func):
            func_dic[key]=func
        return deco2
    
    @deco('f1')
    def f1():
        print('from f1')
    
    @deco('f2')
    def f2():
        print('from f2')
    
    @deco('f3')
    def f3():
        print('from f3')
    
    print(func_dic )
    
    while True:
        cmd=input('>>: ').strip()
        if cmd in func_dic:
            func_dic[cmd]()
  • 相关阅读:
    电脑族,记忆力下降怎么办
    asp.net入門經典讀書筆記
    GridView
    如何学习开源库
    i++与++i区别使用
    计算二次方程根
    MySQL Dump/Restore
    利用SetConsoleCursorPosition控制命令行光标位置
    How to upgrade to Ubuntu 10.10 Maverick Meerkat from ubuntu 10.04 lucid, karmic| Desktop & Server
    通过一条命令获取最快的 apt 源镜像
  • 原文地址:https://www.cnblogs.com/metianzing/p/7009616.html
Copyright © 2011-2022 走看看