• 闭包函数&装饰器&迭代器


    闭包函数

    闭包函数是函数传参的一种方式。它可以把变量和函数一起包起来,下次要直接调用

    我们先来看一下普通的传参方式:

    def inner(x):
        print(x)
    
    inner(1)
    inner(1)
    inner(1)
    

    看上去好像也蛮简单的,但是如果你要传递的值会很多呢,比如要传递摸一个复杂的网址,那么就会变成:

    inner(https://image.baidu.com/search/detailct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=python&step_word=&ie=utf8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined&copyright=undefined&cs=2274312350,3438054066&os=1842179554,428481594&simid=4207124190,751379936&pn=4&rn=1&di=93940&ln=1299&fr=&fmq=1565592554366_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=http%3A%2F%2Fwww.haoracle.com%2Fuploads%2Fimages%2F181214%2F1544751939757415.jpg&rpstart=0&rpnum=0&adpicid=0&force=undefined&ctd=1565592579900^3_1247X953%1)
    

    ???如果要多传几遍岂不是人都傻了。。

    所以这个时候就可以用到闭包函数了:

    def outter(x):
    
        def inner():
            print(x)
    
        return inner
    
    
    inner = outter(2)  # inner  # f = inner
    inner()
    inner()
    inner()
    

    它将所需要的函数A封装在另一个函数B里,调用的时候只要简单的调用函数B就行了

    简单来说,闭包函数是传参的另外一种方式, 参数+函数包在一起返回出去

    这个时候可能又有人要问了,我传参的时候不是只要Ctrl+c, Crtl+v 不就好了?其实闭包函数主要应用就是装饰器,接下来我会一一讲解。

    装饰器

    装饰器是装饰的工具(函数),具有装饰别的函数的作用

    装饰器本质就是一个函数A,装饰的对象也是一个函数B,用一个函数A去装饰一个函数B

    它的结构大概是这样的:

    def A():
        """装饰器"""
        pass
    
    def B():
        """被装饰的对象"""
        pass
    
    B()
    

    那么又会有人问了,我用别的方法也可以办到装饰别的函数啊?

    但是那些方法都不如装饰器来的简洁明了。接下来我就要将那些方法和装饰器进行比较

    首先先给定一个函数index

    import time
    def index():
        """被装饰的函数"""
        print('hello, index')
    	sleep.sleep(1)
    index()
    

    接下来要给这个函数装上打印他运行时间的功能,应该怎么做呢

    方法一:改变主体代码

    import time
    
    def index():
        start = time.time()
        print('hello, index')
        time.sleep(1)
        end = time.time()
        print(end - start)
    
    
    index()
    

    这种方法看似简单,却不实用。如果你需要在很多段代码中加上打印运行时间这一功能的的话,每段代码都改即浪费时间,还可能出现,也大大增加了工作量,所以是不可取的

    方法二:改变调用方式

    import time
    def index():
        """被装饰的函数"""
        print('hello, index')
    
    start = time.time()
    index()
    time.sleep(1)
    end = time.time()
    print(end-start)
    

    这种方法虽然不再改变函数本身了,但是却改变了函数的调用方式,当你以后再需要为别的函数添加功能,依旧要打一大段的代码,这肯定是不可取的

    这里我就将隆重介绍Python的装饰器功能了。在此顺便提一句,装饰器是Python独有的功能,只此一家,别无分号。

    import time
    
    
    def deco(func):  # func = index
        """装饰器函数"""
        def inner():
            start = time.time()
            func()  # index
            time.sleep(1)
            end = time.time()
            print(end - start)
    
        return inner
    
    def index():
        """被装饰的函数"""
        print('hello, index')
    
    
    def index1():
        print('hello, index1')
    
    
    index = deco(index)  # index = inner
    index()
    
    
    index1 = deco(index1)
    index1()
    

    你会发现,无论是函数本身还是调用方式,都没有被更改。如果下次再想为其他函数添加功能,只要简单的语句就可以完成

    二层装饰器

    如果你能充分理解上面这段代码,就可以先恭喜你已经踏出装饰器的第一步了。

    这时候有的人又会问了,如果我想往函数里丢参数应该怎么办?我想丢多个参数应该怎么办?

    这个时候之前学的可变长参数就派上用处了

    import time
    
    
    def deco(func):
        def f1(*args, **kwargs):
            print('args:',args)  # (10,)
            print('kwargs:',kwargs)
            start = time.time()
            # *args = *(10,) 10
            res = func(*args, **kwargs)  # 真正的index()
            end = time.time()
            print(end - start)
            return res
        return f1
    
    def index(x, a=1):
        print('x', x)
        print('a', a)
        print('hello index')
        time.sleep(1)
    
        return 123
    
    # 重新创建的index = deco(index真正的index)
    index = deco(index)  # index  = f1
    index(10)  # f1(1)
    

    这个时候无论你原本的函数中需要多少个参数,装饰器都可以对它进行装饰了

    如果这个时候你已经对代码感到困惑的时候,也不要紧,可以先了解一下装饰器的模板,以后再慢慢摸索

    
    def login(func):
        def iwrapper(*args, **kwargs):
        	# 想要装饰的内容
            res = func(*args, **kwargs)  # shopping()
            return res
    
    
        return wrapper
    

    语法糖

    如果你觉得装饰器已经很简单了,那么只有你想不到,没有Python办不到的。你甚至只要在原函数前加上@deco,就可以实现装饰器的功能了。

    import time
    
    
    def deco(func):
        def f1(*args, **kwargs):
            print('args:',args)  # (10,)
            print('kwargs:',kwargs)
            start = time.time()
            # *args = *(10,) 10
            res = func(*args, **kwargs)  # 真正的index()
            end = time.time()
            print(end - start)
            return res
        return f1
    
    @deco   # 语法糖(更精简的代码)   index = deco(index)
    def index(x, a=1):
        print('x', x)
        print('a', a)
        print('hello index')
        time.sleep(1)
        
    index(10)
    

    三层装饰器

    那么可能又有人会问了,如果我想给二层装饰器加上参数该怎么办?虽然不知道是谁会怎么多事,但是Python也考虑到了这一点。

    接下来我们如果给购物车的shopping功能加上登录功能,并判断是‘db’还是‘file’

    def auth(engine):
        def login(func):
            def inner(*args, **kwargs):
                # 登录功能
                if engine == 'file':
                    username = input('usrename:')
                    pwd = input('pwd:')
                    if username == 'nick' and pwd == '123':
                        print('登录成功')
                        res = func(*args, **kwargs)  # shopping()
                        return res
                    else:
                        print('登录失败')
    
                elif engine == 'db':
                    print('账号密码来自于数据库,非法请求')
    
            return inner
        return login
    
    @auth('db')
    def shopping():
        print('shopping')
    

    这样就能将登录装饰给其他函数了

    三层装饰器模板:

    def sanceng(engine):
        def outter(func):
            def wrapper(*args, **kwargs):  # wrapper是未来要运行的函数
                # 加功能
                print(engine)
                res = func(*args, **kwargs)  # func是被装饰的函数
                return res
    
            return wrapper
    
        return outter
    
    
    @sanceng('file')
    def shopping():
        print('shopping')
    

    总之,暂时无法理解装饰器的原理问题并不大,只要记住装饰器的模板,然后能熟练运用就行。还是那句话,没见过猪跑,难道还没吃过猪肉吗。

    多个装饰器装饰一个函数

    有的时候,你可能会碰到一个函数需要装饰多个功能,这个时候就有可能用上多个装饰器。那么,多个装饰器又是怎样进行装饰的呢?

    def outter1(func):  # func = wrapper2
        def wrapper1(*args, **kwargs):  # wrapper是未来要运行的函数
            print('------------')
            res = func(*args, **kwargs)  # func是被装饰的函数  # wrapper2
            print('------------')
            return res
    
        return wrapper1
    
    def outter2(func):  # func = index
        def wrapper2(*args, **kwargs):  # wrapper是未来要运行的函数
            print('11111111111111')
            res = func(*args, **kwargs)  # func是被装饰的函数  # index()
            print('11111111111111')
            return res
    
        return wrapper2
    
    # @outter1  # index = outter1(index)
    # @outter2  # index = outter2(index)  # 先运行最下面的装饰器
    # # index
    def index():
        print('index')
    index() 
    
    结果为:
    ------------
    11111111111111
    index
    11111111111111
    ------------
    

    由此可见,当多个装饰器装饰同一个函数时,运行顺序是从下往上的,并且第二个装饰器装饰的已经不是原来的函数了,而是已经被第一个装饰器装饰过得函数了。

    迭代器

    什么是迭代?迭代就是更新换代,重复,给予上次的结果推出下次的结果

    可迭代对象

    可迭代对象是具有__iter__方法的对象

    我们可以通过判断一个数据类型是否有iter方法来知道他是否是可迭代对象

    x = 1  # 不可迭代对象
    s = 'hyc'  # 可迭代对象
    lt = [1, 2, 3]  # 可迭代对象
    dic = {'a': 1, 'b': 2}  # 可迭代对象
    tup = (1,)  # 元组只有一个元素必须得加逗号# 可迭代对象
    se = {1, 2, 3}  # 可迭代对象
    f = open('time.py')  # 可迭代对象
    
    
    def func():  # 不可迭代对象
        pass
    

    可见除了数字类型和函数之外都是可迭代对象

    同时可迭代对象可以通过 __next__来迭代

    迭代器对象

    具有__iter__和__next__方法的都是迭代器对象

    s = 'hyc'  # 可迭代对象,不属于迭代器对象
    lt = [1, 2, 3]  # 可迭代对象,不属于迭代器对象
    dic = {'a': 1, 'b': 2}  # 可迭代对象,不属于迭代器对象
    tup = (1,)  # 元组只有一个元素必须得加逗号# 可迭代对象,不属于迭代器对象
    se = {1, 2, 3}  # 可迭代对象,不属于迭代器对象
    f = open('time.py')  # 可迭代对象,迭代器对象
    

    可见迭代器对象只有文件

    其实for循环就是一个迭代,我们可以用iter来实现

    lt = [1,2,3]
    lt_iter = lt.__iter__()
    while 1:
        try:
            print(lt_iter.__next__())
        except StopIteration:
            break
    结果为:
    1
    2
    3
    
    1. 首先使用iter把lt变成迭代器对象;对于文件也要使用iter方法吧文件再一次iter下
    2. 然后使用next方法进行迭代取值
    3. 判断StopIteration异常,遇到异常终止

    可以发现,这样就解决了不依赖索引取值的问题

    总的来说,今天的难点依旧是之前的装饰器,但是如果暂时无法解决的话,可以先记个模板,熟练运用。

  • 相关阅读:
    会计基础-资本与资本公积核算
    FORM 基本控件2
    EBS form的一些知识
    EBS功能安全性基本原理
    主物料界面数据来源
    organization --form 表单中organization 数据来源
    form 相关
    jar/war/ear文件的区别
    ORACLE判别字段是否包含中文
    亲测可用:SecureCRT 7 注册码/序列号
  • 原文地址:https://www.cnblogs.com/hyc123/p/11341351.html
Copyright © 2020-2023  润新知