• 【0812 | Day 13】闭包函数/装饰器/迭代器


    闭包函数

    一、什么是闭包?

    闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。

    def outter():
        x = 1
    
        def inner():
            print(x)
        return inner
    
    
    f = outter()
    
    
    def f2():
        x = 2
        f()
    
    
    f2()
    
    # 1
    
    1.1 两种为函数传参的方式

    为函数传参的方式一:使用参数的形式

    def func(x):
        print(x)
    
    
    func(1)
    func(1)
    func(1)
    
    # 1
    # 1
    # 1
    

    为函数传参的方式二:包给函数

    def outter(x):
        x = 1
    
        def inner():
            print(x)
        return inner
    
    
    f = outter(1)
    f()
    f()
    f()
    # 查看闭包的元素
    print(F"f.__closure__[0].cell_contents: {f.__closure__[0].cell_contents}")
    
    # 1
    # 1
    # 1
    # f.__closure__[0].cell_contents: 1
    

    举个栗子

    def f2(x):
        def f1():
            print(x)
        return f1
    
    f1_1 = f2(1) #返回f1 + 1
    f1_1() #调用f1,传参x = 1
    
    # 1
    

    二、闭包函数的应用

    闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。

    应用一(复杂):

    import requests
    
    
    def get(url):
        res = requests.get(url)
        print(res)
        
    get('https://www.baidu.com')
    get('https://www.baidu.com')
    get('https://www.baidu.com')
    get('https://www.cnblogs.com/linhaifeng')
    get('https://www.cnblogs.com/linhaifeng')
    get('https://www.cnblogs.com/linhaifeng')
    
    #https://www.baidu.com
    #https://www.baidu.com
    #https://www.baidu.com
    #https://www.cnblogs.com/linhaifeng
    #https://www.cnblogs.com/linhaifeng
    #https://www.cnblogs.com/linhaifeng
    

    应用二(闭包):

    #爬取
    import requests
    
    def func(url):
        def get():
            res = requests.get(url)
            print(res.text)
        return get
    
    baidu_spider = func('http://www.iqiyi.com/')
    baidu_spider()
    

    无参装饰器

    装饰器指的是为被装饰器对象添加额外功能,就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能。

    一、装饰器使用原则

    装饰器的实现必须遵循两大原则:

    1. 不修改被装饰对象的源代码
    2. 不修改被装饰对象的调用方式

    二、怎么用装饰器?

    改变源代码(index内部代码块被改变):

    import time
    
    
    def index():
        start = time.time()
        print('welcome to index')
        time.sleep(1)
        end = time.time()
        print(F"index run time is {start-end}")
    
    
    index()
    
    #welcome to index
    #index run time is -1.0008180141448975
    

    编写重复代码(index( )和f2( )都调用,而且time代码重复):

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    def f2():
        print('welcome to index')
        time.sleep(1)
    
    
    start = time.time()
    index()
    end = time.time()
    print(F"index run time is {start-end}")
    
    start = time.time()
    f2()
    end = time.time()
    print(F"f2 run time is {start-end}")
    
    #welcome to index
    #index run time is -1.0046868324279785
    #welcome to index
    #f2 run time is -1.000690221786499
    

    三、两种传参方式

    第一种传参方式:改变调用方式

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
        
    def time_count(func):
        start = time.time()
        func()  #func()=index(),打印'welcome to index'
        end = time.time()
        print(f"{func} time is {start-end}")  #打印
    
        
    time_count(index) #运行time_count(),同时传参给func
    
    #welcome to index
    #<function index at 0x102977378> time is -1.000748872756958
    

    第二种传参方式:包给函数-外包

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    def time_count(func):
        # func = 最原始的index
        def wrapper():
            start = time.time()
            func()
            end = time.time()
            print(f"{func} time is {start-end}")
        return wrapper
    
    # f = time_count(index)
    # f()
    
    
    index = time_count(index)  # index被重新定义,原来的index被覆盖,即index = wrapper,func = index
    index()  # wrapper()
    
    #依旧是以index()方式调用
    
    #welcome to index
    #<function index at 0x102977730> time is -1.0038220882415771
    

    四、完善装饰器

    上述的装饰器,最后调用index()的时候,其实是在调用wrapper(),因此如果原始的index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和wrapper()方法的返回值。

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
        return 123
    
    
    def time_count(func):
        # func = 最原始的index
        def wrapper():
            start = time.time()
            res = func()
            end = time.time()
            print(f"{func} time is {start-end}")
    
            return res
        return wrapper
    
    
    index = time_count(index)  #index = wapper, func = index
    res = index() #wapper(index)
    print(f"res: {res}")
    
    #welcome to index
    #<function index at 0x102977620> time is -1.0050289630889893
    #res: 123
    

    如果原始的index()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper()=index(),所以给wrapper()方法传参即可。

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
        return 123
    
    
    def home(name):
        print(f"welcome {name} to home page")
        time.sleep(1)
    
        return name
    
    
    def time_count(func):
        # func = 最原始的index
        def wrapper(*args, **kwargs):
            start = time.time()
            res = func(*args, **kwargs)
            end = time.time()
            print(f"{func} time is {start-end}")
    
            return res
        return wrapper
    
    
    home = time_count(home) #func = home, home = wrapper
    res = home('egon')  #wrapper('egon') = fun('egon') = home('egon') = 'welcome egon to home page' = 'egon'
    print(f"res: {res}")  # res = func('egon') = home('egon') = 'egon' 
    
    #welcome egon to home page
    #<function home at 0x102977378> time is -1.0039079189300537
    #res: egon
    

    五、装饰器语法糖

    在被装饰函数正上方,并且是单独一行写上@装饰器名

    import time
    
    
    def time_count(func):
        # func = 最原始的index
        def wrapper(*args, **kwargs):
            start = time.time()
            res = func(*args, **kwargs)
            end = time.time()
            print(f"{func} time is {start-end}")
    
            return res
        return wrapper
    
    
    @time_count  # home = time_count(home)
    def home(name):
        print(f"welcome {name} to home page")
        time.sleep(1)
    
        return name
    
    
    @time_count  # index = time_count(index)
    def index():
        print('welcome to index')
        time.sleep(1)
    
        return 123
    
    
    res = home('egon')  #func = home name = 'egon' --> welcome egon to home page --> print(f"{func} time is {start-end}")
    print(f"res: {res}") # egon
    
    #welcome egon to home page
    #<function home at 0x102977620> time is -1.0005171298980713
    #res: egon
    

    六、装饰器模板(重要)

    def deco(func):
    	def wrapper(*args,**kwargs):
    		res = func(*args,**kwargs)
    		return res
    	return wrapper
    

    有参装饰器

    无参装饰器套了两层有参装饰器套了三层

    import time
    
    current_user = {'username': None}
    
    
    def login(func):
        # func = 最原始的index
        def wrapper(*args, **kwargs):
            if current_user['username']:
                res = func(*args, **kwargs)
    
                return res
    
            user = input('username: ').strip()
            pwd = input('password: ').strip()
    
            if user == 'nick' and pwd == '123':
                print('login successful')
                current_uesr['usre'] = user
                res = func(*args, **kwargs)
    
                return res
            else:
                print('user or password error')
    
        return wrapper
    
    
    @login  #home = login(home)
    def home(name):
        print(f"welcome {name} to home page")
        time.sleep(1)
    
        return name
    
    
    @login  #index = login(index)  
    def index():
        print('welcome to index')
        time.sleep(1)
    
        return 123
    
    
    res = index() #运行index之前运行糖果 wrapper(index) 继续运行 返回res = func 继续运行 即真正的index()
    
    #username: nick
    #password: 123
    #login successful
    #welcome to index
    

    对于上面的登录注册,我们把用户登录成功的信息写入内存当中。但是在工业上,用户信息可以存在文本中、mysql中、mongodb当中,但是我们只让用户信息来自于file的用户可以认证。因此我们可以改写上述的装饰器。

    import time
    
    current_user = {'username': None}
    
    
    def login(func):
        # func = 最原始的index
        def wrapper(*args, **kwargs):
    
            if current_user['username']:
                res = func(*args, **kwargs)
    
                return res
    
            user = input('username: ').strip()
            pwd = input('password: ').strip()
            
            engine = 'file'
    
            if engine == 'file':
                print('base of file')
                if user == 'nick' and pwd == '123':
                    print('login successful')
                    current_uesr['usre'] = user
                    res = func(*args, **kwargs)
    
                    return res
                else:
                    print('user or password error')
            elif engine == 'mysql':
                print('base of mysql')
            elif engine == 'mongodb':
                print('base of mongodb')
            else:
                print('default')
    
        return wrapper
    
    
    @login
    def home(name):
        print(f"welcome {name} to home page")
        time.sleep(1)
    
    
    @login
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    res = index()
    
    #username: nick
    #password: 123
    #base of file
    #login successful
    #welcome to index
    

    一、三层闭包

    def f1(y):
    
        def f2():
            x = 1
    
            def f3():
                print(f"x: {x}")
                print(f"y: {y}")
            	return f3
        	return f2
    
    
    f2 = f1(2) #f2(y = 2)
    f3 = f2()  #x = 1
    f3()  #到外部找x, y  
    
    #倒着运行--->先找x, y--->找到x = 1--->y = 2--->结果输出
    
    #x: 1
    #y: 2
    

    现在需求改了,我们需要判断用户动态的获取用户密码的方式,如果是file类型的,我们则让用户进行认证。因此我们可以使用有参装饰器。

    import time
    
    current_uesr = {'username': None}
    
    
    def auth(engine='file'):
    
        def login(func):
            # func = 最原始的index
            def wrapper(*args, **kwargs):
    
                if current_user['username']:
                    res = func(*args, **kwargs)
    
                    return res
    
                user = input('username: ').strip()
                pwd = input('password: ').strip()
    
                if engine == 'file':
                    print('base of file')
                    if user == 'nick' and pwd == '123':
                        print('login successful')
                        current_uesr['usre'] = user
                        res = func(*args, **kwargs)
    
                        return res
                    else:
                        print('user or password error')
                elif engine == 'mysql':
                    print('base of mysql, please base of file')
                elif engine == 'mongodb':
                    print('base of mongodb, please base of file')
                else:
                    print('please base of file')
    
            	return wrapper
    
        	return login
    
    
    @auth(engine='mysql')
    def home(name):
        print(f"welcome {name} to home page")
        time.sleep(1)
    
    
    @auth(engine='file')
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    res = index()  
    
    #username: nick
    #password: 123
    #base of file
    #login successful
    #welcome to index
    

    注意:由于两层的装饰器,参数必须得固定位func,但是三层的装饰器解除了这个限制。我们不仅仅可以使用上述单个参数的三层装饰器,多个参数的只需要在三层装饰器中多加入几个参数即可。也就是说装饰器三层即可,多加一层反倒无用。

    迭代器

    迭代器:迭代的工具。

    迭代是更新换代,比如你子孙繁衍;迭代也可以说成是重复,但每一次的重复都是基于上一次的结果来的,例如计算机中的迭代开发,就是基于软件的上一个版本更新。

    以下代码就不是迭代,它只是单纯的重复

    while True:
    	print('*'*10)
    
    
    

    一、可迭代对象

    定义:具有__iter__方法的对象就是可迭代对象,除了数字类型和函数类型。

    注意:tuple(1)与tuple(1,)类型有区别

    # x = 1.__iter__  # SyntaxError: invalid syntax
    
    # 以下都是可迭代的对象
    
    name = 'nick'.__iter__
    lis = [1, 2].__iter__
    tup = (1, 2).__iter__
    dic = {'name': 'nick'}.__iter__
    s1 = {'a', 'b'}.__iter__
    f = open('49w.txt', 'w', encoding='utf-8')
    f.__iter__
    f.close()
    

    二、迭代器对象

    只有字符串列表都是依赖索引取值的,而其他的可迭代对象都是无法依赖索引取值的。因此我们得找到一个方法能让其他的可迭代对象不依赖索引取值。

    定义:具有__iter____next__方法的都是迭代器对象,只有文件

    缺点:

    1. 取值麻烦,只能一个一个取,并且只能往后取,值取了就没了
    2. 无法使用len()方法获取长度
    # 不依赖索引的数据类型迭代取值
    dic = {'a': 1, 'b': 2, 'c': 3}
    iter_dic = dic.__iter__()
    print(iter_dic.__next__())
    print(iter_dic.__next__())
    print(iter_dic.__next__())
    # print(iter_dic.__next__())  # StopIteration:
    
    #a
    #b
    #c
    
    # 依赖索引的数据类型迭代取值
    lis = [1, 2, 3]
    iter_lis = lis.__iter__()
    print(iter_lis.__next__())
    print(iter_lis.__next__())
    print(iter_lis.__next__())
    # print(iter_lis.__next__())  # StopIteration:
    
    #1
    #2
    #3
    

    上述的方法是非常繁琐的,我们可以使用while循环精简下。其中使用的try...except...为异常处理模块

    #for循环原理
    
    lt = [1,2,3]
    
    lt_iter = lt.__iter__()
    while 1:
        try:
            print(lt_iter.__next__())
        except StopIteration:
            break
    

    注意一:迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象

    注意二:for循环 == 迭代循环

    注意三:迭代器对象使用__iter__()还是迭代器对象

  • 相关阅读:
    flex4的s:states和mx:states的区别
    Flash Builder快捷键
    PE经典DIY案例1:全解开方案让量产PE也能
    U+V2深度隐藏PE制作技术初探
    SQL Server存储过程的删除方法
    利用sql server直接创建日历
    推荐一个好的数据库工具Embarcadero DBArtisan
    css样式大全(整理版)
    asp.net Excel导入&导出
    SQL删除重复数据方法
  • 原文地址:https://www.cnblogs.com/fxyadela/p/11341122.html
Copyright © 2020-2023  润新知