列表操作

为什么切片和区间会忽略最后一个元素

  • 当只有最后一个位置信息时,可以快速看出切片和区间里有几个元素:range(3)和my_list[:3]都返回三个元素
  • 当起止位置信息都可见时,我们可以快速计算出切片和区间长度,用最后一个数减去第一个下标(stop-start)即可
  • 方便用任意一个下标来把序列分割成不重叠的两部分,只要写成my_list[:x]和my_list[x:]就可以

对对象进行切片

s=‘bicycle‘
s[::3]#可以用s[a:b:c]的形式对s在a和b之间以c为间隔取值,c的值还可以负,负值意味着反向取值
‘bye‘
s[::-1]
‘elcycib‘
s[::-2]
‘eccb‘

多维切片和省略

省略的正确书写方法是三个英语句号(...),而不是Unicode码位U+206表示的半个省略号。省略在Python解释器眼中是一个符号。它可以当作切片规范的
一部分,也可以用在函数的参数清单中,比如f(a,...,z),或a[i:...]。在NumPy中,...用作多维数组切片的快捷方式。如果x是四维数组,那么x[i,...]
就是x[i,:,:,:]的缩写

给切片赋值

l=list(range(10))
l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l[2:5]=[20,30]
l
[0, 1, 20, 30, 5, 6, 7, 8, 9]
del l[5:7]
l
[0, 1, 20, 30, 5, 8, 9]
l[3::2]=[11,22]
l
[0, 1, 20, 11, 5, 22, 9]
l[2:5]=100# 如果赋值的对象是一个切片,那么赋值语句的右侧必须是个可迭代对象,即使只有单独一个值,也要把它转换成可迭代的序列
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

~\AppData\Local\Temp/ipykernel_21048/4064895235.py in <module>
----> 1 l[2:5]=100# 如果赋值的对象是一个切片,那么赋值语句的右侧必须是个可迭代对象,即使只有单独一个值,也要把它转换成可迭代的序列


TypeError: can only assign an iterable
l[2:5]=[100]
l
[0, 1, 100, 22, 9]

对序列使用+和*

l=[1,2,3]
l*5
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
5*‘abcd‘
‘abcdabcdabcdabcdabcd‘

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

建立由列表组成的列表

board=[[‘_‘]*3 for i in range(3) ]# 建立一个包含3个列表的列表,被包含的3个列表各自有3个元素
board
[[‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘]]
board[1][2]=‘X‘
board #  把第1行第2列的元素标记为X
[[‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘X‘], [‘_‘, ‘_‘, ‘_‘]]

含有3个指向同一对象的引用的列表是毫无用处的

weird_board=[[‘_‘]*3]*3 # 外面的列表其实包含3个指向同一列表的引用
weird_board
[[‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘]]
weird_board[1][2]=‘0‘
weird_board # 当试图修改第1行第2列的元素时,就立马暴露了列表内的3个引用指向同一个对象的事实
[[‘_‘, ‘_‘, ‘0‘], [‘_‘, ‘_‘, ‘0‘], [‘_‘, ‘_‘, ‘0‘]]

前面犯的错误本质上跟下面的代码犯的错误一样:

row=[‘_‘]*3
board=[]
for i in range(3):
    board.append(row)
print(board)
board[2][0]=‘X‘
print(board)
# 追加同一个行对象(row)3次到游戏板(board)
[[‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘]]
[[‘X‘, ‘_‘, ‘_‘], [‘X‘, ‘_‘, ‘_‘], [‘X‘, ‘_‘, ‘_‘]]

正确的方法相当于下面的代码:

board=[]
for i in range(3):
    row=[‘_‘]*3
    board.append(row)
print(board)
board[2][0]=‘X‘
print(board)
# 每次迭代中都新建了一个列表,作为新的一行(row)被追加到游戏板(board)
# 正如所期待的,只有第2行第1列的元素被修改
[[‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘]]
[[‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘], [‘X‘, ‘_‘, ‘_‘]]

序列的增量赋值

l=[1,2,3]
id(l)# 刚开始时列表的ID
1804782764928
l*=2
l
[1, 2, 3, 1, 2, 3]
id(l)# 运用增量算法后,列表的ID没变,新元素追加到列表上
1804782764928
t=(1,2,3)
id(t)# 元组最开始的ID
1804782776960
t*=2
id(t)# 运用增量算法后新的元组被创建
1804781549888

一个关于+=的谜题

下面的代码运行会发生下面四种情况中的哪一种?
+ t变成(1,2,[30,40])。
+ 因为tuple类型不支持对它的元素赋值,所以会抛出TypeError异常。
+ 以上两个都是
+ 第一个和第二个是对的
t=(1,2,[30,40])
t[2]+=[50,60]
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

~\AppData\Local\Temp/ipykernel_21048/3251923064.py in <module>
      1 t=(1,2,[30,40])
----> 2 t[2]+=[50,60]


TypeError: ‘tuple‘ object does not support item assignment
t # 
(1, 2, [30, 40, 50, 60])
import dis
dis.dis(‘s[a]+=b‘) #s[a]+=b后面的字节码
  1           0 LOAD_NAME                0 (s)
              2 LOAD_NAME                1 (a)
              4 DUP_TOP_TWO
              6 BINARY_SUBSCR
              8 LOAD_NAME                2 (b)
             10 INPLACE_ADD
             12 ROT_THREE
             14 STORE_SUBSCR
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE

