第二章 序列构成的数组

1
2
3
4
5
# 利用列表推导计算2*2笛卡尔积
animals = ['dog','cat','mouse']
ages = [12,32,15]
# 得到的应该是9个元组
[(animal,age) for animal in animals for age in ages ]

列表推导与生成式表达式

列表推导

1
2
3
4
5
# 利用列表推导计算2*2笛卡尔积
animals = ['dog','cat','mouse']
ages = [12,32,15]
# 得到的应该是9个元组
[(animal,age) for animal in animals for age in ages ]
[('dog', 12),
 ('dog', 32),
 ('dog', 15),
 ('cat', 12),
 ('cat', 32),
 ('cat', 15),
 ('mouse', 12),
 ('mouse', 32),
 ('mouse', 15)]
1
2
3

# 输出年龄大于18的
[(animal,age) for animal in animals for age in ages if age>18]
[('dog', 32), ('cat', 32), ('mouse', 32)]
1
2
3
# 以age为排序依据,升序输出
[(animal,age) for age in ages
for animal in animals ]
[('dog', 12),
 ('cat', 12),
 ('mouse', 12),
 ('dog', 32),
 ('cat', 32),
 ('mouse', 32),
 ('dog', 15),
 ('cat', 15),
 ('mouse', 15)]

利用生成器表达式

1
2
3
4
5
6
7
8

# 生成器是惰性的相比与列表表达式,更省空间
# 列表推导作用只有一个:就是生成列表 (当然也可以通过类型转化类实现生成其他类型)
# 用生成器来生成其他类型序列是最好的方式

for genexps in ( "( %s , %s )" %(animal,age) for animal in animals
for age in ages):
print(genexps)
( dog , 12 )
( dog , 32 )
( dog , 15 )
( cat , 12 )
( cat , 32 )
( cat , 15 )
( mouse , 12 )
( mouse , 32 )
( mouse , 15 )
1
2
3
4
5
6
 # 把元组当做记录
lax_coordinator = (33.9425,-118.408056)
city,year,pop,chg,area = ('Tokyo',2003,32450,0.66,8014)
traveler_ids = [('USA','31195855'),('BRA','CE342567'),('ESP','XDA205856'),('ALI','UXE323442')]
for area,_ in sorted(traveler_ids):
print(area)
ALI
BRA
ESP
USA

元组

元组拆包

1
2
3
4

# 将元组内的元素赋给相应变量
student = ('Jack',12375630939)
print( '%s/%s' % student )
Jack/12375630939
1
2
3
4
# 用*处理剩余的元素
student = ('Jack','man','19','2014217025','13275630939')
name,sex,*rest=student
print('%s/%s/%s'%(name,sex,rest))
Jack/man/['19', '2014217025', '13275630939']
1
2
name,*rest,phone=student
print('%s/%s/%s'%(name,rest,phone))
Jack/['man', '19', '2014217025']/13275630939

嵌套元组拆包

1
2
3
4
5
6
7
8
9
10
11

metro_areas = {
('Tokyo','JP',32.4545,(34.232,54.232)),
('Dehi','IN',43.232,(23.545,56.232)),
('NewYork','MX',34.23,(56.232,23.132))
}
print('{:15}|{:^15}|{:^15}'.format('','lat.','long.'))
fmt='{:15}|{:^15.4f}|{:^15.4f}'
# 注意嵌套元组拆包的样式,'(latitude,longitude)'
for name,cc,pop,(latitude,longitude) in metro_areas:
print(fmt.format(name,latitude,longitude))
               |     lat.      |     long.     
Tokyo          |    34.2320    |    54.2320    
Dehi           |    23.5450    |    56.2320    
NewYork        |    56.2320    |    23.1320    

利用逗号快速交换两个元素

1
2
3
4
5
6

a = 'a'
b = 'b'
a,b=b,a
print('{:^10}|{:^10}'.format('a的值','b的值'))
print('{:^10}|{:^10}'.format(a,b))
a的值    |   b的值    
 b     |    a     

具名元组:构建带字段名的元组和有名字的类

1
2
3
4

