• 11. 函数-三大器


    一、迭代器

    1. 定义

    ​ 对于list、string、tuple、dict等这些容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数。iter()是python内置函数。iter()函数会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内的元素。next()也是python内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。
    ​ 迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的_next_方法(Python3中是对象的_next_方法,Python2中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的_next_方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现_iter_方法,而_iter_方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的_iter_方法返回自身self即可

    2. 一些术语

    • 迭代器协议:对象需要提供next()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。
    • 可迭代对象:实现了迭代器协议对象。list、tuple、dict都是Iterable(可迭代对象),但不是Iterator(迭代器对象)。但可以使用内建函数iter() ,把这些都变成Iterable(可迭代器对象)。
    • for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束
    # 随便定义一个list
    listArray=[1,2,3]
    # 使用iter()函数
    iterName=iter(listArray)
    print(iterName)
    结果如下:是一个列表list的迭代器
    <list_iterator object at 0x0000017B0D984278>
     
    print(next(iterName))
    print(next(iterName))
    print(next(iterName))
    # 没有迭代到下一个元素,直接抛出异常
    print(next(iterName))
    # 1
    # 2
    # 3
    # Traceback (most recent call last):
    # File "Test07.py", line 32, in <module>
    # StopIteration
    
    # 实现斐波那契数列
    
    
    class Fib(object):
    	def __init__(self, max):
    		super(Fib, self).__init__()
    		self.max = max
     
    	def __iter__(self):
      		self.a = 0
      		self.b = 1
      		return self
     
    	def __next__(self):
    		fib = self.a
      		if fib > self.max:
       		raise StopIteration
      		self.a, self.b = self.b, self.a + self.b
      		return fib
     
    # 定义一个main函数,循环遍历每一个菲波那切数
    def main():
    	# 20以内的数
     	fib = Fib(20)
     	for i in fib:
      		print(i)
    
    # 测试
    if __name__ == '__main__':
    	main()
    

    3. 可迭代对象

    ​ list,tuple,set,dict,str等可以直接作用于for循环的对象,称为可迭代对象。可迭代对象实现了__iter__方法,用于返回迭代器。

    demo = [1,2,3,4]
    print(isinstance(demo, Iterable)) //True
    iter_object = iter(demo)
    print(iter_object) //<list_iterator object at 0x00000258DC5EF748>
    

    4. 实现一个for循环

    s = 'hello'
    it = iter(s)
    while (True):
    	try:
    		print(next(it))
    	except StopIteration:
    		del it
        	break
    

    ​ StopIteration异常将在迭代器耗尽后被抛出,for-in、生成式(comprehension)、元组解压(tuple unpacking)等迭代操作都会处理并这个异常。

    ​ 迭代器是个迭代值生产工厂,它保存迭代状态,并通过next()函数产生下一个迭代值。实现迭代器需要实现以下两个方法:__iter__返回self ;__next__返回下一个可用的元素,如果无可用元素则抛出StopIteration异常

    迭代器实现__iter__,因此所有的迭代器都是可迭代的,下图展示了iterable和iterator的结构。

    二、生成器

    1. 定义

    ​ 通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

    ​ 所以,如果列表元素可以按照某种算法推算出来,这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

    要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator。

    ​ 作用:延迟操作。也就是在需要的时候才产生结果,不是立即产生结果。

    2. 示例

    (1) 第一类

    ​ 第一类:生成器函数:还是使用 def 定义函数,但是,使用yield而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行

    # 菲波那切数列
    def Fib(max):
    	n, a, b = 0, 0, 1
    	while n < max:
    		yield b
      		a, b = b, a + b
      		n = n + 1
     		return '亲!没有数据了...'
        
    # 调用方法,生成出10个数来
    f=Fib(10)
    # 使用一个循环捕获最后return 返回的值,保存在异常StopIteration的value中
    while True:
    	try:
    		x=next(f)
      		print("f:",x)
     	except StopIteration as e:
      		print("生成器最后的返回值是:",e.value)
      		break
    
    (2) 第二类

    ​ 生成器表达式:类似于列表推导,只不过是把一对大括号[]变换为一对小括号()。但是,生成器表达式是按需产生一个生成器结果对象,要想拿到每一个元素,就需要循环遍历。

    # 一个列表
    xiaoke=[2,3,4,5]
    # 生成器generator,类似于list,但是是把[]改为()
    gen=(a for a in xiaoke)
    for i in gen:
    	print(i)
    
    结果是:
    2
    3
    4
    5
    

    3. send()和__next__()

    # send和__next__()⼀样都可以让⽣成器执⾏到下⼀个yield. 
    
    def eat():
        print("我吃什么啊")
        a = yield "馒头"
        print("a=",a)
        b = yield "烧饼"
        print("b=",b)
        c = yield "菜盒子"
        print("c=",c)
        yield "GAME OVER"
    gen = eat() # 获取生成器
    ret1 = gen.__next__()
    print(ret1)
    ret2 = gen.send("胡辣汤") # 给上一个yield传值,也就是a
    print(ret2)
    ret3 = gen.send("狗粮")
    print(ret3)
    ret4 = gen.send("猫粮")
    print(ret4)
    
    结果:
    我吃什么啊
    馒头
    a= 胡辣汤
    烧饼
    b= 狗粮
    菜盒子
    c= 猫粮
    GAME OVER
    
    • send和next()都是让⽣成器向下走⼀次
    • send可以给上⼀个yield的位置传递值, 不能给最后⼀个yield发送值. 在第⼀次执⾏⽣成器代码的时候不能使⽤send()

    4. yield和return

    ​ yield是分段来执⾏⼀个 函数. return呢? 直接停⽌执⾏函数。种使⽤⽣成器. ⼀次就⼀个. ⽤多少⽣成多少. ⽣成器是⼀个⼀个的指向下⼀个. 不会回去, __next__()到哪, 指针就指到哪⼉. 下⼀次继续获取指针指向的值.

    5. 生成器迭代器

    三、装饰器

    1. 定义

    ​ 装饰器(Decorator)从字面上理解,就是装饰对象的器件。可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出。装饰器有很多种,有函数装饰器、类装饰器。它强调开放封闭原则,装饰器语法是将@装饰名放在被装饰对象上面。简单来说,可以把装饰器理解为一个包装函数的函数,它一般将传入的函数或者是类做一定的处理,返回修改之后的对象.所以,我们能够在不修改原函数的基础上,在执行原函数前后执行别的代码

    ​ 装饰器的本质是一个python函数,其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值(return)也是一个函数对象。比如:插入日志、性能测试、事务处理、缓存、权限校验。比如说,有需要功能模块需要添加认证功能;那这时就可以使用装饰器实现。

    2. 定义简单装饰器

    (1) 代码示例
    def outer(func):
        def inner():
            print('认证成功!')
            result = func()
            print('日志添加成功!')
            return result
        return inner
    
    @outer
    def f1():
        print('业务部门 1 数据接口...')
    
    结果:
    认证成功!
    业务部门1数据接口......
    日志添加成功
    
    (2) 代码分析

    ①程序开始运行,从上往下解释,读到 def outer(func):的时候,发现是一个“一等公民”函数,于是把函数体加载到内存里,然后过

    ②读到@outer时,程序被@语法糖吸引住了,知道这是个装饰器,按规矩要立即执行的,于是程序开始运行@后面那个名字outer所定义的函数

    ③程序返回到outer函数,开始执行装饰器的语法规则。规则是:被装饰的函数的名字会被当做参数传递给装饰函数。装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰的函数。原来的f1函数被当做参数传递给了func,而f1这个函数名之后会指向inner函数。

    ④注意:@outer和@outer()有区别,没有括号时,outer函数依然会被执行,这和传统的用括号才能调用函数不同,需要特别注意!另外,是f1这个函数名(而不是f1()这样被调用后)当做参数传递给装饰函数outer,也就是:func = f1,@outer等于outer(f1),实际上传递了f1的函数体,而不是执行f1后的返回值。还有,outer函数return的是inner这个函数名,而不是inner()这样被调用后的返回值。

    ⑤.程序开始执行outer函数内部的内容,一开始它又碰到了一个函数inner,inner函数定义块被程序观察到后不会立刻执行,而是读入内存中(这是默认规则)

    ⑥再往下,碰到return inner,返回值是个函数名,并且这个函数名会被赋值给f1这个被装饰的函数,也就是f1 = inner。根据前面的知识,我们知道,此时f1函数被新的函数inner覆盖了(实际上是f1这个函数名更改成指向inner这个函数名指向的函数体内存地址,f1不再指向它原来的函数体的内存地址),再往后调用f1的时候将执行inner函数内的代码,而不是先前的函数体。那么先前的函数体去哪了?还记得我们将f1当做参数传递给func这个形参么?func这个变量保存了老的函数在内存中的地址,通过它就可以执行老的函数体,你能在inner函数里看到result = func()这句代码,它就是这么干的!

    3、带有参数的装饰器

    def outer(func):
        def inner(*args,**kwargs):
            print("认证成功!")
            result = func(*args,**kwargs)
            print("日志添加成功")
            return result
        return inner
    
    @outer
    def f1(name,age):
        print("%s 正在连接业务部门1数据接口......"%name)
    
    # 调用方法
    f1("jack",18)
    

    4、一个函数被多个装饰器修饰

    def outer1(func):
        def inner(*args,**kwargs):
            print("认证成功!")
            result = func(*args,**kwargs)
            print("日志添加成功")
            return result
        return inner
    
    
    def outer2(func):
        def inner(*args,**kwargs):
            print("一条欢迎信息。。。")
            result = func(*args,**kwargs)
            print("一条欢送信息。。。")
            return result
        return inner
    
    
    @outer1
    @outer2
    def f1(name,age):
        print("%s 正在连接业务部门1数据接口......"%name)
    
    # 调用方法
    f1("jack",18)
    
    执行结果:
    认证成功!
    一条欢迎信息。。。
    jack 正在连接业务部门1数据接口......
    一条欢送信息。。。
    日志添加成功
    

    5. 装饰器修复功能

    #  可获取func1自己的函数名,即帮助文档
    from functools import wraps
    def timer(func):
        @wraps(func)
        def inner():
            print(time.time())
            ret = func()  # 原来的函数
            return ret
        return inner
    
    @timer  # func1 = timer(func1)
    def func1():
        """
        func1 xxxx
        :return:
        """
        print('func1')
        return 'func1的返回值'
    print(func1.__name__)
    
  • 相关阅读:
    selenium基础(元素定位)
    python函数基础(函数的定义和调用)
    python语句结构(控制语句与pass语句)
    python语句结构(range函数)
    while语句结构(for循环)
    python语句结构(while循环)
    python语句结构(if判断语句)
    python基础语法(运算符及优先级)
    python基础语法(变量与数据类型)
    requests上传文件,又要上传data的处理
  • 原文地址:https://www.cnblogs.com/hq82/p/11745579.html
Copyright © 2020-2023  润新知