一、Python的迭代协议

  迭代器是访问集合内元素的一种方式。它只能依次访问集合内元素。其特点是惰性执行。

  collection.abc的迭代协议提供了两个概念:可迭代对象和迭代器。可迭代对象:必须具有__item__特殊方法;迭代器:必须具有__next__方法。

class Iterable(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __iter__(self):
while False:
yield None
@classmethod
def __subclasshook__(cls, C):
if cls is Iterable:
return _check_methods(C, "__iter__")
return NotImplemented class Iterator(Iterable):
__slots__ = ()
@abstractmethod
def __next__(self):
'Return the next item from the iterator. When exhausted, raise StopIteration'
raise StopIteration
def __iter__(self):
return self # 重载了Iterable的__iter__
@classmethod
def __subclasshook__(cls, C):
if cls is Iterator:
return _check_methods(C, '__iter__', '__next__')
return NotImplemented Iterator.register(bytes_iterator)
Iterator.register(bytearray_iterator)
#Iterator.register(callable_iterator)
Iterator.register(dict_keyiterator)
Iterator.register(dict_valueiterator)
Iterator.register(dict_itemiterator)
Iterator.register(list_iterator)
Iterator.register(list_reverseiterator)
Iterator.register(range_iterator)
Iterator.register(longrange_iterator)
Iterator.register(set_iterator)
Iterator.register(str_iterator)
Iterator.register(tuple_iterator)
Iterator.register(zip_iterator)

  从上面可以看到,字节、字典、列表、集合、元组、字符串和range、zip都被注册到了迭代器元类中,成为可迭代对象。实际上,open文件句柄也是一个迭代器。

二、可迭代对象与迭代器

  1.可迭代对象

  对象含有__iter__方法,就是可迭代对象。不含__iter__方法,也可能是可迭代对象。__getitem__方法会自动创建__iter__来满足遍历条件。

class Person:
def __init__(self, persons):
self.persons = persons
# def __iter__(self):
# return 1
def __getitem__(self, item): # 假如__iter__没有设定,解释器会继续查找__getitem__
return self.persons[item] # __getitem__会自动创建__iter___来进行迭代 per = Person(["Bob", "Tom", "Li", "Jan"])
print(per[:3])
for p in per:
print(p) """
['Bob', 'Tom', 'Li']
Bob
Tom
Li
Jan
"""

  2.迭代器

  iter()用来将可迭代对象生成迭代器。

class Person:
def __init__(self, persons):
self.persons = persons
def __getitem__(self, item):
return self.persons[item]
return len(self.persons) per = Person(["Bob", "Tom", "Li", "Jan"])
my = iter(per)
for i in range(len(per)):
print(next(my)) """
Bob
Tom
Li
Jan
"""

  3.迭代器的实现

  当需要定义可迭代对象时,一般单独写一个迭代器功能。不建议在可迭代对象内写__next__

class MyIterator:
def __init__(self, persons):
self.persons = persons
self.__index = 0
def __iter__(self):
return self
def __next__(self):
try:
per = self.persons[self.__index]
except IndexError:
raise StopIteration
self.__index += 1
return per class Person:
def __init__(self, persons):
self.persons = persons
def __len__(self):
return len(self.persons) def __iter__(self):
return MyIterator(self.persons) # 当要定义可迭代对象时,通常不在类内写__next__,而是写在外面 per = Person(["Bob", "Tom", "Li", "Jan"])
my = iter(per)
for i in range(len(per)):
print(next(my))

三、生成器

  当函数中存在yield时,它就不再是普通的函数了,而是一个生成器函数。

def f1():
print("-----------step1----------")
yield 1
print("-----------step2----------")
yield 2 g = f1() # 这一步没执行
print(next(g))
print(next(g))

  生成器的实现,本质上是对函数的堆存储方式进行了一层封装。

  普通函数的调用和执行过程。被调用函数独在字节码这一步单独存储于PyCodeObject中。它必须一次性运行完,而不会保留执行时的栈帧。

    

import inspect
frame = None
def foo():
bar()
def bar():
global frame
frame = inspect.currentframe() foo()
print(dis.dis(foo))
print(frame.f_code.co_name)
print(frame.f_back.f_code.co_name, frame.f_back) # 所有的栈帧都是分配在对内存当中,独立于调用者存在

PyCodeObject

   生成器对象(PyGenObject)会被编译成相应的字节码,以及每一次yield的栈帧和本地变量。相比于普通函数,PyGenObject多了对f_lasti和f_locals的保存。只要每次yield拿到f_lasti和gen_fn's bytecode,就可以暂停或继续执行生成器。

    

import dis
def f1():
print("-----------step1----------")
name = "Li"
yield 1
print("-----------step2----------")
age = 24
yield 2 g = f1() # 这一步没执行
print(g.gi_code) # 显示代码的起止位置
print(dis.dis(g)) # 显示栈帧与字节码
print(g.gi_frame.f_lasti, g.gi_frame.f_locals) # 显示代码执行至此的栈帧和本地变量集
print(next(g))
print(g.gi_frame.f_lasti, g.gi_frame.f_locals)
print(next(g))
print(g.gi_frame.f_lasti, g.gi_frame.f_locals) """
<code object f1 at 0x110b71f60, file "<ipython-input-261-13e8f3cdc8da>", line 2>
3 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('-----------step1----------')
4 CALL_FUNCTION 1
6 POP_TOP 4 8 LOAD_CONST 2 ('Li')
10 STORE_FAST 0 (name) 5 12 LOAD_CONST 3 (1)
14 YIELD_VALUE
16 POP_TOP 6 18 LOAD_GLOBAL 0 (print)
20 LOAD_CONST 4 ('-----------step2----------')
22 CALL_FUNCTION 1
24 POP_TOP 7 26 LOAD_CONST 5 (24)
28 STORE_FAST 1 (age) 8 30 LOAD_CONST 6 (2)
32 YIELD_VALUE
34 POP_TOP
36 LOAD_CONST 0 (None)
38 RETURN_VALUE
None
-1 {}
-----------step1----------
1
None
14 {'name': 'Li'}
-----------step2----------
2
32 {'name': 'Li', 'age': 24}
"""

PyGenObject

  待续。。。。。。

python(九):迭代器与生成器的更多相关文章

  1. python(九)迭代器和生成器

    一.迭代 迭代就是逐个并且单向访问容器 (这里的容器暂时指数据类型,比如list和dict) 中的元素的行为.举个例子:将一个长度为五的数组逐个从头到尾(即单向)打印的方式称之为迭代.如下图. > ...

  2. python基础—迭代器、生成器

    python基础-迭代器.生成器 1 迭代器定义 迭代的意思是重复做一些事很多次,就像在循环中做的那样. 只要该对象可以实现__iter__方法,就可以进行迭代. 迭代对象调用__iter__方法会返 ...

  3. python之迭代器与生成器

    python之迭代器与生成器 可迭代 假如现在有一个列表,有一个int类型的12345.我们循环输出. list=[1,2,3,4,5] for i in list: print(i) for i i ...

  4. Python之迭代器和生成器

    Python 迭代器和生成器 迭代器 Python中的迭代器为类序列对象(sequence-like objects)提供了一个类序列的接口,迭代器不仅可以对序列对象(string.list.tupl ...

  5. 【Python】迭代器、生成器、yield单线程异步并发实现详解

    转自http://blog.itpub.net/29018063/viewspace-2079767 大家在学习python开发时可能经常对迭代器.生成器.yield关键字用法有所疑惑,在这篇文章将从 ...

  6. python的迭代器、生成器、装饰器

    迭代器.生成器.装饰器 在这个实验里我们学习迭代器.生成器.装饰器有关知识. 知识点 迭代器 生成器 生成器表达式 闭包 装饰器 实验步骤 1. 迭代器 Python 迭代器(Iterators)对象 ...

  7. Python之迭代器,生成器

    迭代器 1.什么是可迭代对象 字符串.列表.元组.字典.集合都可以被for循环,说明他们都是可迭代的. from collections import Iterable l = [1,2,3,4] t ...

  8. python之迭代器、生成器与面向过程编程

    目录 一 迭代器 二 生成器 三 面向过程编程 一.迭代器 1.迭代器的概念理解 ''' 迭代器从字面上理解就是迭代的工具.而迭代是每次的开始都是基于上一次的结果,不是周而复始的,而是不断发展的. ' ...

  9. day13 python学习 迭代器,生成器

    1.可迭代:当我们打印 print(dir([1,2]))   在出现的结果中可以看到包含 '__iter__', 这个方法,#次协议叫做可迭代协议 包含'__iter__'方法的函数就是可迭代函数 ...

  10. Python之迭代器及生成器

    一. 迭代器 1.1 什么是可迭代对象 字符串.列表.元组.字典.集合 都可以被for循环,说明他们都是可迭代的. 我们怎么来证明这一点呢? from collections import Itera ...

随机推荐

  1. java中集合

    一. List集合:  一次只存储一个元素 1.常用的list集合是ArrayList (1)在创建这个集合的对象时, 需要指定这个集合存储的数据类型! 否则这个集合的数据是不安全的. (2)与数组的 ...

  2. 洛谷P4586 [FJOI2015]最小覆盖双圆问题(最小圆覆盖)

    题面 传送门 前置芝士 最小圆覆盖 题解 我们按照\(x\)坐标排序,然后二分中间点,把点分成左右两边,对两边都做一个最小圆覆盖,那么半径大一点的那个就是答案了.然后对半径大的那一边继续二分就行了 然 ...

  3. 工作中遇到的两个问题-正则以及console

    一.今天做点击按钮验证邮箱时,遇到以下几个问题: (1)点击按钮后,执行if(regExp.test(str)),出现一种奇怪的现象:第一次输入正确邮箱验证通过,第二次输入正确邮箱就返回false,第 ...

  4. 【Node】node.js实现服务器的反向代理,解决跨域问题

    跨域对于前端来说是一个老大难的问题,许多方法如jsonp.document.domain + iframe...都有或多或少的问题,一个最佳实践就是通过服务器nginx做反向代理,但奈何不懂相关知识, ...

  5. CentOS6.5下samba服务

    为减少错误已提前关掉了SELinux,防火墙. 安装rpm包: samba-3.6.9-164.el6.x86_64.rpm 启动检测:samba服务可以正常启动!(证明RPM安装正常) 配置文件位置 ...

  6. Xcode 编译更改 Build 输出路径

    Xcode新建一个工程,build之后,可执行文件一般在 ~/Library/Developer/Xcode/DerivedData 下. 可以把这个路径指定为当前工程目录.  指定方法 Xcode ...

  7. leetcode-479-Largest Palindrome Product(找到两个乘数相乘得到的最大的回文数)

    题目描述: Find the largest palindrome made from the product of two n-digit numbers. Since the result cou ...

  8. shell ssh远程执行命令

    [root@backup shell]# vi backup.sh #!/bin/sh ipAddress=172.17.167.38 ssh -tt root@$ipAddress -p 22 &l ...

  9. 能够放在文档的 <head> 中的各种配置元素

    一份关于任何可以写入到你的文档中 <head> 部分的清单 最小推荐 <meta charset="utf-8"> <meta http-equiv= ...

  10. ContentProvider和Cursor以及CursorAdapter三者之间内部链接实现原理 解析

    最近 在学习Android3.0中推出的 Loader 机制,其中CursorLoader 这个加载器说是可以实时监测数据和更新数据,为了一探究竟,就连带的将 ContentProvider和Curs ...