python迭代器及一点点yield

python 里的可迭代对象都有以下方法

    def __iter__(self, *args, **kwargs): # real signature unknown
        """ Implement iter(self). """
        pass

实现了iter(self) 函数
接下来查看iter() 函数

def iter(source, sentinel=None): # known special case of iter
    """
    iter(iterable) -> iterator
    iter(callable, sentinel) -> iterator
    
    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.
    """
    pass

iter() 函数可以有一个或两个参数
有一个参数时,iter(iterable) -> iterator ,Get an iterator from an object(获取对象的迭代器),In the first form, the argument must
supply its own iterator, or be a sequence.(在第一种形式中,即只有一个参数的时候,该对象自身需要提供迭代器,也就是有自己的iter函数,或者该对象是一个队列)
python 的基本数据类型中,有iter函数的有字符串(str),列表(list),元组(tuple),集合(set),字典(dict)

接下来去看看官方文档对iter()函数的解释
python各种版本的文档
点击search page 可以搜索自己需要的内容
3.6.4版本的iter()函数直通车
iter(object[, sentinel])方法在给定第二个参数的时候,则object必须是可调用的对象,iter()方法创建了一个迭代器,每次调用这个迭代器的__next()__方法的时候,都会调用object参数,如果next()方法返回值等于sentinel,将会引发StopIteration,不等于则返回方法返回值.
这里需要注意,python里有个函数callable(object) 可以判断一个对象是否是可调用的,如果对象显示可调用,则返回True,否则返回False,返回True的话,调用有可能失败,如果返回False的话,永远不可能被调用.类是可以被调用的,调用一个类返回一个新实例对象,如果实例对象所属的类有个__call()__方法的话,该实例对象也是可以被调用的.
callable() 这个函数在Python 3.0被删除,后来在3.2版本里又回归了.

#例子一
class A:
    def __init__(self):
        self.list1 = [1, 2, 3, 4, 5, 6, 7]
        print('init()被调用')

    def __call__(self):
        print('call()被调用')
        return next(self)

    def __iter__(self):
        print('iter()被调用')
        self.i = 0
        return self.list1

    def __next__(self):
        if self.i < len(self.list1):
            print('next()方法调用')
            next_value = self.list1[self.i]
            self.i += 1
            return next_value
        else:
            raise StopIteration


if __name__ == '__main__':
    print(callable(A()))  # init()被调用  True
    a = A()
    print(callable(a))  # init()被调用 True
    i = iter(a, 5)
    print(type(i))  # <class 'callable_iterator'>
    for x in i:
        print(x)
'''
call()被调用
next()方法调用
1
call()被调用
next()方法调用
2
call()被调用
next()方法调用
3
call()被调用
next()方法调用
4
call()被调用
next()方法调用
'''
#例子二
i = 0
def getNum():
    global i
    i += 1
    return i

print(callable(getNum))

a = iter(getNum, 5)

for x in a:
    print(x)

'''
True
1
2
3
4
'''

yield
yield表达式在定义生成器函数或异步生成器函数时使用,因此只能在函数定义的主体中使用,在函数体中使用yield会使该函数称为生成器函数.
调用生成器函数时,将返回一个名为生成器的迭代器,该生成器控制着生成器函数的执行,当调用生成器的方法的时候,开始执行生成器函数.执行到第一个yield表达式的时候,该生成器函数被挂起,然后将expression_list格式的值返回给生成器的调用方,
expression_list ::= expression ( “,” expression )* [","]
这里挂起的意思是,保留所有的局部状态,包括局部变量的当前绑定值,指令指针,内部堆栈和任何异常处理的状态.当调用生成器的方法时,恢复执行状态,类似于生成器函数被重新调用一样,只不过是从上次挂起的地方开始.之后的yield表达式的值取决于恢复执行状态时被调用的生成器的方法.
如下例子debug分析具体过程:

# -*- coding:utf-8 -*-

def getNum(n):
    a = 1
    while True:
        if a > n:
            return
        yield a
        print(a, a * 2, a * 3)
        a += 1

if __name__ == '__main__':
    num = getNum(3)
    print(type(num))


    while True:
        try:
            next(num)
        except:
            exit()
  1. 在num=getNum(3) 和 next(num) 处打断点,debug运行程序
  2. getNum()返回一个num,类型为generator
  3. 当程序运行到next()的时候,getNum()开始执行,执行到yield的时候不往下继续,此时a=1,next()执行完
  4. 然后开始第二次执行next(),跳到getNum()函数,继续执行yield之后的代码,print(a, a * 2, a * 3),[结果是 1,2,3]然后a变成2,继续循环,判断if代码,第二次执行到yield,然后不往下继续,next()执行完
  5. 然后继续执行next()所在的循环,第三次执行next(),然后跳转到getNum()函数,执行print(a, a * 2, a * 3),[结果是2 4 6],a自增1变成3,继续执行getNum()函数的循环,if判断,yield a,不往下继续执行,跳到next()执行完
  6. 执行next()所在循环,第四次执行next(),跳转到getNum(),继续执行,print(a, a * 2, a * 3),[结果是 3 6 9],a自增1变成4,if判断通过getNum()函数return,跳到next(),程序捕获异常,退出

后续的协程,yield from 在之后多线程的地方研究.

猜你喜欢

转载自blog.csdn.net/zZsSzss/article/details/102976370