Python学习 day13
一、可迭代对象和迭代器
1、回顾可以被for循环的对象
list、dic、str、set、tuple、文件句柄f、range()、enumerate()
只有可迭代对象才能被for循环,当我们遇到一个新的变量,不确定能不能for循环时就判断它是否可迭代,那如何判断对象可迭代?
2、双下方法
dir() -- 打印一个对象的所有方法
打印一个列表的所有方法:
print(dir([]))
结果:
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
可以看到,前面的方法前后都带有两条下划线,这就是我们说的双下方法
双下方法使用C语言写好的,可以有不同调用方式调用的方法,在实际使用中,我们一般不会直接调用双下方法,而是采用其他的调用方式。
如上面列表中的__add__方法:
print([1, 3] + [1, 2]) # 列表可以直接相加
print([1, 3].__add__([1, 2])) # 调用__add__方法
结果:
从这个例子可以理解,在实际使用时我们会使用“+”来对两个列表相加,但实际“+”机器是不认识的,在调用时实际上还是调用了__add__方法,根据方法中的步骤执行操作,包括数字的加减也是一样。
3、可迭代对象的秘密
利用dir我们可以得到所有可迭代对象的所有方法,为了找到可迭代的秘密,我们对可迭代对象的方法求交集:
print(set(dir([])) & set(dir(())) & set(dir({})) & set(dir(set([])))) # dir 获取的结果是集合,转化为set可求交集
结果:
{'__format__', '__doc__', '__ne__', '__setattr__', '__getattribute__', '__contains__', '__ge__', '__str__', '__init__', '__dir__', '__sizeof__', '__delattr__', '__reduce__', '__gt__', '__iter__', '__lt__', '__subclasshook__', '__repr__', '__len__', '__hash__', '__reduce_ex__', '__new__', '__le__', '__eq__', '__class__'}
“可迭代的”英文即“iterable”,在上面的方法中可以找到与这个词相近的方法__iter__方法,这个方法就是可迭代对象的秘密了
检验:
print('__iter__' in dir(range(10)))
print('__iter__' in dir(enumerate([])))
print('__iter__' in dir(int))
print('__iter__' in dir(bool))
结果:
结论:只要是能被for循环的,即可迭代的,就一定拥有__iter__方法
4、迭代器
上面我们已经知道了__iter__方法,那执行__iter__方法后有什么结果?
print([].__iter__())
结果:
iterator即“迭代器”的意思,事实上,在调用__iter__方法后,该方法会返回一个迭代器。
再次使用set来计算迭代器与可迭代对象所拥有的方法差异:
print(set(dir([].__iter__())) - set(dir([]))) # 利用set求差集
结果:
{'__setstate__', '__next__', '__length_hint__'}
其中“__next__”就是我们要找的方法了,调用__next__将获取到迭代器的下一个元素
使用__length_hint__查看迭代器的元素个数
print(['momo', 2, 5, [1, 2, 3]].__iter__().__length_hint__())
结果:
4
判断迭代器的方法中是否有__iter__方法:
print('__iter__' in dir([].__iter__()))
结果:
True
所以迭代器也是可迭代对象,可以被for循环
5、可迭代协议和迭代器协议
可迭代协议 -- 只要含有__iter__方法的都是可迭代的(即可被for循环的)
迭代器协议 -- 内部含有__next__和__iter__方法的就是迭代器
可迭代的对象.__iter__()方法就可以得到一个迭代器
迭代器也是可迭代对象,迭代器调用__iter__()方法就返回它本身
迭代器中的__next__()方法可以一个一个地获取值
判断是否是可迭代对象或迭代器:
from collections import Iterable
from collections import Iterator print(isinstance([], Iterable))
print(isinstance([], Iterator))
而且,根据上述两个协议,我们可以自定义对象来进行测试:
from collections import Iterable
from collections import Iterator class A:
def __iter__(self): pass # 可以注释掉再测试 def __next__(self): pass # 可以注释掉再测试 a = A()
print(isinstance(a, Iterable))
print(isinstance(a, Iterator))
6、迭代器取值
- 使用__next__()/next()方法取值
iterator = [1, 4, 'python', [1, 2, 3], {'iter': 'iterator'}].__iter__()
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
print(next(iterator))
print(next(iterator))
结果:
迭代器的元素个数是固定的,最后一个值取完后如果再调用__next__就会报错:
Traceback (most recent call last):
File "F:/Users/alice/PycharmProjects/exercise/test/test18.py", line 30, in <module>
print(iterator.__next__())
StopIteration
next(iterator, default=None) -- 内置函数next()调用的应该就是__next__方法,看源代码注释:
"""
next(iterator[, default]) Return the next item from the iterator. If default is given and the iterator
is exhausted, it is returned instead of raising StopIteration.
"""
调用next()方法更好的一点是,如果default传了值,在迭代器耗尽后再取值不会抛异常,而是返回default的值。另外,就是之前说的,不要直接使用双下方法。
- 使用for循环取值
iterator = [1, 4, 'python', [1, 2, 3], {'iter': 'iterator'}].__iter__()
for i in iterator:
print(i)
结果:
for循环使用的是for关键字而不是调用某一方法,这里就可以想到,其实for循环实际是调用了某个双下方法。
可以推测,有__iter__方法的才能被for循环,所以for循环时首先调用__iter__方法,拿到迭代器,然后每次循环调用一次__next__方法,因为for循环是自动结束的,所以结束的标志自然是__next__报错Stop Iteration。验证可以自己写个类验证,添加__iter__和__next__方法,已验证。
- next和for
可迭代对象每调用一次__iter__方法都可以拿到一个新的迭代器
同一个迭代器从头到尾只能取一次,不循环
lst_iter = [1, 2, 3, 4, 5].__iter__()
print(next(lst_iter, None))
print(next(lst_iter, None))
for i in lst_iter:
print(i)
结果:
可以看到,不管用next还是for循环取值,迭代器中的值始终是顺序取值,一个迭代器每个元素只能取一次。
- 数据类型的强制转换
除了上述的常用的取值方法外,数据类型强制转换也是生成器的取值方法,如 lst = list(range(10)) ,但是这种取值方式一般不要用,因为转换相当于把所有值一次性取出,如果生成器内容很多,很容易造成内存占用过多。
7、迭代器的应用
目前使用迭代器的情况还比较少,之后在开发过程中,当调用别人写的函数拿到返回值时,可能存在返回值是迭代器的情况,这时需要合理判断,确定其是否为可迭代对象或迭代器。主要判断方法:
1、Iterator判断,直接打印,是Iterator对象的就是迭代器
2、可迭代对象,判断其中是否有__iter__方法,有就是可迭代对象
3、直接给内存地址,有时打印别人的函数的返回值可能输出是一个内存地址,这是可以判断其可能是迭代器
8、迭代器的特点
1、方便从容器类型中一个一个地取值,会把所有的值都取到
2、节省内存空间,如rang(),文件句柄
3、惰性运算,不要不给
二、生成器
写生成器有两种方式:
1、生成器函数
2、生成器表达式
1、生成器函数
先看示例:
def generator():
print("第一个值")
yield "a"
print("第二个值")
yield "b" g = generator()
print("generator运行了吗")
ret = next(g) # 调用__next__()也是可以的
print(ret)
ret = next(g)
print(ret)
结果:
上面generator函数就是一个生成器函数,与普通函数不同的是,生成器函数中没有return,而是有yield
第8行执行generator函数后,可以看到实际上函数并没有执行,而是在第9行打印后,函数内代码才开始执行
所以生成器函数的执行过程是在第一次调用时会返回一个生成器,即g,生成器可以调用next方法取值,每次取值生成器函数向下执行,遇到yield停止,将yield后面的对象返回,即得到一个next取的值。
所以,生成器本质上就是一个迭代器。
生成器函数:只要含有yield关键字的函数就是生成器函数
yield不能和return共用,且需要写在函数内
2、监听文件输入的例子
有这样一个需求:写一个生成器,监听某一文件是否输入了新的内容,每输入一行就返回一行。
def tail(file_path):
with open(file_path, encoding="utf-8") as f:
while True:
line = f.readline().strip() # 这里会过滤掉空行,如果不加strip()后面再输入的都会多一个空行,所以这里不能把完整的原文档显示,也是个小bug
if line:
yield line
# for line in f:
# line = line.strip()
# if line:
# yield line g = tail("../homework/employeeInfo")
for i in g:
print(i)
Python学习 day13的更多相关文章
- python学习Day13 函数的嵌套定义、global、nonlocal关键字、闭包及闭包的运用场景、装饰器
复习 1.函数对象:函数名 => 存放的是函数的内存地址1)函数名 - 找到的是函数的内存地址2)函数名() - 调用函数 => 函数的返回值 eg:fn()() => fn的返回值 ...
- python学习 day13 迭代器,生成器,枚举对象
一.复习 1.闭包:定义在函数内部的函数(被函数嵌套的函数) 2.装饰器:闭包的一个应用场景 -- 为一个函数添加新功能的工具 3.开放封闭原则:不能修改源代码,不能修改调用方式,但可以对外提供增加新 ...
- Python学习-day13 SqlAlchemy
本节内容 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业 1. ORM介绍 orm英文全称object relational mapping ...
- python学习day13
目录 JavaScript Dom jQuery JavaScript JavaScript 是世界上最流行的编程语言. 这门语言可用于 HTML 和 web,更可广泛用于服务器.PC.笔记本电脑.平 ...
- python学习 day13 装饰器(一)&推导式
装饰器&推导式 传参位置参数在前,关键词参数在后 函数不被调用内部代码不被执行 函数在被调用的时候,每次都会开辟一个新的内存地址,互不干扰 #经典案例 def func(num): def i ...
- python学习博客地址集合。。。
python学习博客地址集合... 老师讲课博客目录 http://www.bootcdn.cn/bootstrap/ bootstrap cdn在线地址 http://www.cnblogs. ...
- 【目录】Python学习笔记
目录:Python学习笔记 目标:坚持每天学习,每周一篇博文 1. Python学习笔记 - day1 - 概述及安装 2.Python学习笔记 - day2 - PyCharm的基本使用 3.Pyt ...
- python学习之旅
python学习分类 python基础 +- day01——python初始.变量.常量.注释.基础数据类型.输入.if day02——while.字符串格式化.运算符.编码初识 day03—— ...
- Python学习--04条件控制与循环结构
Python学习--04条件控制与循环结构 条件控制 在Python程序中,用if语句实现条件控制. 语法格式: if <条件判断1>: <执行1> elif <条件判断 ...
随机推荐
- python学习的一点点心得
好久没发博客了,不解释....接下来写一点自己最近学习python的一点心得. 想要学习python的初衷,是看<软件测试技术大全>一书时,了解到像perl.python.ruby等脚本类 ...
- aspnetcore的那些actionresult们
比MVC5多了n个actionresult,傻傻分不清,整理了下,妈妈再也不用担心了 https://docs.asp.net/projects/api/en/latest/autoapi/Micro ...
- scalaWindows和Linux搭建
Windows搭建 https://www.cnblogs.com/freeweb/p/5623372.html Linux搭建 https://www.cnblogs.com/freeweb/p/5 ...
- 异步IO类
也学习多线程一段时间了,也写了几个简单实用的功能类,也意思到细节的处理的重要性,现在就让我们来写一个稍稍更有用的异步IO的类. 本来想参考Java NIO 中的类,Java NIO作为新io包,本身提 ...
- Python 数据分析—第九章 数据聚合与分组运算
打算从后往前来做笔记 第九章 数据聚合与分组运算 分组 #生成数据,五行四列 df = pd.DataFrame({'key1':['a','a','b','b','a'], 'key2':['one ...
- Partition--分区总结
1. 在SQL SERVER 2008 R2 SP2之前版本,对分区只支持到1000个分区,之后版本支持到15000个分区.2. 分区索引对齐并不要求索引和表使用同一分区方案,但要求两者使用的分区方案 ...
- Android ActionBar使用方法
对于这ActionBar我想很多人都想了解一下到底是怎么一个使用方法,以及它都存在哪些可操作的和使用的地方.如下图所示:<ignore_js_op> 这便是ActionBar的基本内容.获 ...
- RobotFramework与Jenkins集成后构建成功率高于设置阈值但总是显示失败
摘要:robot执行后总是失败 1.在配置jenkins的job时,添加构建步骤Execute Windows batch command,输入执行robotframework测试用例命令 2.然后j ...
- 单例模式(Singleton)小记
概念 引用维基百科对单例的说明: 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在. 继续引用维基百科的实现思路: 实现单例模式的思路是:一个类 ...
- 反省在北京某S2B2C电商小型公司面试时掉链子的问题
昨天,参与北京一家公司面试时,不知道为什么,错了很多题,这些题在该家公司之前已经被问很多次了,当天精神恍惚的没答上来或答错,被问到数据库优化和乐观锁的问题,首先我谈到了存储引擎底层的数据结构 B树/B ...