自学Python之Python基础:(六)可迭代对象与反迭代技巧
来源:互联网 发布:网页调用excel数据 编辑:程序博客网 时间:2024/06/07 12:06
转载的老板请注明出处:http://blog.csdn.net/cc_xz/article/details/78692064万分感谢!
在本篇中,你将了解到:
1.可迭代对象、迭代器与生成器。
2.如何进行反向迭代。
3.对迭代器进行切片。
4.同时迭代多个可迭代对象。
可迭代对象、迭代器与生成器:
容器:
容器是用来存储元素的一种数据结构,容器将所有的数据都保存在内存中,在Python中的典型容器有:list、deque、set、dict、OrderedDict等。容器很容易理解,它就是生活中的箱子、房间等一切可以盛放其他物品的容器。
通过判断对象是否包含某元素来确定它是否是一个容器:
assert 1 in [1, 2, 3, 4] # assert是Python中的断言,断言是表达式的返回结果比如为True。assert 4 not in [1, 2, 3] # 如果表达式的返回结果为False,则报出AssertionError异常。assert 1 in {1, 2, 3, 4, 5} # 例如第五行中,由于元组中并不存在1,所以报出异常。assert 4 not in {1, 2, 3, 5} # 而这些代码表明,list、set、tuple都是容器。 assert 1 in (12, 3, 4, 5) # 因为他们都包含了多个不同的元素。assert 4 not in (1, 2, 3, 5)
输出结果为:
Traceback (most recent call last): File "D:/Python/Code/Demo01/Demo.py", line 5, in <module> assert 1 in (12,3,4,5)AssertionError
可迭代对象:
大部分的容器都是可迭代的,但还有一些对象也是可以迭代的,例如文件对象和管道对象等。一般而言容器所存储的元素是优先的,同样的,可迭代对象也可以用来表示一些包含有限元素的数据结构。任何对象都可以成为可迭代对象,不一定必须是基本数据结构,只要这个对象可以返回一个iterator即可。
x = [1,2,3,4,5]y = iter(x) #iter()函数用于生成一个迭代器。# print(next(x)) #默认list是可迭代对象,而不是迭代器对象。print(next(y)) #而y则是使用iter()函数所生成的迭代器对象。print(type(x))print(type(y))
输出结果为:
1<class 'list'><class 'list_iterator'>
值得注意的是,这里是x是可迭代对象,而y是迭代器,迭代器可以从可迭代对象中获取值。在可以进行迭代的类中,一般会实现__iter__()和__next__()两个函数。
x = [1,2,3,4,5]for x_ in x : pass
当执行上述for循环中的代码时,实际上是在执行:
即:将一个可迭代对象调用iter()函数转换成一个迭代器,再调用这个迭代器的netx()函数,将其中的元素一一解析出来。
迭代器(Iterators):
任何具有__next__()函数的对象都是迭代器,对迭代器调用next()函数可以获取下一个元素。而至于该对象如何产生这个值,同它能否成为一个迭代器并没有关系(只有一个元素)。所以迭代器本质上是一个生产元素的工厂,每次向迭代器请求下一个值时,迭代器都会进行计算并返回相应的元素。
为了更好的理解迭代器的内部结构,首先来定义一个生成斐波拉契数的迭代器(菲波拉契数是值在一个数值的序列中,后面一个数的值等于前面两个数的和)
class fib: def __init__(self): self.prev = 0 #前一个值 self.current = 1 #当前值 def __iter__(self): #该函数返回一个可迭代对象。 return self def __next__(self): #该函数返回一个迭代器(每次迭代返回的值)。 value = self.current self.current += self.prev self.prev = value return valuetest01 = fib()for x in test01: if x > 100: break print(x,end=" ")
输出结果为:
1 1 2 3 5 8 13 21 34 55 89
这个类既是可迭代对象(因为具有__iter__()函数),又是迭代器(因为它具有__next__()函数)。迭代器内部的状态保存着当前对象的prev以及current两个变量的属性,并且在下次迭代时会调用这两个变量。而每次的迭代,都是调用__next__()函数。它会进行如下操作:
1. 修改当前的函数状态,以便下次的迭代。
2. 计算当前的变量。
3. 返回变量的值(value,即迭代结果)。
可迭代对象无法依次迭代出其中包含的元素,因为没有__next__()函数,而迭代器可以。总而言之:迭代器一定是一个迭代对象,但迭代对象不一定是迭代器。
yield关键字 :
yield是类似return的关键字,只不过这个函数是返回一个生成器。当你调用这个函数的时候,函数内部的代码并不会立刻执行。这个函数会返回一个生成器对象。而真正执行这个函数的时候,是使用for等循环进行迭代的时候。
def createYiele(): print("执行了createYiele()") for x in range(5): print("执行了createYiele()中的for循环") yield x * xprint("程序开始执行")for x in createYiele(): print("执行了for循环,x的值为:",x)
输出结果为:
程序开始执行执行了createYiele()执行了createYiele()中的for循环执行了for循环,x的值为: 0执行了createYiele()中的for循环执行了for循环,x的值为: 1执行了createYiele()中的for循环执行了for循环,x的值为: 4
生成器:
生成器实际上是一种更高级更优雅的迭代器,使用生成器可以使用更简洁的语法来定义迭代器。下面也是一个生成斐波那契序列的工厂函数,不过是以生成器的方式编写的:
prev, current = 0, 1 # 创建两个变量,并赋值。 while True: # 创建一个死循环。 yield current # 使用生成器返回current(当前值) prev, current = current, prev + currenttest01 = fib()for x in test01: if x > 100: break print(x, end=" ")
输出结果为:
1 1 2 3 5 8 13 21 34 55 89
首先,fib是一个很普通的函数,但是这个函数中没有return语句,函数返回的值是一个生成器。
当调用f = fib()时,生成器被实例化,并返回,这时并不会执行任何代码,生成器处于空闲状态,注意,这里的prev,current = 0 , 1 并未执行。
然后生成器被包含在for循环中,这是一个迭代器,所以仍然没有执行上述代码。
而当for循环开始执行时,循环才开始调用fib()的next()函数。当执行到yield current时,会返回当前的current的值,然后生成器(fib()函数)又进入空闲状态。
接着再继续执行剩余的步骤,直至current返回的值大于100,执行break跳出循环。
反向迭代:
如果希望反向迭代(从末尾开始迭代)一个列表,可以使用如下操作:
"""对于反向操作,可以使用列表自带的reverse()函数,这样可以将列表中的内容进行反序。但这样实际上对原列表进行了修改,一方面如果列表比较大,容易造成较大的开销,另一方面也容易对象原列表造成影响。"""list01 = [1,2,3,4,5,6]list01.reverse()print(list01)"""当然也可以使用切片操作,使用步进值为-1的切片操作,但这样是得到了一个同原列表大小相同的新列表。容易造成资源浪费。"""list02 = [1,2,3,4,5,6]print(list02[:: -1])
输出结果为:
[6, 5, 4, 3, 2, 1][6, 5, 4, 3, 2, 1]
reversed()函数:
内置函数reversed()函数,接收一个容器,将输出一个反向列表迭代器,也就是说,reversed()函数是同iter()的作用相同,但实际得出的结果是相反的。
list01 = [1,2,3,4,5,6]list02 = reversed(list01)for x in list02: print(x,end=" ")
输出结果为:
6 5 4 3 2 1
也就是说,当一个容器经过iter()函数转变为可迭代对象后,是给容器增加了__iter__函数,同样的,当一个容器经过reversed()函数转变为反向可迭代对象后,也是给该容器增加了__reversed__函数。
实现反向迭代案例:
实现一个“连续浮点数产生器”,同range()函数类似,根据给定的范围(start,end)两个参数,以及步进值(step(递增))产生一些连续的浮点数。
例如,FloatRange(3.0,4.0,0.2)可产生的序列:
class FloatRange: # 首先创建初始化函数,并定义三个关键变量:起止范围,增量值。 def __init__(self, start, end, step=0.1): self.start = start self.end = end self.step = step # __iter__用于将该对象创建为可迭代对象时执行的方法。 def __iter__(self): t = self.start while t <= self.end: yield t t += self.step # __reversed__同iter的作用相同,只不过会将其中的元素颠倒。 def __reversed__(self): t = self.end while t >= self.start: yield t t -= self.step# 正向迭代test01 = FloatRange(1.0, 6, 0.5)for x in test01: print(x, end=" ")print(" ") # 用做换行符效果。# 反向迭代test02 = reversed(FloatRange(1, 6, 0.5))for x in test02: print(x, end=" ")
输出结果为:
1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6 5.5 5.0 4.5 4.0 3.5 3.0 2.5 2.0 1.5 1.0
对迭代器进行切片:
切片:
在Python中我们可以直接使用索引来访问序列中的元素,同时索引可以分为正向和反向的两种,同样,切片也可以使用正向、反向索引来找出序列(列表、字典、元组等)中的元素。
list01 = [1,2,3,4,5,6]print(list01[1:6:2])
输出结果为:
[2, 4, 6]
上述代码中,实现了使用切片的方法查找序列中的元素。而切片的语法为:
list01[start_index :end_index:step]
其中:
start_index表示起始索引值,用于确定从何处开始。end_index表示结束索引值,用于确定到何处停止。step表示步长,切片操作是按照步长,截取从起始索引到结束索引,但不包含结束索引。步长不能为0,默认值为1。
切片的多种操作:
list01 = list(range(1, 20))print(list01[4:13]) # 省略步长,将输出Index4-13中所有的元素。因为默认步长为1。print(list01[:13]) # 省略start_index(起始索引),保留end_index(结束索引),这样会从第一个元素开始,一直到结束索引指定(结束索引-1)的位置。print(list01[4:]) # 保留start_index(起始索引),省略end_index(结束索引),这样会从起始索引处,一直到最后一个元素。print(list01[:]) # 省略start_index和end_index则输出整个序列,即生成一个完整的新序列。print(list01[::3]) # 省略start_index和end_index但保留step,则表示对整个序列按照步长整除的规则取值。print(list01[::-1]) # 如果将步长设置为-1,则可以得到一个反向序列,但注意,这样是创建了一个新的序列,若序列较大,则耗费内存。
输出结果为:
[5, 6, 7, 8, 9, 10, 11, 12, 13][1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13][5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19][1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19][1, 4, 7, 10, 13, 16, 19][19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
使用readlines()配合list()切片:
txt = open('Release.txt','r', encoding= u'utf-8') #创建文本对象#txt[10:30] #无法对文本对象进行切片。list01 = list(txt.readlines()) # readlines()可以将文本按照行来输出,配合list()可以将每行的文本作为一个元素赋值到列表中。print(list01[20:30]) # 从而可以对列表进行切片。
输出结果为:
x = 1[' 1. 更新用户手册。\n', '\n', .....省略部分结果 \n', '- 更新:\n']
但这种方法存在一个问题,即:readlines()函数会一次性将所有文本读入到内存中,如果文本较小,则没有什么压力,但如果需要读取一些例如日志文件等动辄上G的文本,则会对内存造成较大的开销。
使用迭代器进行切片:
from itertools import islicetxt = open('Release.txt','r', encoding= u'utf-8') # 创建文本对象iter01 = islice(txt,10,20,2) # 导入文本对象,以及起始索引、结束索引和步进值。iter02 = iter(txt) # 使用iter()同样可以创建一个可迭代对象,但无法进行切片操作。for line in iter01: print(line)
输出结果为:
4. 调整目录结构。----------------------------------------------------------版本:1.5 1. 删除封底的公司地址。
在一个for多同时迭代多个可迭代对象:
使用索引同时迭代:
from random import randintlist01 = [randint(60,100) for x in range(20)]list02 = [randint(60,100) for x in range(20)]list03 = [randint(60,100) for x in range(20)]list04 = [randint(60,100) for x in range(20)]list05 = []for x in range(len(list01)): list05.append(list01[x]+list02[x]+list03[x]+list04[x])print(list05)
输出结果为:
[319, 276, 290, 307, 300, 314, 316, 385, 345, 386, 304, 334, 301, 282, 325, 326, 331, 335, 356, 320]
使用索引同时迭代可迭代对象,虽然可以实现预计的目标,但是实际使用中会有比较大的局限性。例如,如果需要迭代生成器,那么就无法使用索引进行迭代。
使用zip()函数进行并行迭代:
使用内置的zip()函数进行并行迭代(同时迭代多个可迭代对象),它能将多个可迭代对象合并,再返回一个元组。
from random import randintlist01 = [randint(60, 100) for x in range(20)]list02 = [randint(60, 100) for x in range(20)]list03 = [randint(60, 100) for x in range(20)]list04 = [randint(60, 100) for x in range(20)]list05 = []tuple01 = zip(list01, list02, list03, list04) # 进行组包操作,将各个列表组合成一个元组。for x, y, z, i in tuple01: # 通过for循环进行拆包,将元组中的值进行拆分。 list05.append(x + y + z + i)print(list05)
输出结果为:
[316, 328, 332, 338, 349, 352, 328, 310, 346, 344, 335, 317, 276, 315, 303, 337, 312, 374, 299, 372]
在使用这种方式时,需要注意的是,在将各个列表组合成元组时,如果列表的长度不相等,那么元组的长度将以最短的列表的长度为基准。
对可迭代对象进行串行迭代:
from itertools import chain # 使用此函数对可迭代对象进行串连。from random import randintlist01 = [randint(60, 100) for x in range(20)]list02 = [randint(60, 100) for x in range(20)]list03 = [randint(60, 100) for x in range(20)]list04 = [randint(60, 100) for x in range(20)]chain01 = chain(list01, list02, list03, list04) # 将多个可迭代对象进行链接(串连),从而获得一个可迭代对象。count = 0 # 统计出所有值高于90的次数。for x in chain01: # 此时的迭代,将先迭代list01的第0个元素,再迭代list02的第0个元素,以此类推。 if x > 90: count += 1print(count)
输出结果为:
25
- 自学Python之Python基础:(六)可迭代对象与反迭代技巧
- 自学Python之Python基础:(七)字符串处理技巧
- 自学Python之Python基础:(三)Python代码结构
- 自学Python之Python基础:(四)Python多线程
- 自学Python之 基础语法
- 自学Python之Python基础:(八)正则表达式
- Python基础之六面向对象高级编程
- Python基础(六)
- Python自学基础 判断与循环
- 自学Python之Scrapy爬虫:(一)爬虫基础
- 自学Python之Python基础:(二)Python容器:列表、元组、字典
- 自学Python之Python基础:(五)Python数据结构常用操作
- Python对象之基础对象
- Python基础自学-Python简介
- 【Python自学】02.Python基础
- Python基础(六)- 函数
- python基础(六)dict
- Python基础学习(六)
- 最近编写过程中遇到的问题总结(在练习qt pcl vs vtk的融合中)
- PAT (Basic Level) Practise (中文)1045. 快速排序(25)
- JDK环境变量配置
- (8)使用p命名空间简化setter注入
- 算法导论2-1.3:n个数的一个序列A中查找v
- 自学Python之Python基础:(六)可迭代对象与反迭代技巧
- wxWidgets教程(17)——wxSqlite3用法
- PHP基础之字符串(5)
- SJTU OJ 1990 二哥听CD
- java数据库操作--改写成工具类
- 排序算法复杂性
- 学习英语的第三个阶段-大量阅读
- 如何把Word的默认页面颜色由白色改为绿色
- NetSuite ERP的优势是什么?