import collections
Card = collections.namedtuple('card',['rank','suit'])
dir(Card)
['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__module__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmul__',
 '__setattr__',
 '__sizeof__',
 '__slots__',
 '__str__',
 '__subclasshook__',
 '_asdict',
 '_fields',
 '_make',
 '_replace',
 '_source',
 'count',
 'index',
 'rank',
 'suit']
1
2
3
4
5
6
7
8
9
# 下面看看具名元组的形式
from collections import namedtuple
# 构建一个City类,有四个属性 name country population coordinates
# namedtuple 有两个参数,第一个表示类的名字,第二个字符串是类的属性,各个属性用空格区分
City = namedtuple('City','name country population coordinates')

# 创建一个City类实例
Tokyo = City('Tokyo','JP','36.933',(233.12,12.123))
Tokyo
City(name='Tokyo', country='JP', population='36.933', coordinates=(233.12, 12.123))
1
2
3
# 可以用类的方法来调用属性
# 输出name
print(Tokyo.name,Tokyo.coordinates)
Tokyo (233.12, 12.123)
1
2
# 也可以把具名元组当做元组使用,可以利用索引来定位元素
print(Tokyo[1],Tokyo[3])
JP (233.12, 12.123)
1
2
3
# 具名元组除了继承普通元素的方法外,还有一些特殊方法
# 显示具名元组的所有属性
City._fields
('name', 'country', 'population', 'coordinates')
1
2
3
4
# _make() 通过一个可迭代对象(列表/元组)来构建实例
delhi = ('Delhi','In',23.342,(23.43,12.23))
Delhi = City._make(delhi)
Delhi
City(name='Delhi', country='In', population=23.342, coordinates=(23.43, 12.23))
1
2
# _addict() 把具名元组以OrderedDict的形式返回
Delhi._asdict()
OrderedDict([('name', 'Delhi'),
             ('country', 'In'),
             ('population', 23.342),
             ('coordinates', (23.43, 12.23))])
1
Delhi._asdict().items()
odict_items([('name', 'Delhi'), ('country', 'In'), ('population', 23.342), ('coordinates', (23.43, 12.23))])
1
2
3
4
5
for key,value in Delhi._asdict().items():
if isinstance(value,tuple):
print('{:^15}|'.format(key),'%s|%s'%value)
continue
print('{0:^15}|{1:^15}'.format(key,value))
   name      |     Delhi     
  country    |      In       
population   |    23.342     
coordinates  | 23.43|12.23

切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 切片
# 为什么切片和区间会忽略最后一个元素
# 可快速计算区间长度 利用(end - start)
# 当利用seq[start:stop:step]来求值时,实际上是调用__getitem__(slice(start:stop:step))

# 给切片命名,调用
invoice = """
**********十**********十**********十
name sex age
you man 18
"""
name = slice(0,10)
sex = slice(11,21)
age = slice(22,32)
for i in invoice.split('\n'):
print(i)
**********十**********十**********十
name           sex          age
you            man          18
1
2
for item in invoice.split('\n'):
print('{:^15}|{:^15}|{:^15}'.format(item[name],item[sex],item[age]))
             |               |               
**********   |  **********   |  **********   
name         |      sex      |         age   
you          |      man      |         18    
             |               |               
1
2
# name = slice(0,10)
# item[name] ==> item[0:10]
1
2
3
4
5
# 给切片批量赋值
# 赋值语句右侧必须是可迭代对象
l = list(range(10))
l[2:7] = [55,66,77,88,99]
l
[0, 1, 55, 66, 77, 88, 99, 7, 8, 9]
1
2
del l[2:7]
l
[0, 1]

增量赋值

