hash Hibernate centos7 bash azure vue实例 河南网络推广 sql server 视频教程 jquery第一个子元素 js获取焦点事件 android富文本框架 matlab向量的模 mysql卸载重装 python转java python实例 python语言入门 java基础 java接口实现 java正则表达式详解 java编程语言 java重命名 php实例教程 ntscan 按钮制作 rendercontrol acmecadconverter 谷歌地球打不开 电子书制作软件 flash制作工具 cdr字体加粗 mp4剪切合并大师 0x000007a 现代操作系统 剑灵邪魔铃怎么获得 文字图片制作 grep正则表达式 文件批量更名 qq流览器下载 ps蒙版抠图 js回调函数
当前位置: 首页 > 学习教程  > python

Python基础与拾遗8:Python中的迭代器与解析

2021/2/7 11:06:12 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

Python基础与拾遗8:Python中的迭代器与解析迭代器与解析的由来文件迭代器Python 3.0及之后版本中的手动迭代:iter和next列表解析生成器函数多个迭代器其它迭代环境本篇博文,主要总结Python中的数据类型与注意事项。在Python中,迭代…

Python基础与拾遗8:Python中的迭代器与解析

  • 迭代器与解析的由来
  • 文件迭代器
  • Python 3.0及之后版本中的手动迭代:iter和next
  • 列表解析
  • 生成器函数
  • 多个迭代器
  • 其它迭代环境

本篇博文,主要总结Python中的数据类型与注意事项。在Python中,迭代器与解析是 Python高级程序员常用的技巧与手段。迭代器与解析充分体现了Python语言与C语言,Java语言,MATLAB等常用语言的区别,下面开始干货。

迭代器与解析的由来

在常用的所有编程语言中,for和while等关键字,都可以帮助我们完成循环重复的任务。但是在Python中,提供了迭代协议与解析工具,能使得迭代更高效,语法更简洁。尽管不是必须的,但是这些工具很有用并且强大,是高级Python程序员常用的技巧。

文件迭代器

为了在总结文件迭代器时更形象,笔者还是在计算机D盘下新建一个文本文档命名为test.txt,内容初始化如下所示:

aaaaaa
bbbbbb
cccccc
dddddd
eeeeee
ffffff
gggggg
  1. 1.可以使用==__next__函数进行迭代==,如果读到文件末尾,直接引发StopIteration异常。
f = open('D:/test.txt')
f.__next__() # 'aaaaaa\n'
f.__next__() # 'bbbbbb\n'
f.__next__() # 'cccccc\n'
f.__next__() # 'dddddd\n'
f.__next__() # 'eeeeee\n'
f.__next__() # 'ffffff\n'
f.__next__() # 'gggggg'
f.__next__() # StopIteration
  1. 使用for自动地调用next。注意,此时就不要使用readlines()方法了,因为readlines()是将整个文件加载进内存,从内存使用的角度来看,效果较差
    当然也可以用while语句,但是相比于for迭代器,运行更慢。因为for在Python中是以C语言的速度运行的,而while是使用Python虚拟机运行Python字节码的。
for line in open('D:/test.txt'):
	line = line.rstrip()
    print(line)
for line in open('D:/test.txt').readlines():
	line = line.rstrip()
    print(line)
'''输出如下
aaaaaa
bbbbbb
cccccc
dddddd
eeeeee
ffffff
gggggg
'''

Python 3.0及之后版本中的手动迭代:iter和next

  1. Python 3.0及之后的版本中包含内置函数next,自动调用一个对象的__next__方法。在for循环开始时,会通过它传给iter内置函数,以便从可迭代对象中返回一个迭代器,返回对象含有需要的next方法。比如文件对象的例子,文件对象就是自己的迭代器,下面的例子借用第一小节中创建的文件。
f = open('D:/test.txt')
next(f) # 'aaaaaa\n'
next(f) # 'bbbbbb\n'
next(f) # 'cccccc\n'
next(f) # 'dddddd\n'
next(f) # 'eeeeee\n'
next(f) # 'ffffff\n'
next(f) # 'gggggg'
next(f) # StopIteration
  1. 与文件对象不同,比如列表及其他很多的内置对象,就需要手动地先显式调用iter函数来启动迭代。注意,在Python 2.6及之后的版本中,还可以使用I.next(),Python 3.0及之后的版本中取消
L = [1, 2, 3]
I = iter(L)
type(I) # <class 'list_iterator'>
I.__next__() # 1
next(I) # 2

列表解析

  1. 例子:在Python解释器内部执行一个遍历列表L的迭代,按照顺序将x赋给每个元素,执行表达式的结果,最终返回一个新列表
