轨道
/
Python
Python
/
练习
/
二十一点
二十一点

二十一点

学习练习

简介

比较

Python 支持以下基本比较运算符:

运算符 运算 说明
> “大于” 若a的值严格大于b,则a > b为True
< “小于” 若a的值严格小于b,则a < b为True
== “等于” 若a的值严格等于b,则a == b为True
>= “大于或等于” 若a的值大于b,或a的值等于b,则a >= b为True
<= “小于或等于” 若a的值小于b,或a的值等于b,则a <= b为True
!= “不等于” 若a == b为False,则a != b为True
is “同一性” 当且仅当a和b是同一个对象时,a is b为True
is not “否定的同一性” 若a和b不是同一个对象,则a is not b为True
in “包含测试” 若a是b的成员、子集或元素,则a in b为True
not in “否定的包含测试” 若a不是b的成员、子集或元素,则a not in b为True

它们的优先级都相同(高于布尔运算,但低于算术运算或位运算)。

不同数据类型之间的比较

类型不同的对象(数字类型除外)默认永远不会比较为相等。 class的非同一实例也不会比较为相等,除非该class定义了特殊的富比较方法,用来自定义默认的object比较行为。 通过rich comparisons进行自定义将在后续练习中介绍。 想了解这个主题的(更多)细节,请参阅 Python 文档中的值比较。

数字类型(大部分情况下)是这条类型匹配规则的例外。 只要类型可以为比较而隐式转换,integer可以被认为等于float(或者octal等于hexadecimal)。

对于 Python 标准库中的其他数字类型(复数、十进制数、分数),比较运算符只在“讲得通”的地方定义(也就是隐式转换不会改变结果的地方),但如果底层对象无法为了比较而准确转换,就会抛出TypeError。 想进一步了解 Python 进行数字转换的规则,请参阅 Python 文档中的算术转换。

>>> import fractions

# A string cannot be converted to an int.
>>> 17 == '17'
False

# An int can be converted to float for comparison.
>>> 17 == 17.0
True

# The fraction 6/3 can be converted to the int 2
# The int 2 can be converted to 0b10 in binary.
>>> 6/3 == 0b10
True

# An int can be converted to a complex number with a 0 imaginary part.
>>> 17 == complex(17)
True

# The fraction 2/5 can be converted to the float 0.4
>>> 0.4 == 2/5
True

>>> complex(2/5, 1/2) == complex(0.4, 0.5)
True

将数字与NaN(非数字)类型进行任何有序比较,结果都是False。 Python 对NaN的定义有一个令人困惑的副作用:NaN永远不会比较为等于NaN。

>>> x = float('NaN')

>>> 3 < x
False

>>> x < 3
False

# NaN never compares equal to NaN
>>> x == x
False

比较字符串

与数字不同,字符串(str)是按字典序比较的,依据的是它们各自的 Unicode 码位(也就是把str中每个码位传给内置函数ord()所得到的结果,该函数返回一个int)。 如果两个字符串中的所有码位都相同,并且顺序也一致,那么这两个字符串就被认为是相等的。 这种比较是“逐对”进行的:第一个对第一个,第二个对第二个,依此类推。 在 Python 3.x 中,str和bytes不能直接强制转换或比较。

>>> 'Python' > 'Rust'
False

>>> 'Python' > 'JavaScript'
True

# Examples with Mandarin.
# hello < goodbye
>>> '你好' < '再见'
True

# ord() of first characters
>>> ord('你'), ord('再')
(20320, 20877)

# ord() of second characters
>>> ord('好'), ord('见')
(22909, 35265)

# And with Korean words.
# Pretty < beautiful.
>>> '예쁜' < '아름다운'
False

>>> ord('예'), ord('아')
(50696, 50500)

比较链

比较运算符可以任意串联,也就是说可以用任意长度、任意组合来使用。 请注意,表达式的求值是从left到right进行的。

举例来说,x < y <= z等价于x < y and y <= z,区别在于y只求值一次。 这两种情况下,一旦发现x < y为False,z就完全不求值。 这通常被称为short-circuit evaluation:如果表达式的真值已经确定,求值就会停止。

Python 中的各种布尔运算符、函数以及比较链都支持Short circuiting。 与包括C、C++、C#和Java在内的许多其他编程语言不同,Python 中像a < b < c这样的链式表达式具有传统的数学解释和优先级。

>>> x = 2
>>> y = 5
>>> z = 10

>>> x < y < z
True

>>> x < y > z
False

>>> x > y < z
False

比较对象同一性

运算符is和is not测试的是对象的同一性,而不是对象的值。 对象的同一性在创建之后永远不会改变,可以通过id()函数获取。

当且仅当id(<apple>) == id(<orange>)时,<apple> is <orange>的求值结果为True。 <apple> is not <orange>则得出相反的结果。

由于None和NotImplemented都是单例,比较它们时应该始终使用is和is not。 关于这一惯例的更多细节,请参阅 Python 参考文档中的值比较和PEP8。

>>> my_fav_numbers = [1, 2, 3]

>>> your_fav_numbers = my_fav_numbers

>>> my_fav_numbers is your_fav_numbers
True

# The returned id will differ by system and python version.
>>> id(my_fav_numbers)
4517478208

# your_fav_numbers is only an alias pointing to the original my_fav_numbers object.
# Assigning a new name does not create a new object.
>>> id(your_fav_numbers)
4517478208


>>> my_fav_numbers is not your_fav_numbers
False

>>> my_fav_numbers is not None
True

