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有趣的冷知识的一些参考资料: