序列和数组学习笔记

流畅的Python学习笔记

列表推导是构建列表(list)的快捷方式,生成器表达式则可以用来创建其他任何类型的序列

列表推导

x = 'ABC'
dummy = [ord(x) for x in x] # [65, 66, 67]

列表推导、生成器表达式,以及其他很相似的集合(set)推导和字典(dict)推导,在 Python3 中都有自己的局部作用域。

列表推导和 filtermap 比较

symbols = '$&^%'
beyond_ascii = [ord(s) for s in symbols if ord(s) > 50]
print(beyond_ascii)

beyond_ascii1 = list( filter(lambda c: c > 50, map(ord,symbols)))
print(beyond_ascii1)

不知道是习惯还是什么原因 ,感觉列表推导很好理解,就像读一个句子。 filtermap 不好理解

笛卡尔积

列表推导可以生成两个或两个以上可迭代类型的笛卡尔积。笛卡尔积是一个列表,列表里的元素是由输入的可迭代类型的元素对构成的元祖。因此笛卡尔积
列表的长度等于输入变量的长度的乘积

eg:

colors = ['black','white']
sizes = ['S','M','L']
tshirts = [(color,size) for color in colors 
                        for size in sizes]
# [('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'), ('white', 'M'), ('white', 'L')]
print(tshirts)

生成器表达式

虽然也可以用列表推导来初始化元祖、数组或其他序列类型,但是生成器表达式是更好的选择,因为它背后遵守了迭代器协议,可以诸葛地
产出元素,而不是先建立一个完整的列表,更节省内存。

symbols = '$&^%'
print(tuple(ord(s) for s in symbols))
import array
print(array.array('I',(ord(s) for s in symbols))) # 第一个参数指定数组中数字的存储方式

一个笛卡尔积示例,不会再内存中留下一个6个组合的列表

colors = ['black','white']
sizes = ['S','M','L']
for tshirt in ( '%s %s' % (c,s) for c in colors for s in sizes ):
    print(tshirt)

元祖

元祖拆包

city,year,pop,chg,area = ('Tokyo',2003,32450,0.66,8014)
print(city) # Tokyo
city,_,_,_,_ = ('Tokyo',2003,32450,0.66,8014)
print(city) # Tokyo
city,*rest = ('Tokyo',2003,32450,0.66,8014)
print(city) # Tokyo
print(rest) # [2003, 32450, 0.66, 8014]
city,*rest,area = ('Tokyo',2003,32450,0.66,8014)
print(city,rest,area) # Tokyo [2003, 32450, 0.66] 8014 

嵌套元祖的拆包

string.format 有着丰富的的“格式限定符”(语法是{}中带:号),比如:
填充常跟对齐一起使用
^、<、>分别是居中、左对齐、右对齐,后面带宽度
:号后面带填充的字符,只能是一个字符,不指定的话默认是用空格填充
还有一些精度和进制转换的方法。

metro_areas = [
    ('Tokyo','JP',36.993,(35.689722,139.691667)) ,
    ('Delhi NCR','IN',36.993,(35.689722,139.691667)) ,
    ('Mexico City','MX',36.993,(35.689722,139.691667)) ,
    ('New York','US',36.993,(35.689722,139.691667)) ,
    ('Sao Paulo','BR',-36.993,(35.689722,139.691667)) ,
]

print('{:15} | {:^9} | {:^9}'.format('','lat.','long.'))
fmt = '{:15} | {:9.4f} | {:9.4f}'
for name,cc,pop,(lat,lng) in metro_areas:
    print(fmt.format(name,lat,lng))
    
#                 |   lat.    |   long.  
# Tokyo           |   35.6897 |  139.6917
# Delhi NCR       |   35.6897 |  139.6917
# Mexico City     |   35.6897 |  139.6917
# New York        |   35.6897 |  139.6917
# Sao Paulo       |   35.6897 |  139.6917

具名元祖

collections.namedtuple 是一个工厂函数,它可以用来构建一个带字段名的元祖和一个有名字的类。

from collections import namedtuple
# 字段名可以使空格隔开的字符串 也可以是字符串组成的可迭代对象
City = namedtuple('City','name country population coordinates')
tokyo = City(name='Tokyo',country='JP',population=66.63,coordinates=(35.689722,139.691667))
print(tokyo.name , tokyo.country , tokyo.population , tokyo.coordinates)
# Tokyo JP 66.63 (35.689722, 139.691667)