>>> my_fav_numbers is NotImplemented
False

成员关系比较

运算符in和not in测试的是成员关系。 如果<fish>是<soup>的成员(也就是<fish>是<soup>的子集,或者包含在<soup>中),<fish> in <soup>的求值结果为True,否则为False。 <fish> not in <soup>返回的是否定结果,也就是<fish> in <soup>的相反结果。

对于字符串和 bytes 类型,当且仅当<name>是<fullname>的子串时,<name> in <fullname>为True。

# A set of lucky numbers.
>>> lucky_numbers = {11, 22, 33}
>>> 22 in lucky_numbers
True

>>> 44 in lucky_numbers
False

# A dictionary of employee information.
>>> employee = {'name': 'John Doe', 
                'id': 67826, 'age': 33, 
                'title': 'ceo'}

# Checking for the membership of certain keys.
>>> 'age' in employee
True

>>> 33 in employee
False

>>> 'lastname' not in employee
True

# Checking for substring membership
>>> name = 'Super Batman'
>>> 'Bat' in name
True

>>> 'Batwoman' in name
False

说明

在本练习中,你将实现二十一点的一些规则,比如游戏的玩法和计分方式。

注意:在本练习中,A 表示 ace,J 表示 jack,Q 表示 queen,K 表示 king。 大小王都被去掉。 这里假定使用的是一副标准的 52 张法式扑克牌,不过大多数玩法会把好几副牌洗在一起使用。

1. 计算一张牌的点数

在二十一点里,A 算 1 点还是 11 点,由每位玩家自己决定(稍后会详细说明)。 人头牌(J、Q、K)记 10 点,其他牌则按其“牌面点数”(数值)计分。

定义一个 value_of_card(<card>) 函数,参数为 card。 函数应返回传入的牌面字符串的_数值_。 由于 A 可以取多个值(1 或 11),这个函数暂时先把 A 的点数固定为 1。 稍后,你会实现一个函数,在已知现有手牌的情况下判断 A 的点数。

>>> value_of_card('K')
10

>>> value_of_card('4')
4

>>> value_of_card('A')
1

2. 判断哪张牌的点数更高

定义一个 higher_card(<card_one>, <card_two>) 函数,参数为 card_one 和 card_two。 就计分而言,J、Q 或 K 的点数都是 10。 函数应返回到计分时点数更高的那张牌。 如果两张牌点数相同,就把两张都返回。 在 return 语句里用逗号,就可以同时返回两张牌:

# Using a comma in a return creates a Tuple.  Tuples will be covered in a later exercise.
>>> def returning_two_values(value_one, value_two):
        return value_one, value_two

>>> returning_two_values('K', '3')
('K', '3')

A 可以取多个值,所以这个任务里我们把A的点数固定为 1。

>>> higher_card('K', '10')
('K', '10')

>>> higher_card('4', '6')
'6'

>>> higher_card('K', 'A')
'K'

3. 计算 A 的点数

如前所述,A 可以算 1 点,也可以_算 11 点。 玩家会尽量接近 21 点,但不能_超过 21 点(超过就叫“爆牌”)。

定义一个 value_of_ace(<card_one>, <card_two>) 函数,参数 card_one 和 card_two 是在拿到 A _之前_手里已有的一组牌。 你的函数要判断即将拿到的这张 A 算 1 点还是 11 点,并返回该值。 记住:加上这张 A 后,手牌的点数要尽可能高,但_不能_超过 21。

提示:如果手里已经有 A,那么即将拿到的这张 A 只能算 1 点。

>>> value_of_ace('6', 'K')
1

>>> value_of_ace('7', '3')
11

4. 判断是否为“天生”或“二十一点”手牌

如果玩家最初拿到的两张牌是一张 A(A)和一张十点牌(10、K、Q 或 J),那么他的点数就是 21。 这就叫二十一点手牌。

定义一个 is_blackjack(<card_one>, <card_two>) 函数,参数 card_one 和 card_two 是一组牌。 判断这两张牌是不是二十一点,是就返回布尔值 True,否则返回 False。

注意:点数的_计算_可以有很多种做法。 但如果可以的话,我们希望你去检查手牌**中**(或者某个特定位置上)是否有一张 A 和一张十点牌,而不是去_累加_手牌的点数。

>>> is_blackjack('A', 'K')
True

>>> is_blackjack('10', '9')
False

5. 分牌

如果手牌的前两张点数相同(比如两张 6,或者一张 Q 和一张 K),玩家可以选择把它们当成两手牌来打。 这就叫“分牌”。

定义一个 can_split_pairs(<card_one>, <card_two>) 函数,参数 card_one 和 card_two 是一组牌。 判断这手两张牌能否拆分成两对。 如果可以分牌,返回布尔值 True,否则返回 False

>>> can_split_pairs('Q', 'K')
True

>>> can_split_pairs('10', 'A')
False

6. 加倍

当最初发到的两张牌总共 9、10 或 11 点时,玩家可以再下一次和原注相同的赌注。 这就叫“加倍”。

定义一个 can_double_down(<card_one>, <card_two>) 函数,参数 card_one 和 card_two 是一组牌。 判断这手两张牌能否“加倍”,可以就返回布尔值 True,否则返回 False。

>>> can_double_down('A', '9')
True

>>> can_double_down('10', '2')
False
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Python Exercism

准备好开始 二十一点 了吗?

注册 Exercism,借助 17 个概念146 个练习 和真人导师指导,学习并掌握 Python,全部免费。