微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io
如果你发现特殊情况太多,那很可能是用错算法了。
—— Carig Zerouni
目录

前几节我们介绍了Python 中四种数据结构的特性和基本用法,本节介绍与数据结构相关的高级特性。
1,序列
Python 序列是指,其中存放的元素是有序排列 的,可用下标访问,字符串 ,列表 ,元组 都是序列。
而字典 与集合 中的元素是无序排列的,因此一般不归在序列 中。
Python 序列有如下特点:
- 序列中的元素是
不可变 类型
- 序列中的元素可用
下标 访问,下标可正可负
- 可通过
切片 访问部分连续 元素
- 可进行
相加 ,相乘 ,in 运算
- 可通过
for 循环 遍历所有元素
可以使用collections 模块中的Sequence 类来查看一个对象是否是一个序列:
>>> isinstance('',collections.Sequence) # 字符串是序列
True
>>> isinstance([],collections.Sequence) # 列表是序列
True
>>> isinstance((),collections.Sequence) # 元组是序列
True
>>> isinstance({},collections.Sequence) # 字典不是序列
False
>>> isinstance(set(),collections.Sequence) # 集合不是序列
False
提示:
1,isinstance 函数用于查看一个对象属于某个类
2,在使用模块 时,要先import 该模块
2,迭代器
可迭代类型
我们知道str ,list ,tuple ,dict ,set 都可用for 循环 来遍历,这个遍历的过程就是一个迭代 过程,这些类型都是可迭代 类型。
可迭代的类型,都实现了__iter__ 方法,我们通过dir(可迭代对象) ,可以看到,可迭代对象的魔法方法 中都有一个__iter__ 方法。
我们也可以通过collections 模块中的Iterable 类型来查看一个对象是不是可迭代对象:
>>> isinstance('',collections.Iterable)
True
>>> isinstance([],collections.Iterable)
True
>>> isinstance((),collections.Iterable)
True
>>> isinstance({},collections.Iterable)
True
>>> isinstance(set(),collections.Iterable)
True
迭代器
迭代器 是一种可迭代 的对象。
迭代器一定是可迭代的,可迭代的对象不一定是迭代器。
迭代器要实现两个魔法方法:__iter__ 和 __next__ 。
通过collections 模块中的Iterator 类型来查看这两个方法:
>>> dir(collections.Iterator)
判断一个对象是不是迭代器:
>>> isinstance('',collections.Iterator) # 字符串不是迭代器
False
>>> isinstance([],collections.Iterator) # 列表不是迭代器
False
>>> isinstance((),collections.Iterator) # 元组不是迭代器
False
>>> isinstance({},collections.Iterator) # 字典不是迭代器
False
>>> isinstance(set(),collections.Iterator) # 集合不是迭代器
False
迭代器通用函数
迭代器有一些通用函数,下面我们介绍一些常用的。
1.enumerate 函数
在Python3 中,enumerate 实际上是一个类 ,可通过help(enumerate) 查看,也可以把它当做函数来使用。
其经常被用在for 循环 中,即可遍历下标,又能遍历数据。
作用: 用于给一个可迭代 的对象,添加下标 原型: enumerate(iterable[,start]) -> iterator 参数 iterable: 一个可迭代的对象 参数 start: 下标起始位置 返回值: 一个enumerate 对象,同时也是一个迭代器
示例:
>>> l = enumerate(['a','c','b']) # 参数是一个列表
>>> type(l)
<class 'enumerate'>
>>> isinstance(l,collections.Iterator) # 是一个迭代器
True
>>> for index,item in l: # for 循环遍历,能遍历出下标
... print(index,item)
...
0 a
1 c
2 b
2.iter 函数
作用:将一个可迭代的序列iterable 转换成迭代器 原型:iter(iterable) -> iterator 参数:iterable 是一个可迭代的序列 返回值:一个迭代器
示例:
>>> iter('123') # 参数是字符串
<str_iterator object at 0x7fcb7dd320b8> # str 迭代器
>>> iter([1,2,3]) # 参数是列表
<list_iterator object at 0x7fcb7dd4a0b8> # list 迭代器
>>> iter((1,3)) # 参数是元组
<tuple_iterator object at 0x7fcb7dd4a0b8> # tuple 迭代器
>>> iter(set([1,3])) # 参数是集合
<set_iterator object at 0x7fcb7d2c5e10> # set 迭代器
>>> iter({'a':1,'b':2}) # 参数是字典
<dict_keyiterator object at 0x7fcb7f467098> # dict 迭代器
3.next 函数
作用:返回迭代器的下一个元素 原型:next(iterator[,default]) -> item 参数 iterator:是一个迭代器类型 参数 default:任意类型数据,可省 返回值:
当迭代器中有元素 时:返回迭代器中的下一个元素
当迭代器中没有元素 且没有default 参数 时:抛出StopIteration 异常
当迭代器中没有元素 且有default 参数 时:返回default
示例:
>>> i = iter([1,3,5]) # i 是一个迭代器
>>> next(i) # 返回 1
1
>>> next(i) # 返回 3
3
>>> next(i) # 返回 5
5
>>> next(i) # i 中没有元素,抛出异常
Traceback (most recent call last):
File "<stdin>",line 1,in <module>
StopIteration
>>>
>>> next(i,7) # i 中没有元素,返回第二个参数 7
7
4.len 函数
作用:用于计算一个对象obj 中的元素的个数 原型:len(obj,/) 参数:一般obj 是一个可迭代类型对象,实际上,只要实现了__len__ 方法的对象,都可以使用该函数 返回值:一个整数
示例:
>>> len('abc')
3
>>> len([1,3])
3
5.max 函数
作用:返回可迭代对象iterable 中的最大元素 原型:max(iterable) 参数:iterable 是一个可迭代的对象,并且iterable 中的元素可比较 返回值:最大值
示例:
>>> max([1,2])
2
>>> max([1,3])
3
>>> max('b','a')
'b'
>>> max('abc')
'c'
>>> max(1,'a') # 整数与字符串不是同类型,不可比较
Traceback (most recent call last):
File "<stdin>",in <module>
TypeError: '>' not supported between instances of 'str' and 'int'
6.min 函数
作用:返回可迭代对象iterable 中的最小元素 原型:min(iterable) 参数:iterable 是一个可迭代的对象,并且iterable 中的元素可比较 返回值:最小值
示例:
>>> min([1,2])
1
>>> min([2,3])
2
>>> min('abc')
'a'
>>> min('b','c')
'b'
>>> min('b',2) # 整数与字符串不是同类型,不可比较
Traceback (most recent call last):
File "<stdin>",in <module>
TypeError: '<' not supported between instances of 'int' and 'str'
7.sum 函数
作用:计算可迭代对象iterable 中的数据之和,最后在加上 start 原型:sum(iterable,start=0,/) 参数:iterable 是一个可迭代对象,其中的元素是数字类型 返回值:所有元素之和
示例:
>>> sum([1,3]) # 计算 1,2,3 之和
6
>>> sum([1,3],5) # 计算 1,2,3 之和,再加上 5
11
8.reversed 函数
reversed 实际上是一个类,可用help(reversed) 查看。
作用:将一个序列sequence 反转 原型:reversed(sequence) 参数:sequence 是一个序列类型 返回值:一个reversed 对象,该对象也是一个迭代器 ,可被迭代
示例:
>>> r = reversed('abcde')
>>> r # 一个 reversed 对象
<reversed object at 0x7fcb79970518>
>>> isinstance(r,collections.Iterator) # 也是一个迭代器
True
>>> [i for i in r] # 转换成列表,查看其中的元素
['e','d','b','a']
>>>
>>> r = reversed([1,5,7])
>>> [i for i in r]
[7,1]
3,列表生成式
列表生成式 ,又叫列表推导式 ,用于从一个可迭代对象 生成一个列表 ,它是一种代码简写形式,其优点是代码简洁优雅。
比如,我们有一个列表 [1,5] ,想求其中每个元素的平方,再将结果放入列表中,最终的结果是[1,9,25] 。
如果是一般的方式,我们写出来的代码是这样的:
l = [1,5]
l2 = []
for i in l:
item = i * i
l2.append(item)
print(l2)
如果用列表生成式的方式,代码是这样的:
l = [1,5]
l2 = [i * i for i in l]
print(l2)
可以看到列表生成式比普通的形式要简洁许多。
列表生成式语法
最简单的列表生成式的语法,就像上面的代码一样:

