7  列表与元组

7.1 序列

序列是一些有序元素的集合,支持索引,切片,连接,重复等操作,具有定位index(),计数count()等方法。

表 7.1: Python内置序列类型
类型(中文) 类型(英文) 字面量示例 可变性 备注
range range range(13) 不可变 成员必须构成等差数列
列表 list [1,2,3] 可变 成员是任意对象
元组 tuple (1,2,3) 不可变 成员是任意对象
字符串 str '三个字' 不可变 成员是字符
字节序列
不可变
bytearray
bytes

b'ABCDE'
可变 成员是单个字节

7.2 序列支持的操作

7.2.1 索引与切片

因为序列中的元素有顺序,所以可通过序号访问序列中的元素。 此序号也称索引(index)或下标。

索引分为正序索引与逆序索引。设序列长度为n,元素按顺序从左至右排列。

  • 正序索引从左至右,范围为 0\sim n-1
  • 逆序索引从右至左,范围为 -1\sim -n
  • 逆序索引加 n,就是相应的正序索引。
图 7.1: 正序索引、逆序索引示意图。

如果使用的索引越界,会引发索引错误(IndexError)

# 正序索引,逆序索引
x = [1, 2, 3]
print(x[1], x[-1])
2 3
注记debug:问题出在哪?
x = [1, 2, 3]
x[3]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[2], line 2
      1 x = [1, 2, 3]
----> 2 x[3]

IndexError: list index out of range

切片(slice)操作截取序列的一部分返回。

  • seq[i:j],截取范围为索引ij,含i不含j
  • seq[i:j:k],截取范围为索引ij,含i不含j,且被截取的相邻元素的索引相差k

学了循环程序结构的 小节 13.1.2 后,你会发现切片中索引的含义与range()的参数含义类似。

  • 切片中省略索引与步长,返回序列的全部对象
  • 切片中索引越界不会报错;
# 选取全部元素
s = '岂曰无衣与子同袍'
print(s[::])
岂曰无衣与子同袍
# 切片的索引越界不会报错
# 使用超出索引上界的数字,效果相当于使用索引上界;
print(s[3:30])

# 使用小于索引下界的数字,效果相当于使用索引下界。
print(s[-100:2])
衣与子同袍
岂曰
# 下面返回逆序列表
# 因为s[i:j:k]中省略i,j时,i,j分别是序列的端点
# 至于谁是哪个端点,取决于k的符号
print(s[::-1] )
袍同子与衣无曰岂
注记例:序列索引与切片
s = '五星出东方利中国'
s0 = s[5]
s1 = s[2:-2]
s2 = s[1:10:3]
print(s0)
print(s1)
print(s2)
利
出东方利
星方国

7.2.2 拼接与重复

  • +连接相同类型的序列;
  • *与整数n将一个序列重复n次;
x = [1, 2, 3]
y = [4, 5, 6]
print(x + y)
print(x * 2)
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 1, 2, 3]
注记拓展:range不支持拼接与重复

由于range对象的元素必须构成等差数列,因此range对象不支持拼接与重复。

