#2、序列构成的数组
##2.1 内置序列类型
容器序列
list、tuple、collections.deque这些序列能存放不同类型的数据。
扁平序列
str、bytes、bytearray、memoryview和array.array,这类序列只能容存放纳一种类型

容器序列存放的是它们所包含的任意类型的对象的引用,而扁平序列里存放的是值而不是引用。换句话说,扁平序列其实是一段连续的内存空间。由此可见扁平序列其实更加紧凑,但是它里面只能存放诸如字符、字节和数值这种基础类型。

可变序列
list、bytearray、array.array、collections.deque和memoryview
不可变序列
tuple、str和bytes

##2.2 列表推导和生成器表达式
列表推导是构建列表的快捷方式,而生成器表达式则可以用来创建其它任何类型的序列。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 示例1
symbols = '12345'
codes = []
for mbols in symbols:
codes.append(ord(mbols))
# [49, 50, 51, 52, 53]
print(codes)

# 示例2
symbols1 = '12345'
codes1 = [ord(mbols1) for mbols1 in symbols1]
# [49, 50, 51, 52, 53]
print(codes1)

列表推导可以帮助我们把一个序列或是其它可迭代类型的元素过滤或是加工,然后再新建一个列表。
###2.2.2 列表推导同filter和map的比较
filter和map合起来能做的事情,列表推导也可以做,而且还不需要借助难以理解和阅读的lambda表达式。

1
2
3
4
5
6
7
8
symbols = '12345'
beyond_ascii1 = [ord(s) for s in symbols if ord(s) > 50]
# [51, 52, 53]
print(beyond_ascii1)

beyond_ascii12 = list(filter(lambda c: c > 50, map(ord, symbols)))
# [51, 52, 53]
print(beyond_ascii12)

###2.2.3 笛卡儿积

1
2
3
4
5
6
7
8
9
10
11
12
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
# 第一种
# tshirts = [(color, size) for color in colors for size in sizes]
# 第二种
# for color in colors:
# for size in sizes:
# print((color, size))
# 第三种
tshirts = [(color, size) for size in sizes \
for color in colors]
print(tshirts)

列表推导的作用只有一个:生成列表。如果想生成其它类型的序列,生成器表达式就派上用场了。
###2.2.4 生成器表达式
生成器表达式背后遵守的是迭代器协议,可以逐个的产出元素,而不是先建立一个完整的列表,然后再把这个列表传递到某个构造函数里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 用生成器表达式初始话元组和数组
symbos = '12345'
result = tuple(ord(symbol) for symbol in symbos)
print(result)

import array
result1 = array.array('I', (ord(symbol1) for symbol1 in symbos))
print(result1)

# 使用生成器表达式计算笛卡儿积
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
for tshirt in ('%s %s' %(c, s) for c in colors for s in sizes):
print(tshirt)

生成器表达式逐个产出元素,从来不会一次性产出一个含有6个T恤样式的列表。
##2.3 元组不仅仅是不可变的列表
元组除了用作不可变列表,它还可用于没有字段名的记录。后者常常被忽略。
###2.3.1 元组和记录

1
2
3
4
5
6
7
8
9
lax_cord = (33.9425, -118.4080)
city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8014)
traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')]
for passport in sorted(traveler_ids):
# 拆包
print("%s / %s" % passport)
# 第二个元素没用时可占位符_,
for country, _ in sorted(traveler_ids):
print(country)

###2.3.2 元组拆包
元组拆包可以应用在任何可迭代对象上,唯一的硬性要求是,被可迭代的对象中的元素数量必须要跟接受这些元素的元组的空档数一致。除非我们用*来表示忽略多余的元素。
最好辨认的元组拆包形式就是平行赋值,也就是说把一个可迭代对象里的元素,一并赋值到由对应的变量组成的元组中。

1
2
3
lax_cord = (33.9425, -118.4080)
latitude, longitude = lax_cord
print(latitude, longitude)

另一个很优雅的写法当属不使用中间变量交换两个变量的值:
b, a = a, b
还可以用*运算符把一个可迭代对象拆开作为函数的参数:

1
2
3
4
5
t = (20, 8)
print(divmod(*t))
# divmod(a // b, a % b)
qutient, reminder = divmod(*t)
print(qutient, reminder)

还有一个元组拆包用法则是让一个函数用元组的形式返回多个值,然后调用函数的代码接受这些返回值。

1
2
3
4
import os
_, filename = os.path.split('/home/lucins/.ssh/idrsa.oub')
# idrsa.oub
print(filename)

除此之外元组拆包中可使用*来处理剩下的元素
函数用*args来获取不确定数量的参数。
在python3里,这个概念被扩展到了平行赋值中:

