《流畅的python》读书笔记(3)

第二章

2.1 内置序列类型概览

python标准库中的序列是由C实现的
按存放数据的类型分类:
容器序列:list、tuple 和 collections.deque 这些序列能存放不同类型的数据。容器序列存放的是它们所包含的任意类型的对象引用,注意,是引用。可以是不连续的
扁平序列:str、bytes、bytearray、memoryview 和 array.array,这类序列只能容纳一种类型。扁平序列存放的是值,而不是引用,它是一段连续的内存空间。它里面只能存放诸如字符,字节和数值这种基础类型
按能否被修改来分类:
可变序列:list、bytearray、array.array、collections.deque 和 memoryview。
不可变序列:tuple、str 和 bytes。

2.2 列表推导和生成器表达式

列表推导:

列表推导是构建列表(list)的快捷方式
列表推导的一个范例如下

#普通for循环构造列表
symbols = '$%^&@!'
codes = []
for symbol in symbols:
	codes.append(ord(symbol))
print(codes)

#用列表推导构造列表
symbols = '$%^&@!'
codes = []
for symbol in symbols:
	codes.append(ord(symbol))
print(codes)

codes = []
codes = [ord(symbol) for symbol in symbols]
print(type(codes))
# 打印:<class 'list'> 可见列表推导生成的就是一个列表
print(codes)
# 打印:[36, 37, 94, 38, 64, 33]

列表推导原则:只用列表推导来创建新的列表,并尽量保持简短
列表推导中的变量泄露问题在Python3中得到了解决,代码如下

x = 'ABC'
dummy = [ord(x) for x in x]
print(x) 
# 打印:'ABC'
print(dummy) 
# 打印:[65, 66, 67]
# x的值被保留了。列表推导也正确创建了列表。在[ord(x) for x in x]中
# x的作用域被限制在了[]中

filter和map能做的事情 列表推导也可以做

symbols = '$¢£¥€¤'

# 列表推导。遍历symbols中的每一个字符,如果某一字符的ascii码 > 127
# 则将其添加进列表中
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
print(beyond_ascii)


# 先用map,调用ord将symbols中的每一个字符转换成ascii码,返回值是转换为ascii码后的列表
# 然后外层嵌套一个filter传一个lambda函数,该函数传一个参数c 返回满足 c > 127 的 c
# 然后用这个函数过滤掉 用map转换后的列表symbols 最后用list再转换成一个列表赋值给beyond_ascii
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))
print(beyond_ascii)

# 显然 在此函数中列表推导表达式可读性更强

#使用列表推导计算笛卡尔积
# 对于每一种颜色的衬衫,都有三种不同类型的尺码
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = [(color, size) for color in colors
						for size in sizes]
print(tshirts)

列表推导的作用只有一个:生成列表

生成器表达式

什么是生成器?
任何一个带有yield语句的函数都是生成器,当你直接调用这个函数时,内部的代码是不会被执行的,只有调用yield里面的next函数才会去执行代码,for循环也就是会自动去调用这个next函数来输出值。
可以理解为一个函数被yield中断了,下载再次调用时继续从上一次中断的位置继续执行代码并返回值。通俗一点就是 “要一个元素,给一个元素,而不是列表那样一次性全部生产出来”

生成器表达式则可以用来创建其他任何类型的序列,用生成器表达式来初始化元组、数组或其他序列类型是更好的选择,这是因为生成器表达式背后遵守了迭代器协议,可以逐个地产出元素,而不是先建立一个完整的列表,然后再把这个列表传递到某个构造函数里。可以更好的节省内存

# 示例2-5用生成器表达式初始化元组和数组
# 生成器表达式的语法与列表推导的语法类似,不过是将方括号换位圆括号 然后返回的是一个生成器
# 书中有这样一段文字:如果生成器表达式是一个函数调用过程中的唯一参数,那么不需要额外再用括号把它
# 围起来,可能是翻译的比较不好理解,个人认为大概意思就是这样的。
# 先介绍tuple()函数: tuple() 函数将序列转换为元组。而内部的生成器表达式:ord(symbol) for symbol in symbols
# 返回的是一个generator,当其作为tuple的参数时会自动迭代生成一个序列 所以 他这句话就是 当这个生成器表达式生成的generator作为
# 一个函数的唯一参数时 不需要加括号
# 更通俗点就是 不需要写成 tp = tuple((ord(symbol) for symbol in symbols))

symbols = '$¢£¥€¤'

print(type(ord(symbol) for symbol in symbols))
# 输出:<class 'generator'>
tp = tuple(ord(symbol) for symbol in symbols)
print(tp)
# 输出:(36, 162, 163, 165, 8364, 164)

import array

# array模块是python中实现的一种高效的数组存储类型。
# 它和list相似,但是所有的数组成员必须是同一种类型,在创建数组的时候,就确定了数组的类型

# array.array的构造方法需要两个参数 因此需要加括号。
# array.array(typecode,[initializer]) --typecode:元素类型代码指定数字的存储方式;
# initializer:初始化器,若数组为空,则省略初始化器
# 其中第一个I代表的是 元素类型为无符号整型 即unsigned int 而 i 代表有符号整型即signed int
ary = array.array('I', (ord(symbol) for symbol in symbols))

print(ary)

# 示例2-6生成器表达式计算笛卡尔积
colors = ['red', 'black']

sizes = ['S', 'M', 'L']

tshirts = ('%s %s' % (c, s) for c in colors
		   for s in sizes)

print(tshirts)
# 这里产生的不是一个列表而是一个生成器
# 打印<generator object <genexpr> at 0x0000028CF46B2DE0>

for tshirt in tshirts:
	print(tshirt)

总结:这一小节充分介绍什么是列表推导,什么是生成器表达式。介绍了他们的作用与优势,但并没有清楚的解释生成器这一概念。可以参考其他大佬所写的有关生成器与迭代器的博客,我的理解也比较浅显。需要更进一步的学习。

发布了32 篇原创文章 · 获赞 6 · 访问量 920

猜你喜欢

转载自blog.csdn.net/EEEEEEcho/article/details/103825352