惊奇时刻!盘点哪些让你大呼“卧槽”的 Python 代码!

778852c1708b4a0f285fb47538066040.png

文 | 豆豆

来源:Python 技术「ID: pythonall」

d713f1d2766b4d8d7704b8056a26b160.png

Python 作为一个设计优美的高级语言,提供了很多简单易用的特性,但简单并不意味着容易理解,有时候一些输出结果对于刚入门的小伙伴并不是很明了,反而似乎有点反人类。

今天派森酱就整理了一些非常有趣的例子,事实上这些例子不仅有趣,甚至还可以加深你对 Python 的理解,学到更多有趣的特性。

0x00

>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False # 2.7 版本返回 False
# 3.7 版本返回结果为 True

f1a7a2714109b7805e1f4a609626223a.png

很神奇的一个结果,第一次看到时我也惊呆了,事实上这是 Python 的一种优化机制,叫常量折叠。这意味着在编译时表达式 'a'*20 会被替换为 aaaaaaaaaaaaaaaaaaaa 以减少运行时时常,而只有长度小于 20 的字符串才会发生常量折叠。

0x01

In [3]: a = 'python'

In [4]: b = 'python'

In [5]: a is b
Out[5]: True

In [6]: x = 'python!'

In [7]: y = 'python!'

In [8]: x is y
Out[8]: False

2dfa8c6b30127f2e00612c81ace8029f.png

这是因为在编译优化时 Python 会尝试使用一些已经存在的不可辨对象,这种现象称之为字符串驻留,而只包含字母数字和下划线的字符串在编译时是会驻留的,包含 ! 的字符串是不会驻留的。

0x02

In [11]: some_dict = {}

In [12]: some_dict[5.5] = 'Java'

In [13]: some_dict[5.0] = 'Ruby'

In [14]: some_dict[5] = 'Python'

In [15]: some_dict[5.5] + '_' + some_dict[5.0] + '_' + some_dict[5]
Out[15]: 'Java_Python_Python'

In [16]: 5 == 5.0
Out[16]: True

In [17]: hash(5) == hash(5.0)
Out[17]: True

2e6ba11069581963c98a7df7a396716e.png

Python 字典通过比较不可变对象是否相等和哈希值是否相等来确定是否为不同的键,但不同值的对象也可能具有相同的哈希值。因此字典以为 5.0 和 5 是同一个键,所以 Python 会覆盖掉 Ruby。

0x03

In [21]: def some_func():
    ...:     try:
    ...:         return 'from_try'
    ...:     finally:
    ...:         return 'from_finally'
    ...:

In [22]: some_func()
Out[22]: 'from_finally'

a4749592516f54bf58bb0401130f7385.png

函数的返回值由最后 return 的语句决定,而 finally 一定是最后之行的,所以会覆盖掉 try 中的 return 结果。

0x04

In [23]: for i in range(3):
    ...:     print(i)
    ...:     i = 10
    ...:
0
1
2

8372835ae375cc3dca32652a8ee8dc2b.png

在每次循环开始之前, 迭代器生成的下一个元素会重新赋值给 i,因此赋值语句 i = 10 并不会影响循环。

0x05

In [24]: row = [''] * 3

In [25]: table = [row] * 3

In [26]: table
Out[26]: [['', '', ''], ['', '', ''], ['', '', '']]

In [27]: table[0][0] = 'python'

In [28]: table
Out[28]: [['python', '', ''], ['python', '', ''], ['python', '', '']]

82d01a4f41875839c3ea6dd10fed0f86.png

这是因为通过乘法初始化 table 时,每个 item 也就是 table[0]、table[1]、table[2] 在内存中引用的都是同一个列表。

0x06

In [29]: a = 256

In [30]: b = 256

In [31]: a is b
Out[31]: True

In [32]: a = 257

In [33]: b = 257

In [34]: a is b
Out[34]: False

5245757d72f6b63da93e35943dc53f4f.png

产生这种现象的根本原因就是 256 是一个已经存在的对象而 257 不是,事实上当 Python 启动时数值为 -5 到 256 这些常用的的对象就已经被分配好了。

0x07

In [37]: 'something' is not None
Out[37]: True

In [38]: 'something' is (not None)
Out[38]: False

96878fb856509ceadb27f555e350ad2a.png

这是因为 is not 是一个单独的二元运算符,如果运算符两侧的变量指向同一个对象, 则 is not 的结果为 False, 否则为 True

0x08

In [47]: a = [1, 2, 3, 4]

In [48]: b = a

In [49]: a = a + [5, 6, 7, 8]

In [50]: a
Out[50]: [1, 2, 3, 4, 5, 6, 7, 8]

In [51]: b
Out[51]: [1, 2, 3, 4]
In [52]: a = [1, 2, 3, 4]

In [53]: b = a

In [54]: a += [5, 6, 7, 8]

In [55]: a
Out[55]: [1, 2, 3, 4, 5, 6, 7, 8]

In [56]: b
Out[56]: [1, 2, 3, 4, 5, 6, 7, 8]

ddb599534b9bf9f0e5185ea88842837e.png

在第一个例子中 a = a + ... 会生成一个新的列表然后 a 指向这个列表,但 b 是保持不变的。

而第二个例子中的 a += ... 实际上是使用的是 extend 函数,所以 a 和 b 指向的都是同一个列表。

0x09

t = ('one', 'two')
for i in t:
    print(i)

print('*'*5)
t = ('one')
for i in t:
    print(i)

print('*'*5)
t = ()
print(t)

## 输出
one
two
o
n
e
tuple()

958086b78784e355dca09228d61b1594.png

在 Python 的世界 () 是一个特殊的标记,表示空元组,这个非常容易理解。

t = ('one') 或者 t = 'one' 都是会被解释成为字符串的。正确的写法应该是 t = ('one',)

总结

今天派森酱带领大家整理了一些 Python 中比较好玩有趣的代码,觉得眼前一亮的同时还可以顺带更深刻的理解 Python 的设计思想,一举两得。

小伙伴们还遇到过哪些匪夷所思的代码呢,可以评论区一起分享一波哦~

PS:公号内回复「Python」即可进入Python 新手学习交流群,一起 100 天计划!

老规矩,兄弟们还记得么,右下角的 “在看” 点一下,如果感觉文章内容不错的话,记得分享朋友圈让更多的人知道!

177093f22d93fd4051c7eec5b820307e.gif

代码获取方式

识别文末二维码,回复:python

fd4f96b0d1bdbb5f2a052022762498e6.png

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值