Python中有6种基本的数据类型,除了数字类型,其它5种类型都是可迭代对象
。掌握可迭代对象的特性和方法是非常有必要的。
迭代(遍历)
就是按照某种顺序逐个访问对象中的每一项。
Python中有很多对象都是可以通过for语句来直接遍历的,例如list、string、dict等等,这些对象都是可迭代的,被称为可迭代对象。
可以将可迭代对象想象成一个容器,里面存放了有限个元素,并且每个元素都可以从中获取出来。那么这个容器就是可迭代的,这个容器就是可迭代对象。
所有的可迭代对象都需要实现__iter__
方法,该方法就是用于当我们在循环时将可迭代对象
转换成迭代器
的。
from typing import *
dct = {'one': 1, 'two': 2, 'three': 3, 'zero': 0}
print(isinstance(dct, Iterable))
True
迭代器
是可迭代对象
的一个子集。是一个可以记住遍历的位置的对象,它与列表、元组、集合、字符串这些可迭代对象
的区别就在于__next__()
方法的实现。也就是通过该方法可以一个个的将元素取出来。
迭代器支持__iter__()
和__next__()
方法。其中:
__iter__()
方法返回迭代器对象本身,而可迭代对象的该方法则返回其迭代器。
__next__()
方法返回容器的下一个元素,在结尾时引发StopIteration异常。
遍历的本质其实就是在我们使用for或者while循环可迭代对象时会自动调用该可迭代对象的__iter__()方法得到其迭代器,然后通过这个迭代器的__next__()方法一步步获取下一个元素。直到最后一个元素引发StopIteration异常。
对于迭代器,其__next__()方法和next(迭代器)等效,都是用来获取下一个元素。
通过iter函数可以很简单的将可迭代对象转换成迭代器。
示例1:
>>> from typing import *
>>> iterable = [1, 2, 4]
>>> it = iter(iterable)
>>> print(isinstance(it, Iterable)) # 是否可迭代对象
>>> print(isinstance(it, Iterator)) # 是否迭代器
True
True
示例2:
s = 'ABC' # s此时是一个可迭代对象
it = iter(s) # 转换成迭代器,如果此时报错,则表示s不是一个可迭代对象。
for i in range(len(s)):
print(s[i]) # 遍历s中的所有元素
print(it.__next__)
print(it.__next__()) # 迭代获取元素
print(next(it)) # 继续迭代获取元素
A
B
C
next’ of str_iterator object at 0x0000013D53672D40>
A
B
参见可迭代对象相关函数
的filter函数
参见可迭代对象相关函数
的map函数
除了Python内置的iter之外,还可以通过Python内置的工具包itertools创建迭代器,其中函数包括:
count
cycle
repeat
accumulate
chain
compress
dropwhile
islice
product
permutations
combinations
…
itertools中包含很多用于创建迭代器的实用方法,如果感兴趣可以访问官方文档进行详细了解。
class iterator_list(object):
def __init__(self,a):
self.a=a
self.len=len(self.a)
self.cur_pos=-1
def __iter__(self):
return self
def __next__(self):
self.cur_pos +=1
if self.cur_pos<self.len:
return self.a[self.cur_pos]
else:
raise StopIteration() # 表示至此停止迭代
r = iter(range(10))
print(5 in r)
for i in r:
print(i)
True
6
7
8
9
r = iter(range(10))
print(5 in r)
print(5 in r)
True
False
r = iter(range(10))
print(11 in r)
for i in r:
print(i)
False
你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择,节省内存,按需取值。
生成器
是迭代器
的子集,生成器一定是迭代器,但是迭代器不全是生成器。在Python中一个函数可以用yiled或yiled from替代return返回值,这样的话这个函数就变成了一个生成器对象。
可以通过isinstance(obj, Generator)来进行判断是否为迭代器(需要导入typing模块)。
通过yield
def fib(number):
n, a, b = 0, 0, 1
while n < number:
yield b
a, b = b, a + b
n += 1
gen = fib(5)
print(gen)
print(type(gen))
def func():
print('111')
yield 222
print('123')
yield 1211
gener = func()
print(gener.__next__())
print(gener.__next__())
print(gener.__next__()) # 会报错
111
222
123
1211
Traceback (most recent call last):
File “C:\Users\furongbing\PycharmProjects\pythonProject\t2.py”, line 11, in
print(gener.next()) # 会报错
StopIteration
通过yield from
def generator(array): # 其中:array为可迭代对象
for sub_array in array:
yield from sub_array
gen = generator([1, 2, 3])
print(gen)
print(type(gen))
yield from相对于yield的有几个主要优点:
yield 与 return 的区别:
>>> gen = (x*2 for x in range(10))
>>> gen
>>> type(gen)
列表推导式和生成器推导式的区别:
生成器的优点:
可迭代对象
都可以进行遍历的操作。就是将其中的元素一个个取出来进行操作。
Iter = 'Python'
for i in Iter:
print(i)
P
y
t
h
o
n
对于序列
,不但能遍历里面所有的元素,还能遍历元素的索引。关于序列,将在序列
中详细介绍。
用range()和len()函数遍历得到索引和值
seq = 'Python'
for i in range(len(seq)):
print(f'索引:{i}, 元素:{seq[i]}')
索引:0, 元素:P
索引:1, 元素:y
索引:2, 元素:t
索引:3, 元素:h
索引:4, 元素:o
索引:5, 元素:n
通过enumerate函数遍历得到索引和值
seq = 'Python'
for i, v in enumerate(seq):
print(f'索引:{i}, 元素:{v}')
索引:0, 元素:P
索引:1, 元素:y
索引:2, 元素:t
索引:3, 元素:h
索引:4, 元素:o
索引:5, 元素:n
使用zip函数同时遍历多个序列
seq1 = 'Python'
seq2 = 'Java'
for q, a in zip(seq1, seq2):
print(q, a)
P J
y a
t v
h a
注意:上述用字符串做演示,实际上其它序列对象也是一样的
可以用in
和not in
检查某个元素是否在目标可迭代对象中。可迭代对象
都支持成员资格检查操作。
>>> Iter = ‘Python’
>>> ‘P’ in Iter
>>> ‘a’ not in Iter
True
True
注意:上述用字符串做演示,实际上其它可迭代对象也是一样的
解包的英文是unpacking,就是将容器里的元素全部取出来。在Python中,所有的可迭代对象都支持解包。有些情况中,解包会自动完成,在另外一些情况中,可以手动进行解包。
装包和解包相反,就是将一些元素打包放到一起。
在进行变量赋值的时候,如果变量的数量和右边可迭代对象中元素的数量相同时,解包会自动完成。
a, b = [1, 2]
print(a, b)
c, d = {'one': 1, 'two': 2}
print(c, d)
1 2
one two
我们在进行变量赋值的时候,经常会用到下面这种方式,同时为不同的变量赋值不同的数据:
>>> e, f = 5, 6
实际上这就是利用到了解包。后面的5, 6
其实就是可迭代对象(元组),然后进行自动解包后赋值给了变量e和f。
这里值得注意的是,字典在进行自动解包时获得的是字典的key。
可以通过在可迭代对象前面添加*
符号进行手动解包,解包后的结果是可迭代对象中所包含的所有的元素。
因为是多个元素,所以不能对解包后的结果使用len等只支持单个参数的函数。否则会报错。但是可以使用max等可以接收多个参数的函数。
手动解包一般是用在函数接收参数中。
lst = [1, 2, 4]
print(*lst)
print(max(*lst))
print(len(*lst))
1 2 4
4
Traceback (most recent call last):
File “E:\studypy\tmp5.py”, line 4, in
print(len(*lst))
TypeError: len() takes exactly one argument (3 given)
在赋值或者函数传参时,如果在等式左边变量名添加*
则可以进行装包。
lst = [1, 2, 4, 8, 16]
dct = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
a, b, *c = lst
print(a, b, c)
d, *e, f = dct
print(d, e, f)
1 2 [4, 8, 16]
one [‘two’, ‘three’] four
dct = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
a, *b, c = dct.items()
print(a, b, c)
(‘one’, 1) [(‘two’, 2), (‘three’, 3)] (‘four’, 4)
用*
进行装包后的变量类型其实就是列表,在函数中则是元组。函数的解包与装包将会在函数章节中详细介绍。
描述
通过调用迭代器的__next__()方法从迭代器中检索下一项。如果给出了默认值,则在迭代器耗尽时返回默认值,否则将引发 StopIteration。
示例
a = iter([1, 2, 3])
print(next(a))
print(next(a))
print(next(a))
print(next(a, '无数据了'))
1
2
3
无数据了
描述
对于all函数,如果 iterable 的所有元素均为真值或可迭代对象为空则返回 True。否则返回False。any函数则是只要有一个元素为真值,则返回True,否则返回False。若iterable为空,也返回False。
示例
a = []
b = [1, 2]
c = [1, 0]
d = [0, 0]
print(all(a))
print(all(b))
print(all(c))
print(all(d))
print(any(a))
print(any(b))
print(any(c))
print(any(d))
True
True
False
False
False
True
True
False
描述
返回一个枚举对象。iterable 必须是一个序列,或 iterator,或其他支持迭代的对象。 enumerate() 返回的迭代器的 next() 方法返回一个元组,里面包含一个计数值(从 start 开始,默认为 0)和通过迭代 iterable 获得的值。
示例
dct = {'one': 1, 'two': 2}
for i, j in enumerate(dct):
print(i, j)
0 one
1 two
描述
将iterable中的元素作为function函数的输入,然后将返回结果为真的元素构建一个新的迭代器。
如果function是None,则会假设它是一个身份函数,即iterable中所有返回假的元素会被移除。
请注意,filter(function, iterable)相当于一个生成器表达式,当 function 不是None时等效为:(item for item in iterable if function(item))
function是None时等效为:(item for item in iterable if item)
示例
from typing import *
a = [1, 2, 3, 4]
r = filter(lambda x: x - 2, a)
print(list(r))
print(type(r))
print(isinstance(r, Iterable))
print(isinstance(r, Iterator))
print(isinstance(r, Generator))
[1, 3, 4]
True
True
False
描述
返回可迭代对象中包含的元素的个数。
示例
>>> len(‘string’)
>>> len([1, 2, 3])
6
3
描述
返回一个将 function 应用于 iterable 中每一项并输出其结果的迭代器。 如果传入了额外的 iterable 参数,function 必须接受相同个数的实参并被应用于从所有可迭代对象中并行获取的项。 当有多个可迭代对象时,最短的可迭代对象耗尽则整个迭代就将结束。
示例
from typing import *
a = [1, 2, 3, 4]
b = [2, 4, 6, 8]
r = map(lambda x, y: x + y, a, b)
print(list(r))
print(type(r))
print(isinstance(r, Iterable))
print(isinstance(r, Iterator))
print(isinstance(r, Generator))
[3, 6, 9, 12]
True
True
False
描述
返回字符串中编号最大(最小)的字符。
示例
s = 'pythonTian赛车'
print(min(s))
print(max(s))
T
车
描述
从 start 开始自左向右对 iterable 的项求和并返回总计值。 iterable 的项通常为数字,而 start 值则不允许为字符串。
实例
print(sum([1, 2, 3, 4]))
print(sum([1, 2, 3, 4], 3))
10
13
描述
在多个迭代器上并行迭代,从每个迭代器返回一个数据项组成元组。strict参数为True用来检查所有的可迭代对象的长度是否一致,如果不一致则触发 ValueError。为False时则不检查。
示例
for item in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']):
print(item)
(1, ‘sugar’)
(2, ‘spice’)
(3, ‘everything nice’)
序列一定是可迭代对象,而可迭代对象不一定是序列。对于可迭代对象,如果元素是有序的,则是序列。
序列是一种特殊的可迭代对象,序列中的元素不仅可迭代,而且有顺序,可以通过索引或切片的方式获取序列中的元素。
Python中有6中基本的数据类型,其中字符串、列表、元组都是序列。
序列可以进行拼接和重复以进行扩展。
使用'+'进行拼接
通过+
可以将两种相同类型的序列进行拼接,并且拼接之后的数据类型是原来的类型。
>>> ‘pyth’ + ‘on’
‘python’
相邻的2个字符串会自动合并:
>>> ‘pyt’ ‘hon’
‘python’
>>> [1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]
>>> (1, 2, 3) + (4, 5, 6)
(1, 2, 3, 4, 5, 6)
使用'*'进行重复
通过*
可以将两种相同类型的序列进行重复,并且重复之后的数据类型是原来的类型。
>>> ‘egg’ * 3
‘eggeggegg’
>>> [1, 2] * 2
[1, 2, 1, 2]
>>> (1, 2) * 2
(1, 2, 1, 2)
序列支持索引
(下标访问),第一个字符的索引是 0。之后每个元素的索引依次增加1。
>>> seq = ‘Python’
>>> seq[0]
>>> seq[5]
‘P’
‘n’
索引还支持负数,用负数索引时,从右边开始计数:
>>> seq = ‘Python’
>>> seq[-1]
>>> seq[-2]
>>> seq[-6]
‘n’
‘o’
‘P’
注意,-0 和 0 一样,因此,负数索引从 -1 开始。
索引位置参考下图:
±–±–±–±–±–±–+
| P | y | t | h | o | n |
±–±–±–±–±–±–+
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
索引越界会报错:
>>> seq = ‘Python’
>>> seq[42]
Traceback (most recent call last):
File “”, line 1, in
IndexError: string index out of range
注意:上述用字符串做演示,实际上列表和元组也是一样的
除了索引,序列还支持切片
。索引可以提取单个元素,切片则提取多个元素,参考下图:
±–±–±–±–±–±–+
| P | y | t | h | o | n |
±–±–±–±–±–±–+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
注意:上面的数字对应的是每个字符中间的分隔’|'线。
序列的切片语法格式如下:
序列[头下标:尾下标]
>>> seq = ‘Python’
>>> seq[0:2]
>>> seq[2:5]
‘Py’
‘tho’
切片索引的默认值很有用;省略开始索引时,默认值为 0,省略结束索引时,默认为到字符串的结尾:
>>> seq = ‘Python’
>>> seq[:2]
>>> seq[4:]
>>> seq[-2:]
‘Py’
‘on’
‘on’
注意,输出结果包含切片开始,但不包含切片结束。因此,seq[:i] + seq[i:] 总是等于 seq:
>>> seq = ‘Python’
>>> seq[:2] + string[2:]
>>> seq[:4] + string[4:]
‘Python’
‘Python’
对于使用非负索引的切片,如果两个索引都不越界,切片长度就是起止索引之差。例如, seq[1:3] 的长度是 2。
切片会自动处理越界索引:
>>> seq = ‘Python’
>>> seq[4:42]
>>> seq[42:]
‘on’
‘’
指定步长的切片
>>> seq = ‘Python’
>>> seq[0:6:2] # 从第0个元素到第5个元素,每隔2个就提取一个出来
‘Pto’
>>> seq[:] # 浅拷贝,结果同string
‘Python’
>>> seq[::-1] # 翻转整个字符串
‘nohtyP’
步长为负数时,先翻转然后再每隔步长个字符就提取一个出来。
注意:
1、上述用字符串做演示,实际上列表和元组也是一样的
2、字符串切片后返回的是字符串,同理,列表、元组切片后返回的也是列表、元组。
描述
返回str在seq里面出现的次数。未找到则返回0
示例
seq = ' Python tian \t mao \n taobao '
print(seq.count('o'))
print(seq.count('ao'))
print(seq.count('io'))
4
1
0
描述
返回str在seq里面出现的索引。未找到则报错。rindex表示从右边开始查找。
示例
seq = ' Python tian \t mao \n taobao '
print(seq.index('o'))
print(seq.index('ao'))
print(seq.index('io'))
5
16
Traceback (most recent call last):
File “E:\studypy\tmp.py”, line 4, in
print(s.index(‘io’))
ValueError: substring not found
描述
返回序列的反向序列的迭代器。经过测试,对可迭代对象也生效。
示例
lst = [1, 2, 4]
re = reversed(lst)
print(re)
print(lst)
print(list(re))
[1, 2, 4]
[4, 2, 1]
描述
根据 iterable 中的项返回一个新的已排序列表。具有两个可选参数,它们都必须指定为关键字参数。
key 指定带有单个参数的函数,用于从 iterable 的每个元素中提取用于比较的键 (例如 key=str.lower)。 默认值为 None (直接比较元素)。
reverse 为一个布尔值。 如果设为 True,则每个列表元素将按反向顺序比较进行排序。
示例1:简单排序
>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
示例2:对元素调用函数排序
>>> sorted(“This is a test string from Andrew”.split(), key=str.lower)
[‘a’, ‘Andrew’, ‘from’, ‘is’, ‘string’, ‘test’, ‘This’]
示例3:将元素的某个索引作为key进行排序
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
r = sorted(student_tuples, key=lambda x: x[2], reverse=True)
print(r)
[(‘john’, ‘A’, 15), (‘jane’, ‘B’, 12), (‘dave’, ‘B’, 10)]
多个关键字的排序
student_tuples = [
('john', 'A', 15),
('john', 'A', 20),
('jane', 'B', 12),
('dave', 'B', 10),
('dave', 'B', 5),
]
r = sorted(student_tuples, key=lambda x: (x[0], x[2]))
print(r)
[(‘dave’, ‘B’, 5), (‘dave’, ‘B’, 10), (‘jane’, ‘B’, 12), (‘john’, ‘A’, 15), (‘john’, ‘A’, 20)]
更多排序的内容可以参照:
Operator 模块
排序