6 将s[a]的值存入TOS(Top Of Stack,栈的顶端)

11 计算TOS+=b。这一步能够完成,因为TOS指向的一个可变对象

14 s[a]=TOS赋值。这一步失败,因为s是不可变的元组

list.sort方法和内置函数sorted

fruits=[‘grape‘,‘respberry‘,‘apple‘,‘banana‘]
sorted(fruits) # 新建了一个按照字母排序的字符串列表
[‘apple‘, ‘banana‘, ‘grape‘, ‘respberry‘]
fruits # 原列表并没有变化
[‘grape‘, ‘respberry‘, ‘apple‘, ‘banana‘]
sorted(fruits,reverse=True) # 按照字母降序排序
[‘respberry‘, ‘grape‘, ‘banana‘, ‘apple‘]
sorted(fruits,key=len) 
# 新建一个按照长度排序的字符串列表,因为这个排序算法是稳定的,grape和apple的长度都是5,它们的相对位置跟在原列表中是一样的
[‘grape‘, ‘apple‘, ‘banana‘, ‘respberry‘]
sorted(fruits,key=len,reverse=True)
# 按照长度降序排序的结果。结果并不是上面那个结果的完全反转,因为用到的排序算法是稳定的,在长度一样时,grape和apple的相对位置不会发生改变
[‘respberry‘, ‘banana‘, ‘grape‘, ‘apple‘]
fruits # 直到这一步,原列表fruits都没有任何变化
[‘grape‘, ‘respberry‘, ‘apple‘, ‘banana‘]
fruits.sort() # 对原列表就地排序,返回值None会被控制台忽略
fruits # 此时fruits本身被排序
[‘apple‘, ‘banana‘, ‘grape‘, ‘respberry‘]

用bisect来管理已排序的序列

import bisect
import sys

HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]
NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]
ROW_FMT = ‘{0:2d} @ {1:2d}  {2}{0:<2d}‘


def demo(bisect_fn):
    for needle in reversed(NEEDLES):
        position = bisect_fn(HAYSTACK, needle) # 用特定的bisect函数来计算元素应该出现的位置
        offset = position * ‘  |‘ # 利用该位置来算出需要几个分隔符号
        print(ROW_FMT.format(needle, position, offset)) # 把元素和其应该出现的位置打印出来


if __name__ == ‘__main__‘:
    if sys.argv[-1] == ‘left‘: # 根据命令上最后一个参数来选用bisect函数
        bisect_fn = bisect.bisect_left
    else:
        bisect_fn = bisect.bisect
    print(‘DEMO:‘, bisect_fn.__name__) # 把选定的函数在抬头打印出来
    print(‘haystack ->‘, ‘ ‘.join(‘%2d‘ % n for n in HAYSTACK))
    print(bisect_fn)
    demo(bisect_fn)

DEMO: bisect_right
haystack ->  1  4  5  6  8 12 15 20 21 23 23 26 29 30
<built-in function bisect_right>
31 @ 14    |  |  |  |  |  |  |  |  |  |  |  |  |  |31
30 @ 14    |  |  |  |  |  |  |  |  |  |  |  |  |  |30
29 @ 13    |  |  |  |  |  |  |  |  |  |  |  |  |29
23 @ 11    |  |  |  |  |  |  |  |  |  |  |23
22 @  9    |  |  |  |  |  |  |  |  |22
10 @  5    |  |  |  |  |10
 8 @  5    |  |  |  |  |8 
 5 @  3    |  |  |5 
 2 @  1    |2 
 1 @  1    |1 
 0 @  0  0 

bisect的表现可以从两个方面来调校

首先可以用它的两个可选参数lo和hi来缩小搜寻的范围。lo的默认值是0,hi的默认值是序列的长度,即len()作用于该序列的返回值

其次,bisect函数其实是bisect_right函数的别名,后面还有个姊妹函数叫bisect_left。它们的区别在于,bisect_left返回的插入位置是原序列中
插入元素相等的位置的元素,也就是新元素会被放置于它相等的元素前面,而bisect_right返回的则是跟它相等的元素之后的位置。

根据一个分数,找到它所对应的成绩

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


[grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
# 利用bisect的函数可以在很长的有序序列中作为index的替代,用来更快速地查找一个元素的位置
[‘F‘, ‘A‘, ‘C‘, ‘C‘, ‘B‘, ‘A‘, ‘A‘]

利用bisect.insort插入新元素

insort(seq, item)把变量item插入到序列seq中,并能保持seq的升序顺序

import random
import bisect

SIZE = 7

random.seed(1729)

my_list = []
for i in range(SIZE):
    new_item = random.randrange(SIZE * 2)
    bisect.insort(my_list, new_item)
    print(‘%2d ->‘ % new_item, my_list)
10 -> [10]
 0 -> [0, 10]
 6 -> [0, 6, 10]
 8 -> [0, 6, 8, 10]
 7 -> [0, 6, 7, 8, 10]
 2 -> [0, 2, 6, 7, 8, 10]
10 -> [0, 2, 6, 7, 8, 10, 10]

列表操作

上一篇:vue项目开发中遇到的几个问题


下一篇:1个TCP连接可以发多少个HTTP请求?