1
2
3
# 对序列使用 +,*
# 不修改原有序列,而是返回一个新的序列
[1,2,3]*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
1
[1,2,3]+[3,4,5]
[1, 2, 3, 3, 4, 5]
1
2
3
4
5
# 建立由列表组成的列表
# 注意以下几个例子的差别
a = ['_']*3 #['_', '_', '_']
a[0]=1
a
[1, '_', '_']
1
2
3
b=[['_']*3]*3 #[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
b[0][0]=1
b
[[1, '_', '_'], [1, '_', '_'], [1, '_', '_']]
1
2
3
c = [['_']]*3
c[0][0]=1
c
[[1], [1], [1]]
1
2
3
4
5
6
# *和+操作符不修改原有对象,而是返回一个新的对象
# 但是下面提到的问题需要格外注意
# a * n 形式的语句中如果a是对其他对象的引用的话,需要格外注意,因为这条语句返回的是n个引用的新序列
# 比如 上面例子中的 c = [['_']]*3 -->[[["_"], ["_"], ["_"]]]
# 若修改c[0][0] =1
# 则结果为[[1], [1], [1]]
1
2
3
4
5
# 由如下代码可知
# 虽然报错,但是tp的值成功更新了
tp = (1,2,[3,4])
tp[2] += [5,6]
tp
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-4-77cfd8df9fc7> in <module>()
      1 
      2 tp = (1,2,[3,4])
----> 3 tp[2] += [5,6]
      4 tp


TypeError: 'tuple' object does not support item assignment
1
tp
(1, 2, [3, 4, 5, 6])
1
2
3
4
5
6
7
8
9
10
11
12
13
# 原因如下
# 在执行tp[2] += [5,6]的时候
# python会将tp[2]的值复制到TOS(top of stack)上,
# 在TOS上可以完成tp[2] += [5,6]
# 但是将结果写回tp中时会报错,因为tuple是不可变序列,但是这一系列的操作不是原子类型
# 因此会出现tp结果被改变,同时报错的情况

# 由此我们可得到如下教训
# 不要将可变对象放入tuple中
# 增量赋值( += )不是一个原子操作
# 如下代码可以实现不报错的情况下修改可变列表
tp[2].extend([55,66,77])
tp
(1, 2, [3, 4, 5, 6, 55, 66, 77])

排序 list.sort 和 sorted

1
2
3
4
5
6
7
# 排序 list.sort 和sorted
# list.sort 是就地排序,sorted 不是
import random
li = []
for x in range(10):
li.append(random.randrange(20))
li
[19, 3, 14, 15, 9, 0, 4, 13, 1, 16]
1
2
li.sort()
li
[0, 1, 3, 4, 9, 13, 14, 15, 16, 19]
1
2
3
# 降序
li.sort(reverse=True)
li
[19, 16, 15, 14, 13, 9, 4, 3, 1, 0]
1
2
3
# 按关键字排序
li.sort(key=lambda x:x%10)
li
[0, 1, 13, 3, 14, 4, 15, 16, 19, 9]
1
2
3
# sorted用法类似
li2 = sorted(li,reverse=True)
li2
[19, 16, 15, 14, 13, 9, 4, 3, 1, 0]

利用bisect处理升序序列

bisect.bisect()

1
2
3
4
5
6
7
8
9
# 利用bisect来管理已升序序列,利用它可以快速插入元素而保证序列有序(升序)
# bisect 主要有两个函数 bisect,insort
# 其中bisect 还有一个孪生方法bisect_left
# 他们的区别在于
import bisect
li = [0,1,1,2]
index = bisect.bisect(li,1)
index_left = bisect.bisect_left(li,1)
print(index,index_left)
3 1
1
2
3
4
5
#  index = bisect(haystack, needle)
# 上面式子意思是
# 在haystack(干草堆上)的index位置插入needle这个元素,并且能保证haystack升序
# 因此可知 bisect是在尽可能右的位置插入-->因此在最后一个1后面插入元素
# bisect_left是在尽可能左的位置插入
1
2
3
4
5
6
#  利用bisect.bisect 来针对升序序列快速插入元素
li = [0,1,1,2,4,5,5,7,7,7,9]
needle = 6
index = bisect.bisect(li,needle)
li.insert(index,needle)
li
[0, 1, 1, 2, 4, 5, 5, 6, 7, 7, 7, 9]

利用bisect.insort() 快速插入元素

