自学Python之Python基础:(六)可迭代对象与反迭代技巧
2017-12-01 23:18
337 查看
转载的老板请注明出处:http://blog.csdn.net/cc_xz/article/details/78692064万分感谢!
在本篇中,你将了解到:
1.可迭代对象、迭代器与生成器。
2.如何进行反向迭代。
3.对迭代器进行切片。
4.同时迭代多个可迭代对象。
可迭代对象、迭代器与生成器:
容器:
容器是用来存储元素的一种数据结构,容器将所有的数据都保存在内存中,在Python中的典型容器有:list、deque、set、dict、OrderedDict等。容器很容易理解,它就是生活中的箱子、房间等一切可以盛放其他物品的容器。
通过判断对象是否包含某元素来确定它是否是一个容器:
输出结果为:
可迭代对象:
大部分的容器都是可迭代的,但还有一些对象也是可以迭代的,例如文件对象和管道对象等。一般而言容器所存储的元素是优先的,同样的,可迭代对象也可以用来表示一些包含有限元素的数据结构。任何对象都可以成为可迭代对象,不一定必须是基本数据结构,只要这个对象可以返回一个iterator即可。
输出结果为:
值得注意的是,这里是x是可迭代对象,而y是迭代器,迭代器可以从可迭代对象中获取值。在可以进行迭代的类中,一般会实现__iter__()和__next__()两个函数。
当执行上述for循环中的代码时,实际上是在执行:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201712/8add3bdd1a6dae699d9c14e04a8a42b8)
即:将一个可迭代对象调用iter()函数转换成一个迭代器,再调用这个迭代器的netx()函数,将其中的元素一一解析出来。
迭代器(Iterators):
任何具有__next__()函数的对象都是迭代器,对迭代器调用next()函数可以获取下一个元素。而至于该对象如何产生这个值,同它能否成为一个迭代器并没有关系(只有一个元素)。所以迭代器本质上是一个生产元素的工厂,每次向迭代器请求下一个值时,迭代器都会进行计算并返回相应的元素。
为了更好的理解迭代器的内部结构,首先来定义一个生成斐波拉契数的迭代器(菲波拉契数是值在一个数值的序列中,后面一个数的值等于前面两个数的和)
输出结果为:
这个类既是可迭代对象(因为具有__iter__()函数),又是迭代器(因为它具有__next__()函数)。迭代器内部的状态保存着当前对象的prev以及current两个变量的属性,并且在下次迭代时会调用这两个变量。而每次的迭代,都是调用__next__()函数。它会进行如下操作:
1. 修改当前的函数状态,以便下次的迭代。
2. 计算当前的变量。
3. 返回变量的值(value,即迭代结果)。
可迭代对象无法依次迭代出其中包含的元素,因为没有__next__()函数,而迭代器可以。总而言之:迭代器一定是一个迭代对象,但迭代对象不一定是迭代器。
yield关键字 :
yield是类似return的关键字,只不过这个函数是返回一个生成器。当你调用这个函数的时候,函数内部的代码并不会立刻执行。这个函数会返回一个生成器对象。而真正执行这个函数的时候,是使用for等循环进行迭代的时候。
输出结果为:
生成器:
生成器实际上是一种更高级更优雅的迭代器,使用生成器可以使用更简洁的语法来定义迭代器。下面也是一个生成斐波那契序列的工厂函数,不过是以生成器的方式编写的:
输出结果为:
首先,fib是一个很普通的函数,但是这个函数中没有return语句,函数返回的值是一个生成器。
当调用f = fib()时,生成器被实例化,并返回,这时并不会执行任何代码,生成器处于空闲状态,注意,这里的prev,current = 0 , 1 并未执行。
然后生成器被包含在for循环中,这是一个迭代器,所以仍然没有执行上述代码。
而当for循环开始执行时,循环才开始调用fib()的next()函数。当执行到yield current时,会返回当前的current的值,然后生成器(fib()函数)又进入空闲状态。
接着再继续执行剩余的步骤,直至current返回的值大于100,执行break跳出循环。
反向迭代:
如果希望反向迭代(从末尾开始迭代)一个列表,可以使用如下操作:
输出结果为:
reversed()函数:
内置函数reversed()函数,接收一个容器,将输出一个反向列表迭代器,也就是说,reversed()函数是同iter()的作用相同,但实际得出的结果是相反的。
输出结果为:
也就是说,当一个容器经过iter()函数转变为可迭代对象后,是给容器增加了__iter__函数,同样的,当一个容器经过reversed()函数转变为反向可迭代对象后,也是给该容器增加了__reversed__函数。
实现反向迭代案例:
实现一个“连续浮点数产生器”,同range()函数类似,根据给定的范围(start,end)两个参数,以及步进值(step(递增))产生一些连续的浮点数。
例如,FloatRange(3.0,4.0,0.2)可产生的序列:
输出结果为:
对迭代器进行切片:
切片:
在Python中我们可以直接使用索引来访问序列中的元素,同时索引可以分为正向和反向的两种,同样,切片也可以使用正向、反向索引来找出序列(列表、字典、元组等)中的元素。
输出结果为:
上述代码中,实现了使用切片的方法查找序列中的元素。而切片的语法为:
其中:
切片的多种操作:
输出结果为:
使用readlines()配合list()切片:
输出结果为:
但这种方法存在一个问题,即:readlines()函数会一次性将所有文本读入到内存中,如果文本较小,则没有什么压力,但如果需要读取一些例如日志文件等动辄上G的文本,则会对内存造成较大的开销。
使用迭代器进行切片:
输出结果为:
在一个for多同时迭代多个可迭代对象:
使用索引同时迭代:
输出结果为:
使用索引同时迭代可迭代对象,虽然可以实现预计的目标,但是实际使用中会有比较大的局限性。例如,如果需要迭代生成器,那么就无法使用索引进行迭代。
使用zip()函数进行并行迭代:
使用内置的zip()函数进行并行迭代(同时迭代多个可迭代对象),它能将多个可迭代对象合并,再返回一个元组。
输出结果为:
在使用这种方式时,需要注意的是,在将各个列表组合成元组时,如果列表的长度不相等,那么元组的长度将以最短的列表的长度为基准。
对可迭代对象进行串行迭代:
输出结果为:
在本篇中,你将了解到:
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 value test01 = 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 * x print("程序开始执行") 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 + current test01 = 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 islice txt = 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 randint list01 = [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 randint list01 = [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 randint list01 = [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 += 1 print(count)
输出结果为:
25
相关文章推荐
- python学习笔记--实用技巧之可迭代对象的元素分解
- Python进阶强化训练之对象迭代与反迭代技巧
- Python技巧:对象迭代与反迭代
- Python 基础 —— 判断一个对象是否为可迭代对象
- python3使用了更多内存优化的技巧,比如,python3的zip就是生成可迭代的对象
- 自学Python之Python基础:(七)字符串处理技巧
- Python中对象迭代与反迭代的技巧总结
- 迭代器就是重复地做一些事情,可以简单的理解为循环,在python中实现了__iter__方法的对象是可迭代的,实现了next()方法的对象是迭代器,这样说起来有
- python可迭代对象和迭代器
- Python自学笔记之基础知识回顾2
- python高效编程技巧7(pickle的使用:可以将一个对象存储在一个文件中,或者load进来)
- Python爬虫必备基础与技巧
- 【Python-2.7】如何判断对象是否为可迭代?
- 零基础入门学习Python(20):对象(5)类和对象的内置函数
- Python中一些不为人知的基础技巧总结
- python基础3(自学代码记录)
- Python基础 - 迭代
- python 语言基础之切片,迭代
- 自学Python之Python基础:(二)Python容器:列表、元组、字典
- 零基础入门学习Python(36)--类和对象:给大家介绍对象