L = [1, 2, 3]
L = [x +10 for x in L] # [11, 12, 13]
  1. 效率:列表解析比手动的for循环语句运行得更快,因为迭代在解释器内部是以C语言的速度执行的,而不是以手动Python代码执行的,特别是对于较大的数据集合
  2. 文件中的列表解析,下面的例子借用第一小节中创建的文件。
f = open('D:/test.txt')
lines = f.readlines() # ['aaaaaa\n', 'bbbbbb\n', 'cccccc\n', 'dddddd\n', 'eeeeee\n', 'ffffff\n', 'gggggg']
lines = [line.rstrip() for line in lines] # ['aaaaaa', 'bbbbbb', 'cccccc', 'dddddd', 'eeeeee', 'ffffff', 'gggggg']

上述列表解析可以结合文件迭代进行内存优化,不用提前打开文件。对于较大的文件,列表解析的优势更加显著。

lines = [line.rstrip() for line in open('D:/test.txt')] # ['aaaaaa', 'bbbbbb', 'cccccc', 'dddddd', 'eeeeee', 'ffffff', 'gggggg']
  1. 列表解析允许任意数目的for子句,每个子句有一个可选的相关的if子句。
[x + y for x in 'abc' for y in 'lmn'] # ['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']
  1. if子句。
[x **2 for x in range(10) if x % 2 ==0] # [0, 4, 16, 36, 64]
  1. 列表解析的应用例子:矩阵运算
M = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
N = [[2, 2, 2], [3, 3, 3], [4, 4, 4]]
[M[row][col] * N[row][col] for row in range(3) for col in range(3)] # [2, 4, 6, 12, 15, 18, 28, 32, 36]
[[M[row][col] * N[row][col] for row in range(3)] for col in range(3)] # [[2, 12, 28], [4, 15, 32], [6, 18, 36]]
  1. 列表解析和map内置函数,比for循环会快很多。好的Python程序员会尽量避免低效的手动循环。

生成器函数

  1. 生成器函数定义:编写常规的def语句,但是使用yield语句一次返回一个结果,在每个结果之间挂起和继续他们的状态
  2. 生成器函数返回按需产生结果的一个对象,而不是构造一个结果列表。
  3. 状态挂起:生成器函数在自动生成值的时刻挂起并继续函数的执行。生成器yield一个值,而不是返回一个值。yield语句挂起函数并向调用者发送回一个值,但保留足够的状态使得函数能从他离开的地方继续。
def gen(N):
    for i in range(N):
        yield i ** 2

def main():
    for i in gen(5):
        print(i)

if __name__ == '__main__':
    main()
'''
输出
0
1
4
9
16
'''
  1. 调用生成器函数时,返回一个迭代器对象,该对象支持一个__next__方法来继续执行接口,继续运行到下一个yield结果返回或者引发一个StopIteration异常。
def gen(N):
    for i in range(N):
        yield i ** 2

def main():
    x = gen(5)
    print(type(x)) # <class 'generator'>
    print(x.__next__()) # 0
    print(x.__next__()) # 1
    print(x.__next__()) # 4
    print(x.__next__()) # 9
    print(x.__next__()) # 16
    print(x.__next__()) # StopIteration

if __name__ == '__main__':
    main()
  1. 生成器函数也可能有一条return语句,总是在def语句块的末尾(一般不常用),直接终止生成。
def gen(N):
    for i in range(N):
        yield i ** 2

def main():
    for i in gen(5):
        print(i)

if __name__ == '__main__':
    main()
'''
输出
0
'''
  1. send方法。yield表达式会返回为了发送而传入的值,注意是用在生成器表达式中,即X = yield i,只有yield i 不管用。且用send方法前必须要用next()先初始化生成器
def gen(N):
    for i in range(N):
        x = yield i ** 2
        print(x)


def main():
    G = gen(5)
    next(G)
    r = G.send(77)
    print(r)
    r = G.send(88)
    print(r)
    r = next(G)
    print(r)

if __name__ == '__main__':
    main()
'''
输出
77
1
88
4
None
9
'''

def gen(N):
    for i in range(N):
        x = yield i ** 2
        print(x)

# 下面是没有初始化生成器的错误情况
def main():
    G = gen(5)
    # next(G)
    r = G.send(77)
    print(r)
    r = G.send(88)
    print(r)
    r = next(G)
    print(r)

if __name__ == '__main__':
    main()
'''
输出
TypeError: can't send non-None value to a just-started generator
'''
  1. 生成器是迭代对象。生成器函数与生成器表达式都是单迭代对象,只支持一个迭代器,两个会交替迭代
