三种基本的序列类型:
- 列表(list)
- 元组(tuple)
- range
iterator
迭代器,用于表示一连串数据流的对象。
重复调用迭代器对象的__next__()
方法(或将迭代器对象传递给内置函数next()
)会逐个返回数据流中的元素。当没有元素可用时会抛出StopIteration
异常。
迭代器对象必须包含__iter__()
方法返回迭代器对象自身,因此迭代器对象默认是可迭代的,可以被用于适用于迭代器对象的大部分场合。
iterable
可迭代的,是指对象具备一次返回一个成员的能力。所有的序列类型的对象都是可迭代的。任何实现了__iter__()
或实现了sequence语义的__getitem__()
的对象也都是可迭代的。
可迭代的对象可以被用在for循环或其他所有需要序列的地方(如zip(), map()等)
当一个可迭代的对象作为参数传递给内置函数iter()时就会返回一个迭代器。
当使用一个可迭代的对象时,通常不需要自己调用iter()生产迭代器然后再处理迭代器。for语句会自动完成这个操作,它创建一个临时的未命名变量用于在循环期间保存迭代器。
1 | 3) range_obj = range( |
判断一个对象是不是可迭代的方式
1 | 1,2,3] tmp_list = [ |
generator
通常指生成器函数,一个返回generator iterator的函数。他与普通函数的不同点在于其包含了yield
表达式用于产生一系列值提供给for循环使用或通过next()函数依此获取。
generator iterator
生成器迭代器,generator 函数所创建的对象。生成器迭代器实质上也是迭代器。
每个 yield
会临时暂停处理,记住当前位置执行状态(包括局部变量和挂起的 try 语句)。当该 生成器迭代器 恢复时,它会从离开位置继续执行(这与每次调用都从新开始的普通函数差别很大)。
- 生成器迭代器使用
yield
关键字实现一次返回一个值给调用者,然后暂停执行,等待下一次调用,好处是节省内存空间。 - 生成器可以用next()函数,也可以用for迭代的方式获取元素值,中间还可以用close()来随时终止生成器
- next()函数每次执行时,都会继续执行挂起的生成器函数,直到执行完毕。
生成器的这种特点被称为”延迟计算”或”惰性求值(Lazy evaluation)”,可以有效的节省内存。惰性求值实际上是体现了协同程序的思想。
虽然生成器的这种行为类似于迭代器,但两者有较大差别,迭代器不具备这种执行-暂停-再执行-再暂停的特性,所以迭代器不具有延迟计算,没有协同程序的思想。
1 | def generator_func(x): |
generator expression
生成器表达式,返回一个生成器迭代器的表达式。他是一种特殊的生成器函数。
格式: (结果 for 变量 in 可迭代对象 if 条件筛选)
生成器表达式如果作为某个函数的参数,则可以省略掉()
生成器表达式和列表推导式的区别:
列表推导式比较消耗内存,一次性加载所有数据。生成器表达式几乎不占用内存,使用的时候才分配和使用内存
得到的值不一样。列表推导式得到的是一个列表。生成器表达式获取的是一个生成器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16def add(n, i):
return n + i
def test():
for i in range(4):
yield i
g = test()
for n in [1, 10]:
# n == 1 时,g = (add(n, i) for i in g),此时 n 没有替换为 1。 惰性机制,不到最后不会拿值
# n == 10时,g = (add(n, i) for i in (add(n, i) for i in g))
# g = (add(n, i) for i in (10, 11, 12, 13))
# g = (10 + 10, 10 + 11, 10 + 12, 10 + 13)
g = (add(n, i) for i in g) # 生成器表达式,在此之上所有的代码都没有进行计算
print(list(g)) # [20, 21, 22, 23]
list comprehension
列表推导式,处理一个序列中的所有或部分元素并返回结果列表的一种紧凑写法。
格式: [结果 for 变量 in 可迭代对象 if 条件筛选]
如[‘{:#04x}’.format(x) for x in range(256) if x % 2 == 0] 将生成一个 0 到 255 范围内的十六进制偶数对应字符串(0x..)的列表。其中 if 子句是可选的,如果省略则 range(256) 中的所有元素都会被处理。
序列操作
range类型不支持序列的s1 + s2
和 s * n
操作
s * n
如果 n 小于0,会按照0来处理,会产生一个空序列
如果s是可修改序列, 注意该操作不会创建序列s的副本,而是创建序列s的引用。这种行为通常不是期望的行为
1 | ori_list = [] |
s1 + s1
对不可变序列(str, bytes等)使用连接操作时,如果有大量(N个)不可变序列需要连接时效率会很慢,原因是需要进行N-1次的内存申请和搬运操作
官方推荐使用str.join()
和bytes.join()
方法替代,在执行join操作时,会首先统计出在list中一共有多少个对象,并统计这些对象所维护的字符串一共有多长,然后申请内存,将list中所有的对象维护的字符串都拷贝到新开辟的内存空间中。这里只进行了一次内存空间的申请,就完成了N个对象的连接操作。相比于 +
操作符,待连接的对象越多,效率的提升也会越明显。
1 | import time |
方式 | 十万数据 | 百万数据 | 千万数据 |
---|---|---|---|
方式一 | 0.05587520000000001 | 3.3037078 | 457.09966000000003 |
方式二 | 0.04598980000000001 | 0.45585239999999994 | 6.734024699999964 |
方式三 | 0.030382799999999988 | 0.3231406999999997 | 3.248821899999996 |
切片操作s[i:j:k]
1 | "Hello World" word = |
创建多维数组
先创建一个所需长度的列表,然后对列表中的所有元素进行填充
1
2
3
4
53, 2 # 创建 3 行 2 列的二维数组 row, column =
0] * column ] * row A = [ [
print(A)
[[0, 0], [0, 0], [0, 0]]
>>>使用numpy库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
163,2], int) # 创建 3行2列的二维数组 a1 = numpy.zeros([
print(a1)
[[0 0]
[0 0]
[0 0]]
>>>
3, 2]) # empty不会初始化为0 a2 = numpy.empty([
print(a2)
[[5.11798224e-307 3.44897992e-307]
[1.69118108e-306 9.34609790e-307]
[6.23037657e-307 8.34445138e-308]]
help(numpy.zeros)
Help on built-in function zeros in module numpy:
zeros(...)
zeros(shape, dtype=float, order='C')
range类型
range是一个内置的class类型,是Sequence[int]
的子类,他是一个不可变的数字序列,通常用于for循环中指定循环次数。
range()返回的对象再很多方面表现的像是list,但是实际上不是。他返回的是一个可迭代的对象。
range相对于list和tuple的优点在于,不管range表示的范围是多大,他总是占用很小的相同大小的内存(仅仅保存start/stop/step三个值)。