列表生成式 由三部分组成:
- 列表符号:中括号
[]
- 表达式:用于生成新列表中的每个元素,一般与
item 有关,也可无关
- for 循环:用于迭代原始
可迭代对象
列表生成式中的if 判断
列表生成式中也可以有if 判断,当判断条件 成立时,才会执行表达式,并将该表达式的结果append 到新的列表中。
这里的if 判断没有else 部分,判断条件 一般与item 有关。
如下:

示例:
l = [1,5]
# 只有当 l 中的元素大于 1 时,才算平方
l2 = [i * i for i in l if i > 1]
print(l2)
range 函数
在Python2.x 中 ,range 是一个函数 。
在Python3.x 中,range 是一个类 ,可用help(range) 查看其手册。
>>> range
<class 'range'>
下面介绍Python3.x 中range 的用法,有两种参数形式:
作用:生成一个从start 到stop 的,步长为step 的,可迭代的整数序列,该序列包含start ,不包含stop ,即遵循左开右闭 原则 原型:
range(stop) -> range object
range(start,stop[,step]) -> range object
参数:
当只有stop 参数时,该序列从0 开始到stop ,步长为1
当有start 和stop 参数时,该序列从start 开始到stop ,步长为1
当有step 参数时,步长为step
返回值:一个range 对象
示例:
>>> range(5)
range(0,5)
>>> range(1,5)
range(1,2)
range(1,2)
range 对象是一个序列,而不是一个迭代器:
>>> isinstance(range(5),collections.Sequence)
True
>>> isinstance(range(5),collections.Iterator)
False
可以使用列表生成式 来查看range 中的内容:
>>> [i for i in range(5)] # 从 0 到 5
[0,1,4]
>>> [i for i in range(1,5)] # 从 1 到 5
[1,2)] # 步长为 2
[1,3]
4,生成器
生成器也是一个迭代器 。生成器跟列表有点像,但优点是比列表节省内存 。
对于列表,Python 会为列表中的每个元素都分配实实在在的内存空间,如果列表中的元素很多,那么列表将消耗大量内存。
而对于生成器,Python 并不会为其中的每个元素都分配内存。
生成器记录的是一种算法,是如何生成数据的算法,在每次用到数据时,才会去生成数据,而不会一开始就将所有的数据准备好。
因此,对于相同的功能,生成器 和列表 都能完成,而生成器可以节省大量的内存。
创建生成器有两种方式:
- 将
列表生成式 中的中括号[] 改为小括号()
- 在函数中使用
yield 关键字
使用列表生成式
如下代码可以生成一个列表:
>>> [i for i in range(5)]
[0,4]
将中括号[] 改为小括号() ,就是一个生成器:
>>> l = (i for i in range(5))
>>> l
<generator object <genexpr> at 0x7f3433d2d9e8>
>>> type(l)
<class 'generator'>
>>> isinstance(l,collections.Iterator) # 生成器也是一个迭代器
True
其中的generator 就是生成器的意思,它也是一个类 。
使用yield 关键字
比如,我们想计算列表[1,5] 中每个元素的平方,使用yield 关键字,代码如下:
#! /usr/bin/env python3
def test():
print('test...')
l = [1,5]
for i in l:
tmp = i * i
print('yield tmp:%s' % tmp)
yield tmp
t = test()
print(t) # <generator object test at 0x7fde1cdaa3b8>
print(type(t)) # <class'generator'>
注意,我们定义了一个函数,这里我们只是为了演示如何使用yield 来创建生成器,而不用过多关注如何定义函数。
函数 的概念我们会在后续章节详细介绍。
执行以上代码,输出如下:
<generator object test at 0x7fde1cdaa3b8>
<class'generator'>
你会发现,字符串test... 并没有被打印出来。
你可能会有疑问,既然代码t = test() 已经执行了,那整个test() 函数中的代码都应该执行完了才对,那字符串test... 怎么会没有打印呢?
那是因为,生成器 中代码的执行是惰性 的。当执行t = test() 这行代码时,test() 函数并没有被执行。
前边我们说过,生成器记录的是一个算法 ,而不是真正的数据,数据只有当使用到的时候,才会去生成。
所以,变量 t 中只是一个算法,而没有数据。
因为,生成器也是一个迭代器,所以生成器可以使用next() 函数来访问其中的数据:
i = next(t)
print('i value is',i)
执行上面这行代码后,程序会输出:
test...
yield tmp:1
i value is 1
字符串test... 被打印,说明test() 执行了。
当代码执行到yield 时,变量i 会接收到yield 返回的数据,此时,代码会从yield 处跳出test() 函数。
如果接下来,不再遍历变量t 中的数据,那么整个代码就执行结束了。
如果我们再次执行代码:
j = next(t)
print('j value is',j)
程序会输出:
yield tmp:9
j value is 9
可见代码会在上次yield 的地方,再次向下执行。
我们再次执行代码:
k = next(t)
print('k value is',k)
程序会输出:
yield tmp:25
k value is 25
当t 中的元素被遍历完后,如果再次执行next(t) ,则会抛出StopIteration 异常。
使用next() 函数来遍历生成器中的所有元素是很麻烦的,我们可以像遍历列表一样,用for 循环 来遍历生成器中的元素:
for i in t:
print(i)
输出如下:
test...
yield tmp:1
1
yield tmp:9
9
yield tmp:25
25
整个遍历过程中,字符串test... 只被输出了一次,并没有被输出三次。
说明当代码执行到yield 时,并没有从函数test() 中返回,代码只是暂停在了yield 处,等下次需要数据时,会接着从上次的yield 处接着执行。
生成器比列表节省内存
如果我们想生成一个从0 到9999 的数字序列,用列表的话,是这样的:
>>> l = [i for i in range(10000)]
>>> sys.getsizeof(l) # 内存占用 87624 字节
87624
sys 模块的getsizeof 方法可以查看一个对象的大小,单位是字节 。
用生成器来实现的话,是这样的:
>>> l = (i for i in range(10000))
>>> sys.getsizeof(l) # 内存占用 88 字节
88
可以看到0 到9999 这样的整数序列,使用列表的话,占用 87624 字节;使用生成器的话,只占用 88 字节
5,强制类型转换
我们已经知道了,Python3 中的str ,list ,tuple ,dict ,set 都是一个类:
>>> type('')
<class 'str'>
>>> type([])
<class 'list'>
>>> type(())
<class 'tuple'>
>>> type({})
<class 'dict'>
>>> type(set())
<class 'set'>
每个类都有构造方法 ,这些构造方法可以将其它类型的数据,转换 成该类型数据,我们也可以将这种转换称为强制类型转换 。
提示:
构造方法 是一个类 的初始化方法,就是用什么样的数据去构造一个特定类的对象。
当介绍到类与对象 时,我们会详细介绍。
str 强制转换
作用: 将其它类型数据转换成字符串 原型: str(object='') -> str 参数: 任意类型数据 返回值: 字符串
示例:
>>> str(1) # 将数字转换成字符串
'1'
>>> str([1,2]) # 将列表转换成字符串
'[1,2]'
>>> str((1,2)) # 将元组转换成字符串
'(1,2)'
list 强制转换
作用: 将一个可迭代类型转成列表 原型: list(iterable) -> list 参数: 任意可迭代类型数据 返回值: 列表
示例:
>>> list((1,2)) # 将一个元组转成列表
[1,2]
>>> list(range(3)) # 将一个 range 对象转成列表
[0,2]
tuple 强制转换
作用: 将一个可迭代类型转成元组 原型: tuple(iterable) -> tuple 参数: 任意可迭代类型数据 返回值: 元组
示例:
>>> tuple([1,2]) # 将一个列表转成元组
(1,2)
>>> tuple(range(3)) # 将一个 range 对象转成元组
(0,2)
dict 强制转换
作用: 将一个可迭代类型数据转成字典 原型: dict(iterable) -> dict 参数: iterable 是一个可迭代对象,并且该对象中的元素是元组 返回值: 字典
示例:
>>> t = ((1,2),(2,3)) # t 是一个元组,其中的元素也是元组
>>> dict(t)
{1: 2,2: 3}
>>>
>>> l = [(1,3)] # l 是一个列表,其中的元素是元组
>>> dict(l)
{1: 2,2: 3}
set 强制转换
作用: 将一个可迭代类型数据转成集合 原型: set(iterable) -> set 参数: 一个可迭代对象 返回值: 集合
其实,我们在介绍集合 时,都是用的这种方式来声明的集合,示例:
>>> set('abc') # 将一个字符串转成集合
{'b','a'}
>>> set([0,1]) # 将一个列表转成集合
{0,1}
>>> set((0,1)) # 将一个元组转成集合
{0,1}
(完。)
推荐阅读:
Python 简明教程 --- 9,Python 编码
Python 简明教程 --- 10,Python 列表
Python 简明教程 --- 11,Python 元组
Python 简明教程 --- 12,Python 字典
Python 简明教程 --- 13,Python 集合
欢迎关注作者公众号,获取更多技术干货。
 (编辑:北几岛)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|