1
2
3
4
5
6
7
a, b, *rest = range(5)
# 0 1 [2, 3, 4]
print(a, b, rest)

c, d, *rest1 = range(2)
# 0 1 []
print(c, d, rest1)

在平行赋值中,*前缀只能用一个变量名前面,但是这个变量可以出现在赋值表达式的任意位置:

1
2
3
4
5
6
a, *body, c, d = range(6)
# 0 [1, 2, 3] 4 5
print(a, body, c, d)
*head, e, f = range(6)
# [0, 1, 2, 3] 4 5
print(head, e, f)

元组拆包还有个强大的功能,那就是可以应用在嵌套结构中

###2.3.3 嵌套元组拆包
接受表达式的元组可以是嵌套的,例如(a,b,(c,d))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
metro_areas = [
('tokyo', 'jp', 36.556, (35.2, 140.3)),
('deile', 'DJ', 2626, (515.2, 53.2)),
('cue', 'dsa', 5524, (254.2, 45.22))
]
print('{:15} | {:^9} | {:^9}'.format('', 'lat.', 'long.'))
fmt = '{:15} | {:9.4f} | {:9.4f}'
'''
| lat. | long.
tokyo | 35.2000 | 140.3000
deile | 515.2000 | 53.2000
'''
for name, cc, pop, (latude, longude) in metro_areas:
if longude >= 50:
print(fmt.format(name, latude, longude))

###2.3.4 具名元组
collections.namedtuple是一个工厂函数,它可以用来构建一个带字段名的元组和一个有名字的类。
用namedtuple构建的类的实例所消耗的内存跟元组是一样的,因为字段名都被存在对应的类里面。这个实例跟普通实例比起来也要小一些,因为Python不会用__dict__来存放这些实例的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from collections import namedtuple
# 第一种方法 直接传递对应字段值
Card = namedtuple('Card', ['rank', 'suit'])
tokey = Card('tokey', ("25.2", "36.2"))
# Card(rank='tokey', suit=('25.2', '36.2'))
print(tokey)

# 第二种 传入带名字的值
City = namedtuple('City', 'name count cord')
city = City(name="lenk", count=12, cord="test")
# City(name='lenk', count=12, cord='test')
print(city)
# 12
print(city[1])
# _fields属性是一个包含所有字段名称的数组
# ('name', 'count', 'cord')
print(city._fields)

de_date = ('lenkyes', 15, 22)
# _make() 通过一个可迭代对象来生成这个类的实例,作用跟City(*de_date)一样
delhi = City._make(de_date)
# City(name='lenkyes', count=15, cord=22)
print(delhi)
# _asdict()以OrderedDict形式返回
# OrderedDict([('name', 'lenkyes'), ('count', 15), ('cord', 22)])
print(delhi._asdict())

元组是一个很强大的可以当作记录来用数据类型。它的第二个角色则是充当一个不可变的列表

###2.3.5 作为不可变列表的元组

##2.4 切片
在Python里列表、元组、字符串这类序列类型都支持切片
###2.4.1 为什么区间会忽略最后一个元素
在切片和区间操作里,不包含最后一个元素是Python的风格,这个习惯符合Python、C和其它语言里以0作为起始下标的传统。

1
2
3
4
5
l = [10, 20, 30, 40, 50, 60]
# [10, 20]
print(l[:2])
# [30, 40, 50, 60]
print(l[2:])

###2.4.2 对对象切片
我们可以用s[a:b:c]的形式对s在a,b,c之间间隔取值。c的值还可以为负,负值以为着反向取值。

1
2
3
4
5
6
7
8
9
10
s = 'bicycle'
# bcce
print(s[::2]) # 从第一个起 每隔两个
# elcycib
print(s[::-1]) # 倒取所有
# eccb
print(s[::-2]) # 倒取 每隔两天
ls = "test1testtesttest123123132"
sky = slice(0, 6)
print(ls[sky]) # 用slice区间这种方式

a:b:c 这种用法只能作为索引或者下标用在[]中来返回一个切片对象:slice(a, b, c)。

###2.4.3 多维切片和省略
Python内置的序列类型都是一维的,因此它们只支持单一的索引,成对出现的索引是没用的。
省略:…
###2.4.4 给切片赋值
如果把切片放在赋值语句的左边,或把它作为del操作的对象,我们就可以对序列进行嫁接、切除或就地修改操作。

1
2
3
4
5
6
7
8
9
10
a = list(range(10))
a[2:5] = [20, 30]
del a[5:7]
# [0, 1, 20, 30, 5, 8, 9]
print(a)
# 如果赋值对象是一个切片,那么赋值语句必须是可迭代对象。
# 即便只有一个值,也要把它转换可迭代的序列。
a[3:6] = [100]
# [0, 1, 20, 100, 9]
print(a)