1
2
3
# 利用bisect.insort对升序序列插入元素
bisect.insort(li,8)
li
[0, 1, 1, 2, 4, 5, 5, 6, 7, 7, 7, 8, 8, 9]
1
# bisect.insort 性能比 用bisect.bisect来插入元素来的好

其他存储结构与列表的比较

数组array

1
2
3
4
5
6
7
8
9

# 对于容器序列,它们存放的是不同类型数据的引用
# 而对于扁平序列(一段连续的内存空间)存放的是值而不是引用,扁平序列更加紧凑,比如`array`.`array`,`memoryview`,`bytearray`,`bytes`,`str`
# 扁平序列只能存放诸如字符,字节和数值等基本类型

# 数组array
# - 当我们需要一个只用来存储数字的列表时
# - 相比其他类型存储结构更省空间
# - 处理数字
1
2
3
4
5
6
# 创建一个1000万个浮点数组,读写文件
from array import array
from random import random
floats = array('d',(random() for i in range(10**7)))
with open('floats.bin','wb') as f:
floats.tofile(f)
1
2
3
4
5
6
# 本地目录生成一个72.6MB的bin文件
# 读取文件
float2 = array('d')
with open('floats.bin','rb') as f:
float2.fromfile(f,10**7)
float2[-1]
0.41101118294040595
1
floats == float2
True
1
2
3
# 即利用array的tofile,fromfile来对文件进行存储操作
# 数组类型不具有就地排序方法
# 因此只能利用sorted函数进行排序
1
2
3
4
# 对数组进行排序
import random
f = array('d',(random.randrange(20) for i in range(10) ))
f
array('d', [10.0, 7.0, 7.0, 5.0, 13.0, 15.0, 4.0, 10.0, 0.0, 2.0])
1
2
a = sorted(f)
a
[0.0, 2.0, 4.0, 5.0, 7.0, 7.0, 10.0, 10.0, 13.0, 15.0]

线程安全的双向队列deque

1
2
3
4
5
6
# 双向队列 collections.deque
# 线程安全快速从两端插入和删除元素的数据类型
# 实例
from collections import deque
dq = deque(range(10), maxlen=10)
dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
1
2
3
4
# 旋转 .rotate(n)
# 当n>0时,队列最右边n个元素会移动到队列的左边
dq.rotate(-4)
dq
deque([4, 5, 6, 7, 8, 9, 0, 1, 2, 3])
1
2
3
# maxlen 设置最大长度,如果达到最大长度后继续在尾部添加元素,则头部的元素会被删除
dq.append(99)
dq
deque([5, 6, 7, 8, 9, 0, 1, 2, 3, 99])
1
2
3
# 头部4被删除了
dq.appendleft(88)
dq
deque([88, 5, 6, 7, 8, 9, 0, 1, 2, 3])
1
2
3
4
# 可知头部99被删除了
# 在右端添加元素
dq.extend([11,22,33])
dq
deque([7, 8, 9, 0, 1, 2, 3, 11, 22, 33])
1
2
3
# 在左端添加元素
dq.extendleft([44,55,66])
dq
deque([66, 55, 44, 7, 8, 9, 0, 1, 2, 3])
1
2
3
4
5
# 注意由于extendleft()会将迭代器里面的元素逐个添加到左端,因此迭代器中元素会逆序出现在队列中
# 双端队列对两端的插入,删除进行优化,因此对列表中间元素的操作会慢一些
# 弹出右端元素
dq.pop()
dq
deque([66, 55, 44, 7, 8, 9, 0, 1, 2])
1
2
3
# 弹出左端元素
dq.popleft()
dq
deque([55, 44, 7, 8, 9, 0, 1, 2])
1
2
# pop ,append 等都是原子操作,因此deque是线程安全的,deque可以在多线程中当做线程先进先出的栈使用
# 不用当心资源锁的问题

第二章 完!

本文标题:第二章 序列构成的数组

文章作者:定。

发布时间:2017年6月8日 - 16时06分

本文字数:8,996字

原始链接:http://cocofe.cn/2017/06/08/第二章 序列构成的数组/

许可协议: Attribution-NonCommercial 4.0

转载请保留以上信息。