# 除了从普通元祖集成来的属性之外,具名元祖还有一些自己专有的属性 
print(City._fields)
print(tokyo._asdict())
delhi_data = ('Delhi NCR','IN',21.66,(28.36,135.66))
delhi = City._make(delhi_data)
print(delhi)

# ('name', 'country', 'population', 'coordinates')
# OrderedDict([('name', 'Tokyo'), ('country', 'JP'), ('population', 66.63), ('coordinates', (35.689722, 139.691667))])
# City(name='Delhi NCR', country='IN', population=21.66, coordinates=(28.36, 135.66))

切片

Python 中 listtuplestr 这类序列类型都支持切片操作。

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

我们还可以使用 s[a:b:c] 的形式对s在a和b之间以c为间隔取值 , c 的值还可以为负,意味着反向取值

s = 'bicycle'
print(s[::3]) # bye
print(s[::-1]) # elcycib
print(s[::-2]) # eccb

我们也可以提前定义好切片然后使用

infos = """
Tokyo       35.6897 139.6917 
Delhi NCR   35.6897 139.6917
Mexico City 35.6897 139.6917
New York    35.6897 139.6917
Sao Paulo   35.6897 139.6917"""
city = slice(0,12)
lat = slice(12,20)
lng = slice(20,28)

line_items = infos.split("\n")[1:]
print(line_items)
for item in line_items:
    print(item[city],item[lat] ,item[lng])
    
# ['Tokyo       35.6897 139.6917 ', 'Delhi NCR   35.6897 139.6917', 'Mexico City 35.6897 139.6917', 'New York    35.6897 139.6917', 'Sao Paulo   35.6897 139.6917']
# Tokyo        35.6897  139.6917
# Delhi NCR    35.6897  139.6917
# Mexico City  35.6897  139.6917
# New York     35.6897  139.6917
# Sao Paulo    35.6897  139.6917

为切片赋值