range(10) + range(3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[8], line 1
----> 1 range(10) + range(3)

TypeError: unsupported operand type(s) for +: 'range' and 'range'
2 * range(3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 2 * range(3)

TypeError: unsupported operand type(s) for *: 'int' and 'range'

7.2.3 成员关系判断

in, not in用于判断对象是否是序列成员:

  • 对于列表,元组,range,in, not in判断一个对象是否与这个序列中的某个元素值相等。
  • 对于字符串,字节序列,in, not in判断前序列是否是后序列的子序列。
# in 判断对象是否与某个序列成员具有相同的值
print(3 in [1,2,3])
print([3] in [1,2,3])
print([1,2,3] in [[1,2,3],[4,5]])
True
False
True
# in 判断前字符串是否是后字符串的子字符串
print('王婆婆' in '王婆婆和白婆婆坐在一块石头上')
True
x = list('王婆婆')
y = list('王婆婆和白婆婆')
print(x)
print(y)
print(x in y)
['王', '婆', '婆']
['王', '婆', '婆', '和', '白', '婆', '婆']
False

7.2.4 比较

相同类型的序列对象可以用比较运算符进行比较,比较依据是字典序。 比较运算的优先级与innot in相同。

很多时候我们只需要用seq1 == seq2比较两个序列是否相等。 当且仅当两个序列的长度相等、对应索引处的元素的值也相等时,seq1 == seq2才返回True

注记拓展:序列比较

思考:下列代码的运行结果为何如此?

x = [1,2,3]
y = (1,2,3)
x < y
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[13], line 3
      1 x = [1,2,3]
      2 y = (1,2,3)
----> 3 x < y

TypeError: '<' not supported between instances of 'list' and 'tuple'
x = [1,2,3]
y = (1,2,3)
x == y
False
range(3) == list(range(3))
[0, 1, 2] == list(range(3))
False
True

7.2.5 序列解包赋值

序列解包赋值:赋值符号=右端是一个长度为n的序列时,=左端可以是至多n个由逗号,分隔的标识符:

  • 标识符的数量与序列长度相同时,标识符依次与序列元素绑定。
  • 标识符中可以有至多当一个标识符前面具有星号*时,标识符的数量可以小于序列长度,此时带*的标识符绑定到一个列表,这个列表的元素是原序列对应位置的多个对象。
a, b, c=[1, 2, 3]
# 仅在解包赋值时c前面需要加*
# 赋值完成后,使用c不要加*
a, b, *c, d=range(5)
print(a, b, c, d)
0 1 [2, 3] 4
# 只能有一个标识符带*
# 否则程序无法决定原序列元素该如何在标识符间分配
a, b, *c, *d=range(5)
  Cell In[18], line 3
    a, b, *c, *d=range(5)
    ^
SyntaxError: multiple starred expressions in assignment

7.2.6 其他序列函数与方法

下面用seq表示序列/可迭代对象。

  1. 函数
  • len(seq)返回序列长度。
  • max(seq)返回序列中的最大值,min(seq)返回序列中的最小值。 如果序列中的对象不能用比较运算符比较大小,会报错。 此外,max()min()还可以接受多个同类型的参数,返回这多个参数中的最大/最小者。
  • sum(seq),适用于元素全是数值型数据的序列,返回序列元素和。
  • any(seq)all(seq),适用于包括序列在内的可迭代对象,判断可迭代对象的元素是否全部或部分为True
  • hash(不可变对象),仅用于不可变序列,返回不可变序列的哈希值。
  1. 方法
  • seq.count(value),返回序列seq中值为value的元素出现的次数。
  • seq.index(value),返回序列seq中值为value首次出现的索引。如果序列中没有值为value的元素,报错ValueError

可以看出,本节的函数与方法的区别在于使用方式: 使用函数的形式是函数名承(序列);使用方法的形式是序列.方法名称(方法参数)

注记序列函数方法使用示例
# max返回序列最大值
import random
max(random.sample(range(1,101), 10))
97
# len返回序列长度
len(range(100))
100
'王婆婆和白婆婆坐在一块石头上'.count('婆')
'王婆婆和白婆婆坐在一块石头上'.index('婆')
4
1

7.3 可变序列支持的操作

本节介绍仅适用于可变序列的操作和方法。

Python内置数据类型中,属于可变序列的只有列表list与可变字节序列bytearray,后者的应用远没有前者广泛。

所以为了减轻认知负荷,初学者完全可以把这一节视为“列表支持的操作”,本节的示例代码也都用了列表。

下表中,s是列表,x是任意对象,t是任意可迭代对象,n是正整数。

表 7.2: 可变序列支持的操作
方法 使用示例
s[i] = x s的第i个元素改为x
s[i:j] = t 将对象加入序列尾部
s[i:j:k] = t 将一个对象插入序列的指定索引位置
del s[i] 删除s的第i个元素
del s[i:j] 删除s相关切片包含的元素
del s[i:j:k] 删除s相关切片包含的元素
s += t 相当于s = s + t
s *= n 相当于s = s * n
# 索引+赋值,修改列表元素
x = [1, 2, 3]
x[1] = 'a'
print(x)
[1, 'a', 3]
x = [1, 2, 3]
y = ['a', 'b', 'c']
x += y
print(x)
[1, 2, 3, 'a', 'b', 'c']
表 7.3: 可变序列支持的方法
方法 使用示例 含义
clear L.clear() 将序列变为空序列
copy L1 = L.copy() 生成对象的浅拷贝
append L.append(value) 将对象加入序列尾部
extend L.extend(iterable) 用可迭代对象iterable的元素扩充序列
insert L.insert(index, value) 将一个对象插入序列的指定索引位置
pop L.pop(index) 返回索引为index的元素
并将此元素从序列中移除
默认索引为-1
remove L.remove(value) 移除序列中第一个具有特定值的元素
reverse L.reverse() 原位反转序列的顺序
# clear的效果
L = [1,2,3]
print(L)
L.clear()
print(L)
[1, 2, 3]
[]
# 浅拷贝的效果1
L = [1,2,3]
print(L, id(L))
L1 = L.copy()
print(L1, id(L1))
[1, 2, 3] 2478428230720
[1, 2, 3] 2478428200704
# 浅拷贝的效果2
L = [[1,2],3]
print(L, id(L))
L1 = L.copy()
print(L1, id(L1))
print(id(L[0]), id(L1[0]))
[[1, 2], 3] 2478428193984
[[1, 2], 3] 2478428230720
2478428226112 2478428226112
# 理解原地改变(in place)
# append()方法改变列表L本身,这种改变是副作用,而非append()的返回值
# append()作为可调用对象的返回值是None
L = [1,2,3]
print(L.append([1,2,3])) # 返回None
print(L) # 返回序列L的值
None
[1, 2, 3, [1, 2, 3]]
# append与extend的区别
# append将对象作为列表的最后一个元素添加
L = [1,2,3]
L.append([1,2,3])
print(f'append的效果:{L}')

# extend用参数将原序列扩展,extend的参数需要是可迭代对象
L = [1,2,3]
L.extend([1,2,3])
print(f'extend的效果:{L}')
append的效果:[1, 2, 3, [1, 2, 3]]
extend的效果:[1, 2, 3, 1, 2, 3]
# extend的参数必须是可迭代对象,否则报错
[1,2,3].extend(1)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[29], line 2
      1 # extend的参数必须是可迭代对象,否则报错
----> 2 [1,2,3].extend(1)

TypeError: 'int' object is not iterable
# pop返回序列中具有指定索引的元素,并将此元素从序列中移除
L = list(range(10))
# pop默认返回并删除最后一个元素
L.pop()
print(f'L.pop后的列表:{L}')
# 指定返回并删除索引为3的元素
L.pop(3)
print(f'L.pop(3)后的列表:{L}')
# 在索引5处插入字符对象'x'
L.insert(5, 'x')
print(f'insert后的列表:{L}')
9
L.pop后的列表:[0, 1, 2, 3, 4, 5, 6, 7, 8]
3
L.pop(3)后的列表:[0, 1, 2, 4, 5, 6, 7, 8]
insert后的列表:[0, 1, 2, 4, 5, 'x', 6, 7, 8]
注记例:班级点名册维护
students = ['张三', '李四', '王五', '赵六', '钱七']
  1. 新来一位同学’孙八’,把他加到名单末尾,打印名单。
  2. ’王五’转学了,把他从名单中删除,打印名单。
  3. insert() 把’周九’插入到第二个位置(索引 1),打印名单。
  4. pop() 弹出名单最后一个同学,打印被弹出的同学名字和剩余名单。
  5. 判断’李四’是否在名单中,打印 '李四在名单中''李四不在名单中'
注记例:班级考试成绩排序统计
scores = [78, 92, 65, 88, 73, 95, 81, 60, 97, 84]
  1. 找出最高分和最低分(用 max()min()),打印结果。
  2. sort() 将成绩从低到高排序,打印排序后的列表。
  3. 统计有多少人及格(≥ 60 分),用循环遍历列表计数。
  4. sorted() 对成绩从高到低排序(不改变原列表),打印排序结果。再打印原列表,确认原列表没有被改变。
  5. count() 统计列表中有几个 90 分及以上的同学。 提示:可以先用循环 + 条件判断逐个检查,也可以尝试其他思路。
注记例:待办清单维护
todo = ['写作业', '复习英语', '跑步 30 分钟', '买牛奶', '给妈妈打电话']
  1. 打印待办清单,每项前面带序号,格式如下:
1. 写作业
2. 复习英语
3. 跑步 30 分钟
4. 买牛奶
5. 给妈妈打电话
  1. pop() 弹出最后一项任务,打印 '已完成:xxx' 和剩余清单。

  2. 在待办清单开头插入一项 '今天交实验报告!'(用 insert),并打印新清单。

  3. reverse() 反转清单顺序,打印结果。再调一次 reverse() 恢复原顺序,打印验证。

注记例:随机生成4副手牌
import random
# 生成不含大小鬼的手牌
suits = [i+str(j) for i in ['H', 'D', 'S','C'] for j in range(1, 14)]
# 原地打乱手牌
random.shuffle(suits)
# 初始化4副手牌
deck1 = []
deck2 = []
deck3 = []
deck4 = []
while len(suits)>0:
    deck1.append(suits.pop())
    deck2.append(suits.pop())
    deck3.append(suits.pop())
    deck4.append(suits.pop())

print(f'第1副牌:{deck1}')
print(f'第2副牌:{deck2}')
print(f'第3副牌:{deck3}')
print(f'第4副牌:{deck4}')
第1副牌:['S1', 'C13', 'H11', 'D9', 'H4', 'C11', 'D5', 'C1', 'H2', 'S10', 'C9', 'D7', 'S8']
第2副牌:['D2', 'D4', 'S3', 'S5', 'D3', 'H3', 'C2', 'C10', 'S7', 'H12', 'S4', 'C3', 'S12']
第3副牌:['H9', 'H8', 'S13', 'C6', 'D1', 'C5', 'H13', 'S2', 'H10', 'S9', 'S11', 'D11', 'C4']
第4副牌:['C7', 'H5', 'D10', 'C8', 'D13', 'H6', 'S6', 'H7', 'D12', 'D8', 'C12', 'D6', 'H1']

7.4 列表

7.4.1 创建列表

有3种方法可创建列表:

  1. 列表字面量。用方括号[]包围的列表的元素,列表元素间用逗号,分隔;[]是空列表。
[1, 2, 3]
[1, 2, 3]
  1. list()list(iterable)将可迭代对象转换为列表,如list(range(3))list()是空列表。

  2. 列表推导式(list comprehension)。此法处理序列中的全部或部分元素、并将处理结果以列表形式返回。

列表推导式被一对方括号 [] 包围,括号中包含至少一个 for 子句,还可以有额外的一个或多个 for 子句、if 子句。 相当于将这些 forif 从左至右嵌套起来,将最内层表达式的计算结果作为列表元素返回。

# 列表推导式
squares = [x**2 for x in range(10)]
print(squares)

# 用循环的等价写法
squares = []
for i in range(10):
    squares.append(i ** 2) 
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[(i, i ** 2) for i in range(10)]
[(0, 0),
 (1, 1),
 (2, 4),
 (3, 9),
 (4, 16),
 (5, 25),
 (6, 36),
 (7, 49),
 (8, 64),
 (9, 81)]
# 带多个for以及if的列表推导式
combs = [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
print(combs)

# 用循环的等价写法
combs = []
for x in [1,2,3]:
    for y in [3,1,4]:
        if x != y:
            combs.append((x, y))
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
注记例:生成扑克牌

用列表,生成一副扑克牌。

除去大小鬼,扑克牌有4种花色:红心(heart),方块(diamond),黑桃(spade),梅花(club);

每种花色有13个点数,见 图 7.2

图 7.2: 除去大小鬼的扑克牌牌面。
# 法1:嵌套循环
suits = []
for i in ['H', 'D', 'S','C']:
    for j in range(1,14):
        suits.append(i+str(j))
print(suits)
['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'H7', 'H8', 'H9', 'H10', 'H11', 'H12', 'H13', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7', 'S8', 'S9', 'S10', 'S11', 'S12', 'S13', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13']
# 法2:列表推导
suits = [i+str(j) for i in ['H', 'D', 'S','C'] for j in range(1, 14)]
print(suits)
['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'H7', 'H8', 'H9', 'H10', 'H11', 'H12', 'H13', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7', 'S8', 'S9', 'S10', 'S11', 'S12', 'S13', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13']

7.4.2 列表方法

  1. list.sort(key = None, reverse = False), 返回排序后的序列。默认升序排列,若reverse = True,则降序排列。 key参数可接受一个函数,将列表元素经过此函数处理后的值作为排序依据。

  2. list.reverse(),返回原序列的逆序序列。

还有两个函数,返回列表,与列表sort()reverse()方法类似:

  1. sorted(iterable, key = None, reverse = False)。与sort()类似,区别在于:
  • API:list.sort()sorted(iterable)
  • 排序对象:list.sort()仅适用于列表;sorted(iterable)适用于任何可迭代对象;
  • 返回类型:sort()对列表原地排序,返回Nonesorted()返回排序后的列表,原可迭代对象不变;
  1. reversed(iterable)reversedreverse的区别,和sortedsort的区别类似。 有一点区别是,reversed返回类型为reversed的可迭代对象,sorted返回列表。
注记例:序列排序
# 列表二各元素是元组
# 将列表元素按照元组的第1个成员降序排名
# key参数接受一个函数做输入
# 这里的函数用lambda定义,是匿名函数,功能是返回函数的输入的第1个元素
grades = [('小朱', 90),('小苟', 70),('小杨', 43)]
grades.sort(key = lambda x: x[1], reverse = True)
print(grades)
[('小朱', 90), ('小苟', 70), ('小杨', 43)]

7.5 元组

元组与列表类似,除了元组是不可变对象,列表是可变对象。

创建元组也有3种方法:

  1. 元组字面量。
  • 元组字面量由逗号,分隔的元素构成,如1,2,3。被打印的元组对象总是由圆括号包围,写元组字面量时外围的圆括号可省略;
  • ()表示空元组;
  • 只有一个元素的元组,用在这个元素后面加,的方式表示,如1,,等价于(1,)
  1. tuple()类,如tuple(range(4))
注记思考:该选用哪种数据类型?

生成扑克牌,可以采用多种数据类型代表一张牌,如字符串,列表和元组。在这个应用中,你认为哪种数据类型更合适?

# 用列表
suits_list = [[i,j] for i in ['H', 'D', 'S','C'] for j in range(1, 14)]
print(suits_list)
[['H', 1], ['H', 2], ['H', 3], ['H', 4], ['H', 5], ['H', 6], ['H', 7], ['H', 8], ['H', 9], ['H', 10], ['H', 11], ['H', 12], ['H', 13], ['D', 1], ['D', 2], ['D', 3], ['D', 4], ['D', 5], ['D', 6], ['D', 7], ['D', 8], ['D', 9], ['D', 10], ['D', 11], ['D', 12], ['D', 13], ['S', 1], ['S', 2], ['S', 3], ['S', 4], ['S', 5], ['S', 6], ['S', 7], ['S', 8], ['S', 9], ['S', 10], ['S', 11], ['S', 12], ['S', 13], ['C', 1], ['C', 2], ['C', 3], ['C', 4], ['C', 5], ['C', 6], ['C', 7], ['C', 8], ['C', 9], ['C', 10], ['C', 11], ['C', 12], ['C', 13]]
# 用元组
suits_tuple = [(i,j) for i in ['H', 'D', 'S','C'] for j in range(1, 14)]
print(suits_tuple)
[('H', 1), ('H', 2), ('H', 3), ('H', 4), ('H', 5), ('H', 6), ('H', 7), ('H', 8), ('H', 9), ('H', 10), ('H', 11), ('H', 12), ('H', 13), ('D', 1), ('D', 2), ('D', 3), ('D', 4), ('D', 5), ('D', 6), ('D', 7), ('D', 8), ('D', 9), ('D', 10), ('D', 11), ('D', 12), ('D', 13), ('S', 1), ('S', 2), ('S', 3), ('S', 4), ('S', 5), ('S', 6), ('S', 7), ('S', 8), ('S', 9), ('S', 10), ('S', 11), ('S', 12), ('S', 13), ('C', 1), ('C', 2), ('C', 3), ('C', 4), ('C', 5), ('C', 6), ('C', 7), ('C', 8), ('C', 9), ('C', 10), ('C', 11), ('C', 12), ('C', 13)]
注记例:购物车管理
# 每个元组中的3个元素分别存储商品名,单价,数量
cart = [
    ('可乐',   3.5,  2),
    ('薯片',   6.0,  1),
    ('矿泉水', 2.0,  3),
]
  1. append()添加一条新商品 ('巧克力', 12.0, 1)
  2. insert()('面包', 5.0, 2) 插入到第二的位置(索引 1)。
  3. remove() 删除矿泉水。
  4. 统计购物车共有几类商品(用 len()),并计算总数量(所有商品数量之和)。
  5. 计算购物车内所有商品的总价。
# 添加巧克力
cart.append(('巧克力', 12.0, 1))
print(f'追加巧克力后:{cart}')
# 插入面包
cart.insert(1, ('面包', 5.0, 2))
print(f'插入面包后:{cart}')
# 移除可乐后
cart.remove(('矿泉水', 2.0,  3))
print(f'移除矿泉水后:{cart}')
# 统计商品类别
print(f'车内共有{len(cart)}类商品')
total_qty = sum([qty for name, price, qty  in cart])
print(f'车内共有{total_qty}件商品')
# 计算总价
total_price = sum(price * qty for name, price, qty in cart)
print(f'商品总价为{total_price}')
追加巧克力后:[('可乐', 3.5, 2), ('薯片', 6.0, 1), ('矿泉水', 2.0, 3), ('巧克力', 12.0, 1)]
插入面包后:[('可乐', 3.5, 2), ('面包', 5.0, 2), ('薯片', 6.0, 1), ('矿泉水', 2.0, 3), ('巧克力', 12.0, 1)]
移除矿泉水后:[('可乐', 3.5, 2), ('面包', 5.0, 2), ('薯片', 6.0, 1), ('巧克力', 12.0, 1)]
车内共有4类商品
车内共有6件商品
商品总价为35.0

7.6 练习

  1. 随机生成20道10以内的加减法题目,不能重复。
  2. 蒙特卡洛模拟。
  • 生成 1\sim5 范围内的随机整数,直到生成质数(2,3,5),记录此时生成的随机数的个数n
  • 重复上述实验1000次,记录1000个n_i, i= 1,2,...,1000
  • 计算1000个 n_i 的平均值 \bar{n} = \sum_{i=1}^{1000}n_i/1000

根据概率论,\bar{n} 会依概率收敛到从 1\sim5 中随机抽样抽到指数的概率3/5。

  1. 黑杰克I。模拟纸牌游戏黑杰克(AKA21点)。

游戏规则:

  • 玩家与庄家初始各有2张牌。玩家的2张牌是明牌,庄家的牌1明1暗。
  • ​玩家根据自己的手牌,可选择加牌或停手。 玩家的目的是取得最接近 21 点数的牌来击败庄家。玩家的点数必须等于或低于21,超过21称为爆牌。
  • 牌中没有大小鬼。2-10 点的牌以牌面的点数计算,J、Q、K 每张为10 点。 如果不会导致爆牌,A=11,否则A=1。 玩家停手后,庄家开始行动。庄家的点数<=16时必须加牌;庄家点数>=17时必须停牌。
  • 输赢判定:爆牌方输;双方都未爆牌时,更接近21点者赢;如果双方都为21点时,一方只有两张牌(一张 A 再加一张价值 10 点的牌,称为黑杰克),另一方的牌多于2张,则黑杰克方赢;其他情况的双方点数相同,是平局。
  1. 黑杰克II。制定一种策略,如自己的手牌点数小于特定点数时,就要牌,最多要牌2次等等。一般庄家的2张牌中,1张对玩家可见,1张牌面未知。策略还可以考虑庄家的明牌。 按照你指定的策略,模拟进行n次牌局。报告n与你赢的频率。

  2. 黑杰克III。思考多个策略,模拟这些策略的胜率。报告各策略及其胜率,找到最优的策略。

  3. 随机取名。 利用姓氏列表与常用汉字组成的字符串,随机生成姓名。

xing = [   '赵','钱','孙','李','周','吴','郑','王','冯','陈',
    '褚','卫','蒋','沈','韩','杨','朱','秦','尤','许',
    '何','吕','施','张','孔','曹','严','华','金','魏',
    '陶','姜','戚','谢','邹','喻','柏','水','窦','章',
    '云','苏','潘','葛','奚','范','彭','郎','鲁','韦',
    '昌','马','苗','凤','花','方','俞','任','袁','柳',
    '酆','鲍','史','唐','费','廉','岑','薛','雷','贺',
    '倪','汤','滕','殷','罗','毕','郝','邬','安','常',
    '乐','于','时','傅','皮','卞','齐','康','伍','余',
    '元','卜','顾','孟','平','黄','和','穆','萧','尹',
    '姚','邵','湛','汪','祁','毛','禹','狄','米','贝',
    '明','臧','计','伏','成','戴','谈','宋','茅','庞',
    '熊','纪','舒','屈','项','祝','董','粱','杜','阮',
    '蓝','闵','席','季','麻','强','贾','路','娄','危',
    '江','童','颜','郭','梅','盛','林','刁','钟','徐',
    '邱','骆','高','夏','蔡','田','樊','胡','凌','霍',
    '虞','万','支','柯','咎','管','卢','莫','经','房',
    '裘','缪','干','解','应','宗','宣','丁','贲','邓',
    '郁','单','杭','洪','包','诸','左','石','崔','吉',
    '钮','龚','程','嵇','邢','滑','裴','陆','荣','翁',
    '荀','羊','於','惠','甄','麴','加','封','芮','羿',
    '储','汲','邴','糜','松','井','段','富','巫','乌',
    '焦','巴','弓','牧','隗','山','谷','车','侯','宓',
    '蓬','全','郗','班','仰','秋','仲','伊','宫','宁',
    '仇','栾','暴','甘','钭','厉','戎','祖','武','符',
    '刘','景','詹','束','龙','叶','幸','司','韶','郜',
    '黎','蓟','薄','印','宿','白','怀','蒲','台','从',
    '鄂','索','咸','籍','赖','卓','蔺','屠','胥','能',
    '苍','双','闻','莘','党','翟','谭','贡','劳','逄',
    '姬','申','扶','堵','冉','宰','郦','雍','郤','璩',
    '桑','桂','濮','牛','寿','通','边','扈','燕','冀',
    '郏','浦','尚','农','温','别','庄','晏','柴','瞿',
    '阎','充','慕','连','茹','习','宦','艾','鱼','容',
    '向','古','易','慎','戈','廖','庚','终','暨','居',
    '衡','步','都','耿','满','弘','匡','国','文','寇',
    '广','禄','阙','东','殴','殳','沃','利','蔚','越',
    '夔','隆','师','巩','厍','聂','晁','勾','敖','融',
    '冷','訾','辛','阚','那','简','饶','空','曾','毋',
    '沙','乜','养','鞠','须','丰','巢','关','蒯','相',
    '查','后','荆','红','游','竺','权','逯','盖','益',
    '桓','公','万','俟','司','马','上官','欧阳','夏侯','诸葛',
    '闻人','东方','赫连','皇甫','尉迟','公羊','澹台','公冶','宗政','濮阳',
    '淳于','仲孙','太叔','申屠','公孙','乐正','轩辕','令狐','钟离','闾丘',
    '长孙','慕容','鲜于','宇文','司徒','司空','亓官','司寇','仉督','子车',
    '颛孙','端木','巫马','公西','漆雕','乐正','壤驷','公良','拓拔','夹谷',
    '宰父','谷粱','晋楚','闫法','汝鄢','涂钦','段干','百里','东郭','南门',
    '呼延','妫海','羊舌','微生','岳帅','缑亢','况後','有琴','梁丘','左丘',
    '东门','西门','商牟','佘佴','伯赏','南宫','墨哈','谯笪','年爱','阳佟']
s = '阿啊哎哀唉埃挨癌矮艾爱碍安氨俺岸按案暗昂凹熬傲奥澳八巴叭吧拔把坝爸罢霸白百柏摆败拜班般颁斑搬板版办半伴扮瓣邦帮膀傍棒包胞宝饱保堡报抱豹暴爆卑杯悲碑北贝备背倍被辈奔本崩逼鼻比彼笔币必毕闭辟碧蔽壁避臂边编蝙鞭扁便变遍辨辩标表别宾滨冰兵丙柄饼并病拨波玻剥播脖伯驳泊勃博搏膊薄卜补捕不布步部擦猜才材财裁采彩踩菜蔡参餐残蚕惨灿仓苍舱藏操曹槽草册侧测策层叉插查茶察差拆柴缠产阐颤昌长肠尝偿常厂场畅倡唱抄超巢朝潮吵炒车扯彻撤尘臣沉陈闯衬称趁撑成呈承诚城乘惩程橙吃池驰迟持匙尺齿斥赤翅充冲虫崇抽仇绸愁筹酬丑瞅臭出初除厨础储楚处触川穿传船喘串窗床晨创吹垂锤春纯唇醇词瓷慈辞磁雌此次刺从匆葱聪丛凑粗促催脆翠村存寸措错搭达答打大呆代带待袋逮戴丹单担胆旦但诞弹淡蛋氮当挡党荡刀导岛倒蹈到盗道稻得德的灯登等邓凳瞪低堤滴迪敌笛底抵地弟帝递第颠典点电店垫淀殿雕吊钓调掉爹跌叠蝶丁叮盯钉顶订定丢东冬懂动冻洞都斗抖陡豆督毒读独堵赌杜肚度渡端短段断锻堆队对吨敦蹲盾顿多夺朵躲俄鹅额恶饿鳄恩儿而尔耳二发乏伐罚阀法帆番翻凡烦繁反返犯泛饭范贩方坊芳防妨房肪仿访纺放飞非啡菲肥废沸肺费分纷芬坟粉份奋愤粪丰风枫封疯峰锋蜂冯逢缝凤奉佛否夫肤孵弗伏扶服浮符幅福辐蝠抚府辅腐父付妇负附复赴副傅富赋腹覆该改钙盖溉概干甘杆肝赶敢感刚岗纲缸钢港高搞稿告戈哥胳鸽割歌阁革格葛隔个各给根跟更耕工弓公功攻供宫恭巩拱共贡勾沟钩狗构购够估咕姑孤菇古谷股骨鼓固故顾瓜刮挂拐怪关观官冠馆管贯惯灌罐光广归龟规硅轨鬼柜贵桂滚棍郭锅国果裹过哈孩海害含函寒韩罕喊汉汗旱杭航毫豪好号浩耗呵喝合何和河核荷盒贺褐赫鹤黑嘿痕很狠恨哼恒横衡轰哄红宏洪虹鸿侯喉猴吼后厚候乎呼忽狐胡壶湖葫糊蝴虎互户护花华哗滑化划画话桦怀淮坏欢还环缓幻唤换患荒慌皇黄煌晃灰恢挥辉徽回毁悔汇会绘惠慧昏婚浑魂混活火伙或货获祸惑霍击饥圾机肌鸡积基迹绩激及吉级即极急疾集辑籍几己挤脊计记纪忌技际剂季既济继寂寄加夹佳家嘉甲贾钾价驾架假嫁稼尖坚间肩艰兼监减剪检简碱见件建剑健舰渐践鉴键箭江姜将浆僵疆讲奖蒋匠降交郊娇浇骄胶焦礁角脚搅叫轿较教阶皆接揭街节劫杰洁结捷截竭姐解介戒届界借巾今斤金津筋仅紧锦尽劲近进晋浸禁京经茎惊晶睛精鲸井颈景警净径竞竟敬境静镜纠究九久酒旧救就舅居局菊橘举矩句巨拒具俱剧惧据距聚卷倦决绝觉掘嚼军君均菌俊峻卡开凯慨刊堪砍看康抗炕考烤靠科棵颗壳咳可渴克刻客课肯坑空孔恐控口扣枯哭苦库裤酷夸跨块快宽款狂况矿亏葵愧溃昆困扩括阔垃拉啦喇腊蜡辣来莱赖兰拦栏蓝篮览懒烂滥郎狼廊朗浪捞劳牢老乐勒雷蕾泪类累冷愣厘梨离莉犁璃黎礼李里哩理鲤力历厉立丽利励例隶粒俩连帘怜莲联廉脸练炼恋链良凉梁粮两亮辆量辽疗聊僚了料列劣烈猎裂邻林临淋磷灵玲凌铃陵羚零龄领岭令另溜刘流留硫瘤柳六龙笼隆垄拢楼漏露卢芦炉鲁陆录鹿碌路驴旅铝履律虑率绿氯滤卵乱掠略伦轮论罗萝逻螺裸洛络骆落妈麻马玛码蚂骂吗嘛埋买迈麦卖脉蛮满曼慢漫忙芒盲茫猫毛矛茅茂冒贸帽貌么没枚玫眉梅媒煤霉每美妹门闷们萌盟猛蒙孟梦弥迷谜米泌秘密蜜眠绵棉免勉面苗描秒妙庙灭民敏名明鸣命摸模膜摩磨蘑魔抹末沫陌莫漠墨默谋某母亩牡姆拇木目牧墓幕慕穆拿哪内那纳娜钠乃奶奈耐男南难囊恼脑闹呢嫩能尼泥你拟逆年念娘酿鸟尿捏您宁凝牛扭纽农浓弄奴努怒女暖挪诺哦欧偶爬帕怕拍排牌派攀盘判叛盼庞旁胖抛炮跑泡胚陪培赔佩配喷盆朋棚蓬鹏膨捧碰批披皮疲脾匹屁譬片偏篇骗漂飘瓢票拼贫频品平评凭苹屏瓶萍坡泼颇婆迫破剖扑铺葡蒲朴浦普谱七妻栖戚期欺漆齐其奇歧骑棋旗企岂启起气弃汽契砌器恰千迁牵铅谦签前钱潜浅遣欠枪腔强墙抢悄敲乔桥瞧巧切茄且窃亲侵秦琴禽勤青氢轻倾清情晴顷请庆穷丘秋蚯求球区曲驱屈躯趋取娶去趣圈全权泉拳犬劝券缺却雀确鹊裙群然燃染嚷壤让饶扰绕惹热人仁忍认任扔仍日绒荣容溶熔融柔肉如儒乳辱入软锐瑞润若弱撒洒萨塞赛三伞散桑嗓丧扫嫂色森僧杀沙纱刹砂傻啥晒山杉衫珊闪陕扇善伤商赏上尚梢烧稍少绍哨舌蛇舍设社射涉摄申伸身深神审婶肾甚渗慎升生声牲胜绳省圣盛剩尸失师诗施狮湿十什石时识实拾蚀食史使始驶士氏世市示式事侍势视试饰室是适逝释收手守首寿受兽售授瘦书抒叔枢殊疏舒输蔬熟暑署属鼠薯术束述树竖数刷耍衰摔甩帅双霜爽谁水税睡顺瞬说丝司私思斯撕死四寺似饲松耸宋送颂搜艘苏俗诉肃素速宿塑酸蒜算虽随髓岁遂碎穗孙损笋缩所索锁他它她塌塔踏胎台抬太态泰贪摊滩坛谈潭坦叹炭探碳汤唐堂塘糖躺趟涛掏逃桃陶淘萄讨套特疼腾藤梯踢啼提题蹄体替天添田甜填挑条跳贴铁厅听廷亭庭停蜓挺艇通同桐铜童统桶筒痛偷头投透突图徒涂途屠土吐兔团推腿退吞托拖脱驼妥拓唾挖哇蛙娃瓦歪外弯湾丸完玩顽挽晚碗万汪亡王网往忘旺望危威微为围违唯惟维伟伪尾纬委萎卫未位味胃谓喂慰魏温文纹闻蚊吻稳问翁窝我沃卧握乌污屋无吴吾五午伍武舞务物误悟雾夕西吸希析息牺悉惜晰稀溪锡熙嘻膝习席袭媳洗喜戏系细隙虾瞎峡狭辖霞下吓夏厦仙先纤掀鲜闲弦贤咸衔嫌显险县现线限宪陷献腺乡相香厢湘箱详祥翔享响想向巷项象像橡削消萧硝销小晓孝效校笑些歇协胁斜谐携鞋写泄泻卸屑械谢蟹心辛欣新信兴星猩刑行形型醒杏姓幸性凶兄匈胸雄熊休修羞朽秀绣袖嗅须虚需徐许序叙畜绪续蓄宣玄悬旋选穴学雪血寻巡询循训讯迅压呀鸦鸭牙芽崖哑雅亚咽烟淹延严言岩沿炎研盐颜衍掩眼演厌宴艳验焰雁燕央扬羊阳杨洋仰养氧痒样腰邀摇遥咬药要耀爷也冶野业叶页夜液一伊衣医依仪夷宜姨移遗疑乙已以矣蚁椅义亿忆艺议亦异役抑译易疫益谊逸意溢毅翼因阴音吟银引饮蚓隐印应英婴鹰迎盈营蝇赢影映硬哟拥永泳勇涌用优忧幽悠尤犹由邮油游友有又右幼诱于予余鱼娱渔愉愚与宇羽雨语玉吁育郁狱浴预域欲喻寓御裕遇愈誉豫元员园原圆袁援缘源远怨院愿曰约月岳钥悦阅跃越云匀允孕运晕韵蕴杂砸灾栽宰载再在咱暂赞脏葬遭糟早枣藻灶皂造噪燥躁则择泽责贼怎曾增赠渣扎眨炸摘宅窄债沾粘展占战站张章涨掌丈仗帐胀账障招找召兆赵照罩遮折哲者这浙针侦珍真诊枕阵振镇震争征挣睁蒸整正证郑政症之支汁芝枝知织肢脂蜘执直值职植殖止只旨址纸指趾至志制治质致智置中忠终钟肿种仲众重州舟周洲轴宙皱骤朱株珠诸猪蛛竹烛逐主煮嘱住助注贮驻柱祝著筑抓爪专砖转赚庄桩装壮状撞追准捉桌着仔兹姿资滋籽子紫字自宗综棕踪总纵走奏租足族阻组祖钻嘴最罪醉尊遵昨左作坐座做蔼隘庵鞍黯肮拗袄懊扒芭疤捌跋靶掰扳拌绊梆绑榜蚌谤磅镑苞褒雹鲍狈悖惫笨绷泵蹦匕鄙庇毙痹弊璧贬匾辫彪憋鳖瘪彬斌缤濒鬓秉禀菠舶渤跛簸哺怖埠簿睬惭沧糙厕蹭茬岔豺掺搀禅馋蝉铲猖敞钞嘲澈忱辰铛澄逞秤痴弛侈耻宠畴稠锄雏橱矗揣囱疮炊捶椿淳蠢戳绰祠赐醋簇窜篡崔摧悴粹搓撮挫瘩歹怠贷耽档叨捣祷悼蹬嘀涤缔蒂掂滇巅碘佃甸玷惦奠刁叼迭谍碟鼎董栋兜蚪逗痘睹妒镀缎兑墩盹囤钝咄哆踱垛堕舵惰跺讹娥峨蛾扼鄂愕遏噩饵贰筏矾妃匪诽吠吩氛焚忿讽敷芙拂俘袱甫斧俯脯咐缚尬丐柑竿尴秆橄赣冈肛杠羔膏糕镐疙搁蛤庚羹埂耿梗蚣躬汞苟垢沽辜雇寡卦褂乖棺逛闺瑰诡癸跪亥骇酣憨涵悍捍焊憾撼翰夯嚎皓禾烘弘弧唬沪猾徊槐宦涣焕痪凰惶蝗簧恍谎幌卉讳诲贿晦秽荤豁讥叽唧缉畸箕稽棘嫉妓祭鲫冀颊奸歼煎拣俭柬茧捡荐贱涧溅槛缰桨酱椒跤蕉侥狡绞饺矫剿缴窖酵秸睫芥诫藉襟谨荆兢靖窘揪灸玖韭臼疚拘驹鞠桔沮炬锯娟捐鹃绢眷诀倔崛爵钧骏竣咖揩楷勘坎慷糠扛亢拷铐坷苛磕蝌垦恳啃吭抠叩寇窟垮挎筷筐旷框眶盔窥魁馈坤捆廓睐婪澜揽缆榄琅榔唠姥涝烙酪垒磊肋擂棱狸漓篱吏沥俐荔栗砾痢雳镰敛粱谅晾寥嘹撩缭瞭咧琳鳞凛吝赁躏拎伶聆菱浏琉馏榴咙胧聋窿娄搂篓陋庐颅卤虏赂禄吕侣屡缕峦抡仑沦啰锣箩骡蟆馒瞒蔓莽锚卯昧媚魅氓朦檬锰咪靡眯觅缅瞄渺藐蔑皿闽悯冥铭谬馍摹茉寞沐募睦暮捺挠瑙呐馁妮匿溺腻捻撵碾聂孽拧狞柠泞钮脓疟虐懦糯殴鸥呕藕趴啪耙徘湃潘畔乓螃刨袍沛砰烹彭澎篷坯劈霹啤僻翩撇聘乒坪魄仆菩圃瀑曝柒凄祈脐崎鳍乞迄泣掐洽钳乾黔谴嵌歉呛跷锹侨憔俏峭窍翘撬怯钦芹擒寝沁卿蜻擎琼囚岖渠痊瘸冉瓤壬刃纫韧戎茸蓉榕冗揉蹂蠕汝褥蕊闰腮叁搔骚臊涩瑟鲨煞霎筛删煽擅赡裳晌捎勺奢赦呻绅沈笙甥矢屎恃拭柿嗜誓梳淑赎蜀曙恕庶墅漱蟀拴栓涮吮烁硕嗽嘶巳伺祀肆讼诵酥粟溯隋祟隧唆梭嗦琐蹋苔汰瘫痰谭檀毯棠膛倘淌烫滔誊剔屉剃涕惕恬舔迢帖彤瞳捅凸秃颓蜕褪屯豚臀驮鸵椭洼袜豌宛婉惋皖腕枉妄偎薇巍帷苇畏尉猬蔚瘟紊嗡涡蜗呜巫诬芜梧蜈侮捂鹉勿戊昔犀熄蟋徙匣侠暇馅羡镶宵潇箫霄嚣淆肖哮啸蝎邪挟懈芯锌薪馨衅腥汹锈戌墟旭恤酗婿絮轩喧癣炫绚渲靴薛勋熏旬驯汛逊殉丫押涯衙讶焉阎蜒檐砚唁谚堰殃秧鸯漾夭吆妖尧肴姚窑谣舀椰腋壹怡贻胰倚屹邑绎姻茵荫殷寅淫瘾莺樱鹦荧莹萤颖佣庸咏踊酉佑迂淤渝隅逾榆舆屿禹芋冤鸳渊猿苑粤耘陨酝哉赃凿蚤澡憎咋喳轧闸乍诈栅榨斋寨毡瞻斩盏崭辗栈绽彰樟杖昭沼肇辙蔗贞斟疹怔狰筝拯吱侄帜挚秩掷窒滞稚衷粥肘帚咒昼拄瞩蛀铸拽撰妆幢椎锥坠缀赘谆卓拙灼茁浊酌啄琢咨姊揍卒佐佘赊'

7.7 参考资料

Python官方文档:序列类型