《流畅的python》
内置序列类型概览
列表推导和生成器表达式
最重要最基础的序列类型应该就是列表(list)了。list事一个可变序列,并且能同时存放不同类型的元素。
列表推导是构建列表(list)的快捷方式,而生成器表达式则可以用来创建其他任何类型的序列。
列表推导
列表推导通常的原则:只用列表推导来创建新的列表,并且尽量保持简短。如果列表推导的代码超过了两行,可能要考虑是不是得用for循环重写了。
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = [(color, size) for color in colors for size in sizes]
print(tshirts)
print('------------------------------------------------------')
tshirts = [(color, size) for size in sizes for color in colors]
print(tshirts)
输出:
[(‘black’, ‘S’), (‘black’, ‘M’), (‘black’, ‘L’), (‘white’, ‘S’), (‘white’, ‘M’), (‘white’, ‘L’)]
[(‘black’, ‘S’), (‘white’, ‘S’), (‘black’, ‘M’), (‘white’, ‘M’), (‘black’, ‘L’),(‘white’, ‘L’)]
生成52张牌的列表:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]
列表推导的作用只有一个:生成列表。
生成器表达式
虽然也可以用列表推导来初始化元祖、数组和其他序列类型,但是生成器表达式是更好的选择。因为生成器表达式背后遵守了迭代器协议,可以逐个地产出元素,而不是先建立一个完整的列表,然后再把这个列表传递到某个构造函数里。生成器表达式显然能够节省内存。
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):
print(tshirt)
输出:
black S
black M
black L
white S
white M
white L
这种方式与上述列表推导不同的是,用到生成器表达式之后,内存里不会留下一个有6个组合的列表,因为生成器表达式会在每次for循环运行时才生成一个组合。
元组
不可变列表,用于没有字段名的记录。
元组其实是对数据的记录:元组中的每个元素都存放了记录中一个字段的数据,外加这个字段的位置。
for循环可以分别提取元组里的元素,也叫做拆包。因为元组中第二个元素对我们没有什么用,所欲它赋值给“_”占位符。
元组拆包
如上图代码5,一个%
运算符就把passport元组里的元素对应到了print函数的格式字符串空档中。
元组拆包可以应用到任何可迭代对象上,唯一的硬性要求是,被可迭代对象中的元素数量必须要跟接受这些元素的元组的空档数一致。除非用*
来表示忽略多余的元素。
最好辨认的元组拆包形式:平行赋值
lax_coordinates = (33.9425, -118.408056)
latitude, longitude = lax_coordinates
print(latitude)
print(longitude)
输出:
33.9425
-118.408056
不使用中间变量交换两个变量的值:
a, b = b, a
用*
运算符把一个可迭代对象拆开作为函数的参数:
divmod(20, 8)
t = (20, 8)
divmod(*t)
用*
运算符来处理剩下的元素:
具名元组
collections.namedtuple
是一个工厂函数,可以用来构建一个带字段名的元组和一个有名字的类。
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
用namedtuple构建的类的实例所消耗的内存跟元组是一样的。
from collections import namedtuple
City = namedtuple('City', 'name country population coordinates')
tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
print(tokyo)
print(tokyo.population)
print(tokyo.coordinates)
print(tokyo[1])
创建一个具名元组需要两个参数:
- 类名
- 类个各个字段的名称(由数个字符串组成的可迭代对象,或由空格分隔开的字段名组成的字符串)
存放子啊对应字段里的数据要以一串参数的形式传入到构造函数中。
可以通过字段名或者位置来获取一个字段的信息。
具名元组的专属属性
from collections import namedtuple
City = namedtuple('City', 'name country population coordinates')
print(City._fields)
LatLong = namedtuple('LatLong', 'lat long')
delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
delhi = City._make(delhi_data)
print(delhi._asdict())
for key, value in delhi._asdict().items():
print(key+':', value)
输出:
_fields
属性是一个包含这个类所有字段名称的元组。
_make
通过接收一个可迭代对象来生成这个类的一个实例,作用同City(*delhi_data)
是一样的。
_asdict()
把具名元组以collections.OrderedDict
的形式返回。
切片
在Python里,像列表(list)、元组(tuple)和字符串(str)这类序列类型都支持切片操作。
切片和区间操作里不包含区间范围的最后一个元素。
li = [10, 20, 30, 40, 50, 60]
print(li[:2])
print(li[2:])
print(li[:3])
print(li[3:])
输出:
对对象进行切片
s = 'bicycle'
print(s[::3])
print(s[::-1])
print(s[::-2])
输出:
s[a:b:c]
:s在a和b之间以c为间隔取值。c为负值表示反向取值。
li = list(range(10))
print(li)
li[2:5] = [20, 30]
print(li)
del li[5:7]
print(li)
li[3::2] = [11, 22]
print(li)
# li[2:5] = 100 TypeError: can only assign an iterable
li[2:5] = [100]
print(li)
如果赋值的对象是一个切片,那么赋值语句的右侧必须是个可迭代的对象li[2:5] = [100]
。
把一个序列复制几分然后再拼接起来,快捷做法:把这个序列乘以一个整数。
li = [1, 2, 3]
print(li*5)
print(5*'abcd')
print(li)
输出:
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
abcdabcdabcdabcdabcd
[1, 2, 3]
+
和*
都不会修改原有的操作对象,而是构建一个全新的序列。