def gen(N):
    for i in range(N):
        yield i ** 2

def main():
    X = gen(5)
    I1, I2 = iter(X), iter(X)
    print(I1.__next__()) # 0
    print(I2.__next__()) # 1
    print(I1.__next__()) # 4
    print(I2.__next__()) # 9
    print(I1.__next__()) # 16
    print(I2.__next__()) # StopIteration

if __name__ == '__main__':
    main()

多个迭代器

  1. range对象支持len和索引,不是自己的迭代器(需要用iter手动返回迭代器)。支持多个迭代器,每个迭代器会记住各自的位置。
R = range(3)
I1 = iter(R)
I2 = iter(R)
next(I1) # 0
next(I2) # 0
  1. 列表,字典,元组,集合等内置类型支持多个迭代器
L = [1, 2, 3]
I1 = iter(L)
I2 = iter(L)
next(I1) # 1
next(I2) # 1
  1. zip,map和filter不支持相同结果中的多个活跃迭代器
Z = zip((1, 2, 3), (10, 11, 12))
I1 = iter(Z)
I2 = iter(Z)
next(I1) # (1,10)
next(I2) # (2,11)

其它迭代环境

  1. sorted排序可迭代对象中的各项,在Python 3.0及之后的版本中直接返回列表
L = [3, 2, 1]
I = iter(L)
sorted(I) # [1, 2, 3]
sorted(L) # [1, 2, 3]
  1. zip组合可迭代的对象中的各项,在Python 3.0及之后的版本中返回可迭代对象
L1 = [1, 2, 3]
L2 = [4, 5, 6]
I1 = iter(L1)
I2 = iter(L2)
zip(I1, I2) # <zip object at 0x0000025295FB2F80>
list(zip(I1, I2)) # [(1, 4), (2, 5), (3, 6)]
zip(L1, L2) # <zip object at 0x0000025295FBE4C0>
list(zip(L1, L2)) # [(1, 4), (2, 5), (3, 6)]
  1. enumerate根据相对位置配对可迭代对象中的各项,在Python 3.0及之后的版本中返回可迭代对象
L = [1, 2, 3]
I = iter(L)
enumerate(I) # <enumerate object at 0x0000025295FC3C00>
list(enumerate(I)) # [(0, 1), (1, 2), (2, 3)]
enumerate(L) # <enumerate object at 0x0000025295FAC680>
list(enumerate(L)) # [(0, 1), (1, 2), (2, 3)]
  1. filter选择一个函数为真的项,在Python 3.0及之后的版本中返回可迭代对象
L = [-3, -2, -1, 0, 1, 2, 3]
I = iter(L)
filter(bool, I) # <filter object at 0x0000025295F57910>
list(filter(bool, I)) # [-3, -2, -1, 1, 2, 3]
filter(bool, L) # <filter object at 0x0000025295FB8220>
list(filter(bool, L)) # [-3, -2, -1, 1, 2, 3]
  1. reduce针对可迭代对象中成对的项运行一个函数,在Python 3.0及之后的版本中直接返回结果。需要提供两个参数的函数,一个可迭代对象,和一个可选的初始化值。函数逻辑是依次从可迭代对象中取一个元素,和上一次调用函数的结果做参数再次调用函数。
reduce(lambda x, y: x + y, (1, 2, 3, 4)) # 10
reduce(lambda x, y: x + y, (1, 2, 3, 4), 100) # 110
  1. sum计算可迭代对象中的总数
L = [1, 2, 3]
I = iter(L)
sum(I) # 6
sum(L) # 6
  1. any和all针对可迭代对象中任何所有项为真时返回True。注意,是直接针对可迭代对象进行判断,不是判断迭代器对象,见下方例子。
L = [0, 1, 2, 3]
I = iter(L)
any(I) # True
any(L) # True
all(I) # True
all(L) # False
  1. max和min分别返回一个可迭代对象中的最大和最小项。注意,是直接针对可迭代对象进行判断,不是判断迭代器对象,见下方例子。
L = [0, 1, 2, 3]
I = iter(L)
max(I) # ValueError: max() arg is an empty sequence
max(L) # 3
min(I) # ValueError: min() arg is an empty sequence
min(L) # 0

以上,欢迎各位读者朋友提出意见或建议。

欢迎阅读笔者后续博客,各位读者朋友的支持与鼓励是我最大的动力!

written by jiong
我从来不想独身,
却有预感晚婚。
我在等,
世上唯一契合灵魂。


本文链接: http://www.dtmao.cc/news_show_2000245.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?