内置序列类型分类
大部分书里面,我们的序列都是被分为可变序列和不可变序列,其中可变序列有list/bytearray/array.array/collections.deque/memoryview,而不可变序列有tuple/str/bytes。
内置序列可以分为容器序列和扁平序列,其中容器序列是它们所包含的任意类型的对象是引用,而扁平序列里存放的是值;而且屏边序列是一段连续的内存空间。其实区分容器序列和扁平序列最简单的方法是容器序列可以存放不同类型的数据,扁平序列只能存放一种类型。容器序列:list/tuple/collections.deque,扁平序列:str/bytes/bytearray/memoryview/array.array。
列表推导
temps = 'asdfg'
codes = []
for temp in temps:
codes.append(ord(temp))
print(codes)
codes1 = [ord(temp) for temp in temps]
print(codes1)
#output
[97, 115, 100, 102, 103]
[97, 115, 100, 102, 103]
观察上面两段输出,输出结果一样,但是第二种写法相对第一种写法简单很多,这个第二种写法就是列表推导式!Ord()用于将字符编程Unicode码位。
我们通常用列表推导式来创建新的列表。
Python2中会出现列表推导式影响上下文的同名变量,python3中不会影响,如下例所示,运行环境为python3.6,表达式内部的变量和赋值只在局部起作用,表达式的上下文里的同名变量还可以正常引用,局部变量不会影响到它们。也就是temps的值一直没变。
temps = 'asdfg'
codes = [ord(temp) for temp in temps]
print(codes)
print(temps)
#output
[97, 115, 100, 102, 103]
asdfg
再举个例子说明列表推导式的用途!
colors = ['black','red']
sizes = ['S','M','L']
tshirts = [(color,size) for color in colors for size in sizes]
print(tshirts)
#output
[('black', 'S'), ('black', 'M'), ('black', 'L'), ('red', 'S'), ('red', 'M'), ('red', 'L')]
生成器表达式
所谓生成器表达式,就是背后遵守了迭代器协议,逐个产出元素,而不是先建立一个完整的列表,然后再把这个列表传递到某个构造函数里。这样就可以节约内存了。
生成器的语法和列表推导式差不多,只是方括号变成了圆括号而已。
colors = ['black','red']
sizes = ['S','M','L']
for tshirt in('{} {}'.format(color,size) for color in colors for size in sizes):
print(tshirt)
#output
black S
black M
black L
red S
red M
red L
元组拆包
traveler_ids = [('USA','123456'),('BRA','789012'),('CAN','345678')]
for country,_ in traveler_ids:
print(country)
#output
USA
BRA
CAN
上例中,我们利用for循环来提取元组中的元素,这个过程就叫做拆包!我们注意到在for语句中有个“_”,这是一个占位符,当我们觉得哪个元素不是我们需要的时候,将其赋值给占位符就好。
元组的拆包可以应用到任何可迭代对象上,只要被迭代对象中的元素必须跟接受这些元素的元组的空档数一致。
country,id = ('USA','123456')
print(country)
print(id)
a = 1
b =2
print(a,b)
a,b = b,a
print(a,b)
#output
USA
123456
1 2
2 1
如上例所示,最好辨认的就是平行赋值,就是将一个可迭代对象里的元素,一并赋值到由对应的变量组成的元组。
第二个就是不实用中间变量来交换两个数的值!这个写法相对于C/Java等语言的写法要简单很多。
t = (20,6)
d = divmod(*t)
print(d)
print(type(d))
print(*d)
#output
(3, 2)
<class 'tuple'>
3 2
上例我们看到输出的d其实是一个元组,通过*号对元组进行了拆包,那么就是说对于divmod函数传入的也是一个元组,但是里面也会对其进行拆包。
利用*号还能获取不确定数量参数。
a,b,*args = range(6)
print(a,b,args)
a,*args,b = range(5)
print(a,args,b)
#output
0 1 [2, 3, 4, 5]
0 [1, 2, 3] 4
还可以嵌套元组拆包!
a,b,c = [1,2,(3,4)]
print(a,b,c)
print(c)
print(*c)
#output
1 2 (3, 4)
(3, 4)
3 4
我们知道**kwargs可以用来接收参数,同样,它也可以用来拆包!
def run(**kwargs):
print(kwargs)
run1(**kwargs)
def run1(a,b):
print(a,b)
if __name__ == "__main__":
run(a=1,b=2)
#output
{'a': 1, 'b': 2}
1 2
其实从上面的拆包过程,*args与**kwargs前面我们知道,前者用于接收元组类型的数据,后者接收字典类型的数据,结合上面的例子,说到底其实两个参数前面的*号就是用来拆包的,没有别的什么用!在接收数据的时候完成拆包!
具名元组
元祖的特点之一除了不可变,还有就是没有字段名记录的,如果元组里的元素有字段名,那么元组不就是字典,是键-值对了吗?的确,我们一般用的元组中的各个元素是没有字段名的,但是还有一种特殊的元组——具名元组。
具名元组是带有字段名元组,虽然说是元组,但是实际上是类的一种。要想构建一个具名元组,需要使用python里的一个工厂函数:collections.namedtuple(typename, field_names, verbose=False, rename=False) .
namedtuple需要两个必要函数,typename是构建的具名元组的元组类型名,field_names是这个具名元组类里的各个字段的名字,名字之间用空格或者逗号隔开,verbose若为True则会打印这个类的详细信息,若rename为True则会把field_names中与python关键字冲突或者重复的字段用这个关键字的位置来替代,如:[‘abc’, ‘def’, ‘ghi’, ‘abc’]会被转换为 [‘abc’, ‘_1’, ‘ghi’, ‘_3’]。
from collections import namedtuple
City = namedtuple('City',"country,capital,population")
beijing = City('china','beijing','13')
#output
City(country='china', capital='beijing', population='13')
从以上代码我们可以看到,我们可以使用namedtuple构建一个类,创建的类就是一个具名元组,在我们初始化一个具名元组的时候就像初始化一个类一样!
切片
在python中,列表、元组、字符串都支持切片。
在切片的过程中,我们一直都是左闭右开的,也就是忽略了最后一个元素!
我们还可以利用s[a:b:c]的形式对s在a和b之间以c为间隔取值,如果c为负,意味着反向取值。
s = "Pycharm"
print(s[::3])
print(s[::-1])
print(s[3::-2])
#output
Phm
mrahcyP
hy
还可以对其进行切片赋值!
l = list(range(10))
print(l)
l[2:4] = [10,20]
print(l)
del l[4:6]
print(l)
l[3::2] = [30,40,50]
print(l)
l[4:7] = [100]
print(l)
#output
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 10, 20, 4, 5, 6, 7, 8, 9]
[0, 1, 10, 20, 6, 7, 8, 9]
[0, 1, 10, 30, 6, 40, 8, 50]
[0, 1, 10, 30, 100, 50]
《流畅的python》