l = list(range(10))
print(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l[2:5] = [20,30]
print(l)
# [0, 1, 20, 30, 5, 6, 7, 8, 9]
del l[5:7]
print(l)
# [0, 1, 20, 30, 5, 8, 9]
l[2:5] = 100 # TypeError: can only assign an iterable

对序列使用 +*

l = [1,2,3]
print(l * 5)

# [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

print(5 * 'abc')

# abcabcabcabcabc

+* 都遵守不修改原有的操作对象,构建一个全新的序列的规律

* 初始化一个由列表组成的列表

# 列表推导的方式
board = [ [''] * 3 for i in range(3) ]
print(board)

# [['', '', ''], ['', '', ''], ['', '', '']]
board[1][2] = 'X'
print(board)
# [['', '', ''], ['', '', 'X'], ['', '', '']]
# 含有3个指向同一个对象的引用列表是毫无用处的
weird_board = [[''] * 3] * 3
print(weird_board)

# [['', '', ''], ['', '', ''], ['', '', '']]
weird_board[1][2] = 'X'
print(weird_board)
# [['', '', 'X'], ['', '', 'X'], ['', '', 'X']]
# 3个都指向同一个引用

board = []
for i in range(3):
    # 这里每次都新建一个列表
    row = [''] * 3
    board.append(row)
print(board)

# [['', '', ''], ['', '', ''], ['', '', '']]
board[1][2] = 'X'
print(board)
# [['', '', ''], ['', '', 'X'], ['', '', '']]

list.sort 和内置 sorted

list.sort 方法会就地排序列表,也就是说不会把原列表复制一份,所以它返回None
sorted 会新建一个列表作为返回值,这个方法可以接受任何形式的可迭代对象作为参数。包括不可变序列和生成器。不管接受什么参数,它都返回一个列表
它们都有两个可选参数 reverse , key 一个只有一个参数的函数,它会被用在每个元素上产生的结果将是排序算法依赖对比的关键字。

fruits = ['grape','raspberry','apple','banana']
print(sorted(fruits))
# ['apple', 'banana', 'grape', 'raspberry']

print(sorted(fruits,reverse=True))
# ['raspberry', 'grape', 'banana', 'apple']
fruits.sort(key=len)
print(fruits)
# ['grape', 'apple', 'banana', 'raspberry']

bisect 管理已排序的序列

bisect 模块包含两个主要函数,bisectinsort, 两个函数都利用二分查找算法来再有序序列中查找插入元素

import bisect
HAYSTACK = [1,3,4,5,6,7,8,9,11,22,45,56,69]
position = bisect.bisect_left(HAYSTACK,2)
print(position) # 1

position = bisect.bisect(HAYSTACK,2)
print(position) # 1

bisect 可以用来建立一个用数字作为索引的查询表格,比如说把分数和成绩对应起来。

def grade(score, breakpoints=[60,70,80,90],grades='FDCBA'):
    i = bisect.bisect(breakpoints,score)
    return grades[i]

print([ grade(score) for score in [33,99,77,70,89,90,100] ])

# ['F', 'A', 'C', 'C', 'B', 'A', 'A']

bisect.insort插入新元素,总能保证seq的升序顺序


import random

SIZE = 7

my_list = []
for i in range(SIZE):
    new_item = random.randrange(SIZE*2)
    bisect.insort(my_list,new_item)
    print('{:2d} -> {}'.format(new_item,my_list))

#  5 -> [5]
#  1 -> [1, 5]
#  0 -> [0, 1, 5]
#  3 -> [0, 1, 3, 5]
# 13 -> [0, 1, 3, 5, 13]
#  7 -> [0, 1, 3, 5, 7, 13]
#  9 -> [0, 1, 3, 5, 7, 9, 13]

当列表不再是首选

当我们要存放1000万个浮点数时,数组(array)的效率要高很多,因为数组在背后存的并不是 float 对象,而是数字的机器翻译
也就是字节表述。如果要频繁对队列进行先进先出的操作,deque (双端队列)的速度应该更快。

数组

创建数组需要一个类型码,这个类型码用来表示在底层 C 语言应该存放怎样的数据类型,如 b 类型码代表的是有符号的字符 (signed char)
Python 不允许你在数组存放指定类型之外的数据

下面代码展示将1000wan个随机浮点数的数组存到文件再读取出来


from array import array
from random import random

floats = array('d',(random() for i in range(10**7)))

fp = open('floats.bin','wb')
floats.tofile(fp)
fp.close()
print(floats[-1])

floats2 = array('d')
fp = open('floats.bin','rb')
floats2.fromfile(fp,10**7)
fp.close()
print(floats2[-1])
print(floats2 == floats)

# 0.6205547382661204
# 0.6205547382661204
# True

内存试图 memoryview

memoryview 是一个内置类,它能让用户在不复制内容的情况下操作同一个数组的不同切片。

from array import array
numbers = array('h' ,[-2,-1,0,1,2])
memv = memoryview(numbers)
print(len(memv)) # 5
print(memv[0])  # -2
memv_oct = memv.cast('B') # 转换为 `B` 类型 无符号字符
print(memv_oct.tolist()) # 以列表的形式看 memv_oct
# [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
memv_oct[5] = 4 # 把位置5的字节赋值成4
# 因为我们把占2个字节的证书的高位字节改成了4所以这个有符号的整数的值变成了 1024 (没看懂 ~~~)
print(numbers)
# array('h', [-2, -1, 1024, 1, 2])

双向队列和其他形式的队列

利用 .append.pop 方法我们可以把列表当做栈和队列来用。但是删除列表第一个元素或者第一个元素之前添加一个元素是很
耗时的,因为这些操作会牵扯到移动列表里的所有元素。

collections.deque 类(双向队列)是一个线程安全的可以快速从两端添加或者删除元素的数据类型,而且如果想要有一种数据类型来
存放"最近用到的几个元素",deque 也是一个很好的选择。这是因为在新建一个双向队列的时候你可以指定这个队列的大小。如果这个队列
满员了,还可以从反向端删除过期的元素,然后再尾端添加新的元素。

from collections import deque

# maxlen 是一个可选参数,代表这个队列可以容纳的元素数量,一旦设定,这个属性就不能修改
dq = deque(range(10),maxlen=10)
print(dq)
# deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)

# 队列的旋转操作接收一个参数n
# n>0时 最右边的n个元素会被移动到左边,n<0,最左边n个元素移动到右边
dq.rotate(3)
print(dq)
# deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
dq.rotate(-4)
print(dq)
# deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
dq.appendleft(-1)
print(dq)
# deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
dq.extend([11,12,13])
print(dq)
# deque([3, 4, 5, 6, 7, 8, 9, 11, 12, 13], maxlen=10)
dq.extendleft([10,20,30,40])
print(dq)
# deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)

你可能感兴趣的:(序列和数组学习笔记)