Python奇技淫巧
问题来源于2019Hackergame中一道考察 Python 语言奇技淫巧(各种边界情况)的题,Python版本为3.7.
1 | def challenge_1(self, answer): |
直接输入 'Hello'
即可过关。
1 | def challenge_2(self, answer): |
Python 中 ==
表示判断相等关系,而 is
判断是否为同一个对象。相等并且为同一个对象的例子可以是 None
。一些整数和字符串也可能满足这个性质,但这与实现有关,Python 并不保证这一点。
相等但是不为同一个对象的例子,找一种可变的数据类型即可,例如列表。
一个例子:1, 1, [1], [1]
。
1 | def challenge_3(self, answer): |
这里 in
和 ==
的优先级是什么呢?其实 Python 中比较运算这样连起来写等价于 answer in answer and answer == answer
,正如 1 < x < 2
等价于 1 < x and x < 2
。
随便一个字符串都可以过关。
1 | def challenge_4(self, answer): |
这个小题需要我们找到两个对象,其中一个 *= 2
之后对象本身会改变(和自己的另一个引用相等),而另一个对象 *= 2
之后引用改变(和原来自己的引用不相等)。找一个 mutable 和一个 immutable 即可,例如 [1], 1
。
1 | def challenge_5(self, answer): |
看上去像是 answer = []
,但实际上 reversed([1, 2, 3])
是一个 iterator,遍历一次之后就空了,所以答案是 [3, 2, 1]
。
1 | def challenge_6(self, answer): |
max
函数用对象之间的比较运算来取出最大值。我们可以找 {0}, {1}
这两个集合,Python 中集合的比较运算是判断(真)子集关系,所以 {0} > {1}
和 {1} > {0}
都是 False
,两次 max
结果不同。
1 | def challenge_7(self, answer): |
不满足乘法分配律?Python 中的乘法不一定是对数值进行,字符串和列表也可以和整数做乘法,所以很容易找到 2, 'a', 'b'
这样的解,此时等号两边是 abab
和 aabb
。
1 | def challenge_8(self, answer): |
不满足乘法结合律?也可以和上一题一样用字符串或者列表来构造。这里可以利用它们乘以负数时总是会得到空串的性质,例如一个解是 [0], -1, -1
。
1 | def challenge_9(self, answer): |
需要 a ** b
和 b ** a
的类型不同。这里想要考察,Python 中相同基本类型的运算结果类型,可能与具体的值相关。
(-1) ** 2 = 1
,是 int
类型,而 2 ** (-1) = 0.5
,是 float
类型。若没有判断不能为浮点型也可以用 (-1) ** 0.5
来得到 complex
复数类型。
1 | def challenge_10(self, answer): |
a
非空,并且 a
里面对 b
进行无覆盖的计数结果大于 a
的长度,这看起来不可能,其实 b
是空串的时候就可以。一个可能的解是 'a', ''
。
1 | def challenge_11(self, answer): |
Python 中的函数可以以 max(1, 2, 3)
的形式调用,也可以以 max([1, 2, 3])
的形式调用。当参数个数为 1 时,就会匹配后一种形式。所以我们只需要让 answer
列表里面只有一个元素,这样 max(*answer)
就也会匹配到第二种形式,从而可以做到两个 max
的结果不相同。一个可能的解是 [[0]]
。
1 | def challenge_12(self, answer): |
a
比 b
小,但是 a
中的每个元素都比 b
中对应位置的元素大?看起来不大可能,其实 a
是空列表并且 b
非空的时候就可以做到,因为 a
中根本没有元素,对空集计算 all
会得到 True
。
1 | def challenge_13(self, answer): |
看似 a
和 b
是两个整数,但如果这样的话,我们可以推出 a ^ b == a
,从而 b == 0
,矛盾,这是不可能的。除了整数以外,集合也支持 ^
和 -
运算,很容易找到满足题目中关系的集合,例如 {1}, {1}
。
1 | def challenge_14(self, answer): |
这里 answer[0] += answer[1]
一行执行时需要产生异常,但是却需要 answer
的值发生变化。这是 Python 官方文档 FAQ 中指出的一种特殊情况,让 answer
是 tuple 并且 answer[0]
是 list 就可能触发这种情况,原因是 list 的 +=
运算可以正常进行,但是接下来在 tuple 中更新引用时会失败,tuple 是 immutable 类型。答案可以是 [1], [1]
。
1 | def challenge_15(self, answer): |
看起来似乎需要找一个列表和一个列表中的元素,元素不在列表的最大和最小值之间。但其实 item
和 l
都可以是字符串,此时 item
可以是一个长度大于等于 2 的字符串,而 min(l)
和 max(l)
都是按单个字符计算的。一个可能的答案是 'aa', 'aaa'
。
1 | def challenge_16(self, answer): |
看似不可能。我们可以从 l in l
开始思考,Python 中什么字面量可以 in
它自己呢?可以是字符串或者 bytes
。对 bytes
进行遍历时,元素是 0~255 范围的 int
,所以 233, b'\xe9'
可以满足条件。
1 | def challenge_17(self, answer): |
对 l
取下标 0
得到的元素并不 in l
,这可能吗?可以想想 Python 的各种基本类型,其中 dict
就可以满足我们的要求,因为对 dict
类型取下标拿到的是 value,而判断对象是否 in
一个 dict 时,是根据 key 的集合判断的。一个可能的答案是 1, {0:1}
。
1 | def challenge_18(self, answer): |
这个小题有点 tricky,看起来使用各种 list
、dict
、set
之类的东西搞不定了。我们可以拿出杀手锏,浮点数中的特殊值,inf
和 nan
。这种浮点数并不能直接输入,但经过简单尝试可以发现,输入很大的数,例如 1e999
,就可以得到 inf
,而 inf - inf
可以得到 nan
,nan
有一个性质就是 nan == nan
并不成立,所以这道题的答案可以是 1e999, 1e999
。
1 | def challenge_19(self, answer): |
长度小于 5 的字符串,每个字符都是 decimal
,并且取 Unicode 码之后加起来需要等于 23333。我们可以发现,满足 isdecimal()
条件的字符,不只有 0~9 这 10 个数字,还有很多各种各样的 Unicode 字符。我们可以简单写一个程序输出它们:
1 | for i in range(0x110000): |
在输出的这些 Unicode 码中,找到 5 个加起来是 23333 并不难,可以在适当的范围内手工尝试,也可以写程序搜索解决。例如一个可行的解是 '᱙᱙᱃۰'
。
1 | def challenge_20(self, answer): |
我们需要找到 7 个对象,它们转化为字符串之后各不相同,但是每个对象却都和 0 相等。比较好想到的例子是 0
、0.0
、False
和 0j
,它们分别是 int
、float
、bool
、complex
四种 Python 数值类型的对象。可 Python 只有这四种数值类型,另外三个去哪里找呢?
我们可以发现,浮点数标准中,是存在 0.0
和 -0.0
两个表示 0 的值的,它们相等。Python 中,str(-0.0)
可以得到 '-0.0'
这个字符串。我们可以类似构造出复数类型下面的各种包含浮点 0.0
和 -0.0
的组合。最终的 7 个对象是:0, 0.0, -0.0, False, -0.0-0.0j, -0.0j, 0j
。
转载于2019hackergame不同寻常的python考试
另外关于Python有趣的冷知识的一些参考资料: