Python 中 9 种奇怪的特性及其解释
Python 是一种优雅的语言,但远非完美。
Python 有一些奇怪的特性,甚至会让资深开发人员感到困惑。
光说不练假把式。本文将向您展示令人困惑的 Python 代码段,并给出精确的解释。
1.有 “else “语句,但没有 “if “语句
许多编程语言都有 “if-else “结构来处理条件语句。
然而,在 Python 中,您甚至可以使用没有 “if “的 “else “语句。
leaders = ["Elon", "Tim", "Warren"] for i in leaders: if i == "Yang": print("Yang is a leader!") break else: print("Not found Yang!") # Not found Yang!
例如,上述代码中没有 “if “语句。但 “else “语句下的代码块已成功执行!
这就是 Python 中的 “for-else “语法。
这是一个奇怪的特性,我们在编码中使用它时应该小心谨慎。但它的思想却出乎意料地简单:
当 for 循环没有中断时,”else “代码块就会执行。
为了证明这一点,让我们稍微修改一下列表:
leaders = ["Yang", "Elon", "Tim", "Warren"] for i in leaders: if i == "Yang": print("Yang is a leader!") break else: print("Not found Yang!") # Yang is a leader!
如上面的程序所示,此时 leaders
列表中包含 “Yang”。因此,for 循环被破坏,else 语句没有执行。
2.不可变的元组被更改
我们知道,元组是不可变的 Python 对象。
但下面的元组是可以更改的:
tp = ([1, 2, 3], 4, 5)
tp[0].append(4)
print(tp)
# ([1, 2, 3, 4], 4, 5)
这是因为嵌套的 Python 对象的可变性取决于每个对象本身。tp 是一个不可变的元组,但 tp 的第一项是一个可变的 list。
3.256 是 256,但 257 不是 257
如果用 Python 比较数字,有时结果会出人意料:
>>> a=256
>>> b=256
>>> a is b
True
>>> x=257
>>> y=257
>>> x is y
False
在底层下,Python 预加载了[-5, 256]
范围内的所有小整数,以节省时间和内存成本。因此,当声明这个范围内的整数时,Python 只会引用缓存的整数,而不会创建任何新对象。
换句话说,a 和 b 是同一个对象,但 x 和 y 是两个不同的对象。
我们可以打印每个变量的 id 来证明这一点:
>>> id(a)
1696073345424
>>> id(b)
1696073345424
>>> id(x)
1696122928496
>>> id(y)
1696122928752
这种机制被命名为整数互操作或整数缓存。
但是,下面的代码会打印出什么呢?还是False
?
>>> 257 is 257
事实上,Python 总是会尽力让事情保持清晰。由于我们没有分别定义两个变量,Python 可以在单行命令中获得足够的上下文,这次将打印 True。
4.Python 中的字符串比较
与整数缓存机制类似,Python 也会缓存小尺寸的字符串,以节省计算资源。
让我们来看一个例子:
>>> a = "Yang"
>>> b = "Yang"
>>> a is b
True
>>> c = "Yang Zhou"
>>> d = "Yang Zhou"
>>> c is d
False
字符串缓存也取决于 Python 的实现。
在本例中,我使用的是 CPython,它的缓存算法是 AST optimizer,最多可以缓存 4096 个字符,但任何包含空格的字符串都不会被置换。
5. 0.1+0.2 is not 0.3
每个人都知道 0.1 + 0.2 等于 0.3,但 Python 并不这么认为:
print(0.1+0.2==0.3)
# False
如果不是 0.3,那么在 Python 中 0.1+0.2 的结果是什么?
print(0.1+0.2)
# 0.30000000000000004
公平地说,这不是 Python 的错。没有计算机能精确计算浮点数值。
计算机只能存储和处理一定精度的浮点数。因此,浮点运算依赖于机器处理器中的硬件实现,没有一种编程语言可以说它的浮点计算总是正确的。
6.”+=”比”=”更快
在 Python 中连接字符串时,”+=”和 “+”操作符可以得到相同的结果,但代价不同:
import timeit
print(timeit.timeit("s1 = s1 + s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100))
# 0.7268792000000001
print(timeit.timeit("s1 += s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100))
# 0.3451913999999999
在 Python 中,当连接两个以上字符串时,+= 操作符比 + 更快。因为 += 是就地操作,与 + 操作相比,可以节省创建新对象的时间。
这个特性乍一看很奇怪,但它能帮助我们大大加快程序的运行速度,尤其是在处理复杂字符串的情况下。
7.三个点
如果需要一个 Python 函数的占位符,您可能会使用 pass 关键字:
def my_func():
pass
但还有另一种方法:
def my_func():
...
是的,三点或省略号也可以是占位符。
这种语法糖背后的故事非常有趣。Python 之父 Guido van Rossum 使省略号在语法上正确,因为 “有些人认为它很可爱”。
此外,省略号还可用于 Python 中的类型提示。
例如,任意长度的同质元组可以这样表达 – Tuple[int,...]
。
8.Python 中的 “with “语句是什么?
其他编程语言都没有 “with “语句,因此对于初学者来说,Python 的 “with “语句显得非常奇怪。
with open("test.txt",'w') as f:
f.write("Yang is writing!")
事实上,”with “语句只是用于上下文管理的语法糖。使用它,你就不需要明确地编写 close() 函数来关闭文件,因为文件会在使用后自动关闭。
示例代码如下
f = open("test.txt",'w')
try:
f.write("Yang is writing!")
except Exception as e:
print(f"An error occurred while handling the file: {e}")
finally:
# Always close a file after using it
f.close()
哪个片段更整洁?
with “语句是 Pythonic 的一大特色。
9.一个星号和两个星号
如果您阅读开源 Python 项目,下面这种函数定义样式可能会出现很多次:
def func(*args, **kwargs):
pass
这些奇怪的星号是什么意思?
在定义 Python 函数时,我们可以定义一个前缀为星号的参数来捕捉无限数量的参数。
- 前缀为一个 * 的参数可以捕获任意数量的位置参数到一个元组中。
- 以两个 * 为前缀的参数可以将任意数量的关键字参数捕获到一个 dict 中。
此外,在某些情况下,利用 Python 中的星号可以使我们的代码更加优雅。
例如,我们可以借助星号解包iterables:
A = [1, 2, 3]
B = (4, 5, 6)
C = {7, 8, 9}
L = [*A, *B, *C]
print(L)
# [1, 2, 3, 4, 5, 6, 8, 9, 7]
本文文字及图片出自 9 Weird Python Features and How To Explain Them
你也许感兴趣的:
- 具有魔法的 H.264
- 多用户环境中的 rootless Docker
- 【外评】微软的人工智能聊天机器人将 “回忆 “您在其新 PC 上所做的一切
- 【外评】苹果需要解释重新出现已删除照片的错误
- 你需要知道的现代 CSS 技巧(2024 年春季版)
- 使用 :has() 作为 CSS 父选择器及其他更多内容
- 【外评】大科技公司致欧盟:“去死”
- npm又被滥用,灰产用《庆余年2》盗版资源——把开源公共基础设施的羊毛薅秃了
- 【外评】如果您没有在 Edge 中使用必应,微软现在会说您的电脑需要 “修复”
- Chrome 浏览器开发工具(DevTools)现在使用双子座(Gemini )来帮助处理控制台中的 JavaScript 错误
你对本文的反应是: