Interesting python
文章目录
💡 一些有趣且鲜为人知的 Python 特性
Run at Python 3.8.11
Bad key
dct = {
}
dct[5] = "java"
dct[5.0] = "python"
Output:
>>> dct[5.0]
python
>>> dct[5]
python
Remark:
-
Python 字典通过键的值是否相等以及比较哈希值来确定两个键是否相同
-
具有相同值的不可变对象在 Python 中始终具有相同的哈希值
>>> 5 == 5.0 True >>> hash(5) == hash(5.0) True
奇怪的return
def demo():
try:
return "try"
finally:
return "finally"
Output:
>>> demo()
finally
Remark:
当在 try...finally
语句的 try
中执行 return
, break
或 continue
后,finally 子句依然会执行
特殊 for
s = "wtf"
dct = {
}
for i, dct[i] in enumerate(s):
pass
Output:
>>> dct
{
0: 'w', 1: 't', 2: 'f'}
Remark:
Python 语法中对 for 的定义是:
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
其中 exprlist
指分配目标,这意味着可迭代对象中的每一项都会执行类似 {exprlist} = {next_value}
的操作,上述代码等效于:
>>> i, dct[i] = (0, 'w')
>>> i, dct[i] = (1, 't')
>>> i, dct[i] = (2, 'f')
执行时机差异
arr = [1, 2, 3]
g = (x for x in arr if arr.count(x) > 0)
arr = [2, 4, 6]
Output:
>>> print(list(g))
[2]
Remarks:
- 在生成器表达式中,in 子句在声明时执行,而条件子句则是在运行时执行
赋值方式差异
# demo1
lst = [1, 2]
print(id(lst)) # 140444505190976
lst = [3, 4]
print(id(lst)) # 140445040739584
# demo2
lst2 = [1, 2]
print(id(lst2)) # 140444504561920
lst2[:] = [3, 4]
print(id(lst2)) # 140444504561920
Remarks:
- 在 demo1 中,lst 被绑定到新对象
- 在 demo2 中,lst2的切片赋值为原地更新
浅拷贝
l = [''] * 3
ll = [l] * 3
print(ll) # [['', '', ''], ['', '', ''], ['', '', '']]
ll[0][0] = 'x'
print(ll) # [['x', '', ''], ['x', '', ''], ['x', '', '']]
奇怪的function
lst = [lambda a:a*x for x in range(4)]
res = [func(1) for func in lst]
print(res)
# [3, 3, 3, 3]
Remarks:
- 当在循环内部定义一个函数时,如果该函数在其主体内部使用了循环变量,则闭包函数将于循环变量绑定,而不是它的值
- 因此,所有的函数都是使用最后分配给变量的值 (3)来进行计算的
隐式连接
>>> print('wtf''') # wtf
>>> print("wtf""") # wtf
>>> print("""wtf") # raise SyntaxError
>>> print('''wtf') # raise SyntaxError
Remarks:
-
Python 提供隐式的字符串连接,例:
>>> print("wtf" "python") # wtfpython
-
'''
和"""
在 Python 中是一组字符串定界符,Python 解释器在先遇到三个引号的时候会尝试再寻找三个终止引号作为定界符,若不存在则会导致 SyntaxError 异常
What’s wrong with boolean
lst = [1, 1.0, '123', True, False]
int_num = 0
bool_num = 0
for i in lst:
if isinstance(i, int):
int_num += 1
elif isinstance(i, bool):
bool_num += 1
print(int_num) # 3
print(bool_num) # 0
Remarks:
- 布尔是 int 的子类
- True 的整数值是 1,False 的整数值是 0
丢失的变量
e = 7
try:
raise Exception()
except Exception as e:
pass
print(e) # NameError: name 'e' is not defined
Remarks:
当使用 as 为目标分配异常的时候,将在 except 子句的末尾清除该异常
except Exception as e:
foo
# 会被翻译为:
except Exception as e:
try:
foo
finally:
del e
跳过一行
value = 11
valuе = 32
print(value) # 11
Remarks:
<aside> 💡 如果你想重现的话,最好的方法是直接复制上面的代码片段到你的文件中 </aside>一些非西方字符虽然看起来和英语字母相同,但会被解释器识别为不同的字母
>>> ord('е') # 西里尔语的 'e' (Ye)
1077
>>> ord('e') # 英文字母 'e'
101
>>> 'е' == 'e'
False
空间移动
# numpy.__version__ 1.21.2
import numpy as np
def energy_send(x):
# 初始化一个 numpy 数组
np.array([float(x)])
def energy_receive():
# 返回一个空的 numpy 数组
return np.empty((), dtype=np.float).tolist()
energy_send(123.456)
print(energy_receive()) # 123.456
Remarks:
- 注意在 energy_send 函数中创建的 numpy 数组并没有返回,因此内存空间被释放并可以被重新分配
- numpy.empty() 直接返回下一段空闲内存,而不重新初始化,而这个内存节点恰好就是刚刚释放的那个(通常情况下,并不绝对)
同人不同命
a = [1, 2]
b = a
a = a + [3]
print(a) # [1, 2, 3]
print(b) # [1, 2]
a = [1, 2]
b = a
a += [3]
print(a) # [1, 2, 3]
print(b) # [1, 2, 3]
Remarks:
a += b
并不总是与a = a + b
表现相同