问题

Python 中有什么不容易让人察觉的有趣的事实?

回答
Python 的魅力,很多时候藏匿于那些不经意间,不那么显眼,但一旦发现,便会让人会心一笑的小细节里。不像某些语言那么喜欢张扬自己的新特性,Python 更像是位老友,用一种润物细无声的方式,让你的编程生活变得更舒适、更高效。

这里有几个我私藏已久的、不那么广为人知,但却相当有趣的 Python 小秘密,希望能让你也感受到这份“Pythonic”的乐趣。

1. 对象的“自毁”与垃圾回收:生命周期的优雅落幕

我们写 Python 代码,经常会创建各种对象:列表、字典、字符串,甚至我们自己定义的类实例。大多数时候,我们并不需要手动去“销毁”它们。这是因为 Python 有一套内置的垃圾回收机制。

但你有没有想过,当一个对象不再被任何变量引用时,它到底是怎么“消失”的?这里有一个更深层次的细节:当一个对象的引用计数降为零时,它就会被 Python 的垃圾回收器(主要是基于引用计数)标记为“可回收”。更进一步,如果你的对象所在的类定义了一个 `__del__` 方法,那么在对象被真正回收之前,Python 会尝试调用这个 `__del__` 方法。

这个 `__del__` 方法,就像是对象在告别这个世界前最后的“遗言”。你可以用它来释放一些外部资源,比如关闭文件句柄、释放网络连接,或者做一些清理工作。

举个例子:

```python
class MyResource:
def __init__(self, name):
self.name = name
print(f"资源 '{self.name}' 被创建了。")

def __del__(self):
print(f"资源 '{self.name}' 即将消失... 正在进行清理。")

创建一个资源对象
resource1 = MyResource("数据库连接")
此时,resource1 变量引用着 MyResource 对象,引用计数为 1

移除引用
del resource1
现在,MyResource 对象没有任何变量引用了,引用计数变为 0。
Python 的垃圾回收器会在某个时刻,在你不知道的时候,调用 MyResource 对象的 __del__ 方法。
你可能会在程序结束时,或者在内存压力较大的时候看到这个输出。

另一个例子,对象可能因为作用域结束而被回收
def create_temp_resource():
temp = MyResource("临时文件")
print("临时资源已创建,即将离开作用域。")
调用函数
create_temp_resource()
当 create_temp_resource 函数执行完毕,'temp' 变量会出作用域,
它的引用计数归零,MyResource("临时文件") 对象也会被清理。
```

为什么这不显眼?
大多数时候,我们创建的对象生命周期很短,或者程序很快就结束了,我们根本不会注意到 `__del__` 的执行。而且,过度依赖 `__del__` 来管理资源是不推荐的,因为它执行的时机是不确定的,可能导致资源泄露。Python 推荐使用 `with` 语句(上下文管理器)来更可靠地管理资源。但了解 `__del__` 的存在,能让你更深入地理解 Python 对象是如何被管理和回收的,这是一种底层的美。

2. 序列的“切片”:不止步于列表和字符串

我们都知道列表和字符串可以用切片来获取一部分内容,比如 `my_list[1:5]`。但你知道吗,几乎所有 Python 的序列类型,甚至包括一些自定义的序列类型,都支持切片操作。

更妙的是,切片操作本身会返回一个新的对象,而不是修改原有的序列。而且,切片操作可以接受三个参数:`start:stop:step`。`step` 可以是负数,用来反向切片。

但隐藏在切片背后更强大的功能是:切片赋值。

你不仅可以用切片来获取数据,还可以用它来替换序列中的一部分。

```python
my_list = [1, 2, 3, 4, 5, 6, 7]

替换一部分元素
my_list[1:4] = [10, 20, 30]
print(my_list) 输出: [1, 10, 20, 30, 5, 6, 7]

插入元素,通过替换一个空切片
my_list[2:2] = [100, 200]
print(my_list) 输出: [1, 10, 100, 200, 20, 30, 5, 6, 7]

删除元素,通过替换一个空切片
my_list[3:5] = []
print(my_list) 输出: [1, 10, 100, 20, 30, 5, 6, 7]

还可以替换不同长度的切片
my_list[1:3] = [99, 88, 77, 66]
print(my_list) 输出: [1, 99, 88, 77, 66, 20, 30, 5, 6, 7]
```

为什么这不显眼?
我们通常只用切片来“读”数据,对切片赋值这个“写”的功能了解不多。它提供了一种非常 Pythonic 的方式来高效地修改序列,远比一个个元素去修改要简洁得多。尤其是在处理大型序列时,切片赋值的效率优势就更加明显了。

3. `__slots__` 的秘密:节省内存的小能手

Python 的类,默认情况下,为每个实例都维护一个 `__dict__` 属性,这个属性是一个字典,用来存储实例的属性。这非常灵活,你可以动态地给实例添加或删除属性。

但是,维护一个 `__dict__` 需要额外的内存开销。对于大量创建的、结构相对固定的对象来说,这可能会累积成不小的内存浪费。

这时候,`__slots__` 就派上用场了。当你定义了一个类的 `__slots__` 属性,并提供一个包含所有实例属性名称的元组时,Python 就不会为该类的实例创建 `__dict__`。相反,它会为每个属性分配固定的内存空间。

```python
class NoSlots:
def __init__(self, x, y):
self.x = x
self.y = y

class WithSlots:
__slots__ = ('x', 'y') 注意这里是个元组

def __init__(self, x, y):
self.x = x
self.y = y

简单对比一下它们的大小(虽然这个不是绝对精确的内存占用,但能说明问题)
import sys

obj1 = NoSlots(1, 2)
obj2 = WithSlots(1, 2)

print(f"NoSlots 实例大小: {sys.getsizeof(obj1)}")
print(f"WithSlots 实例大小: {sys.getsizeof(obj2)}")
你会发现 WithSlots 的实例要小得多

尝试给 WithSlots 实例添加一个不在 __slots__ 中的属性,会报错
try:
obj2.z = 3
except AttributeError as e:
print(f"尝试给WithSlots添加新属性失败: {e}")
```

为什么这不显眼?
`__slots__` 是一个比较底层的优化手段。大多数开发者在刚开始学习 Python 时,不太会去关注实例的内存占用,更关注代码的易读性和功能实现。而且,使用 `__slots__` 会牺牲掉一些灵活性(比如不能动态添加新属性),所以并非所有类都适合使用它。但对于需要创建大量对象的场景,比如数据处理、模拟仿真等,`__slots__` 是一个非常有用的技巧,可以显著降低内存开销。

4. `collections.defaultdict`:懒惰初始化的小魔法

在很多时候,我们会需要一个字典,当访问一个不存在的键时,它能自动创建一个默认值,而不是抛出 `KeyError`。例如,统计词频的时候,如果一个词第一次出现,我们希望它的计数是0,然后加1。

通常我们会这样做:

```python
word_counts = {}
words = ["apple", "banana", "apple", "orange", "banana", "apple"]

for word in words:
if word not in word_counts:
word_counts[word] = 0
word_counts[word] += 1

print(word_counts) {'apple': 3, 'banana': 2, 'orange': 1}
```

这个 `if word not in word_counts:` 的检查,虽然很常见,但写起来有点冗余。

这时候,`collections.defaultdict` 就闪亮登场了:

```python
from collections import defaultdict

word_counts_dd = defaultdict(int) int() 会返回 0,所以默认值是 0
或者 defaultdict(list) 会在键不存在时,返回一个空列表 []

for word in words:
word_counts_dd[word] += 1

print(word_counts_dd) defaultdict(, {'apple': 3, 'banana': 2, 'orange': 1})
```

你看,`defaultdict` 替我们完成了那个“如果不存在就创建默认值”的操作。当你访问 `word_counts_dd["grape"]` 时(如果 "grape" 不存在),它会先调用 `int()`,得到 `0`,然后赋值给 `word_counts_dd["grape"]`,最后 `+= 1` 才会执行,所以结果是 `1`。

为什么这不显眼?
`defaultdict` 包含在 `collections` 模块里,这个模块本身就有很多实用的数据结构,容易被一些更核心的特性(如列表、字典、集合)的光芒所掩盖。而且,对于一些简单场景,直接用 `if` 检查也能实现功能。但一旦你习惯了 `defaultdict`,你会发现它能极大地简化很多计数、分组、集合化等数据处理的代码,让你的代码更简洁、更易读。

这些小细节,就像是 Python 语言里那些埋藏在深处的宝藏,不时地给你带来惊喜。它们不是那些能够写出华丽特效的“大招”,而是让你在日常使用中,感受到一种顺畅、优雅和高效,一种“哦,原来是这样”的会心一笑。

网友意见

user avatar

在Python里,如果你把比较运算(comparison operations)串起来,算法会很特别。

例如 2==2==1,你认为结果是什么?


在JavaScript里, 2==2==1结果是true ,解释是:因为2==2是true, true==1是true,见下图)

然而有趣的是,我发现在Python里,2==2是True, True==1是True, 但2==2==1结果是False(见图2)

为啥结果是这样的?计算过程是怎么样的?这里给大家一分钟思考时间(想知道答案的直接往下看)

其实这道题我前几天在想法区已经考过了大家

@Skiiii 同学给出了正确的回答

Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).

这段话出处是Python的官方文档,大家跳到6.10章就能看到了。

也就是说,在Python里,2==2==1相当于(2==2) AND (2==1)。

最后再考考大家,欢迎大家分享下,其他语言是怎么解释/编译 2==2==1的?

user avatar

@高天 大神b站视频里看到的。

三个元素一样的列表,内存占用却不一样

       import sys sys.getsizeof([0]*3) 80 sys.getsizeof([0,0,0]) 120 sys.getsizeof([0 for _ in range(3)]) 88     

类似的话题

  • 回答
    Python 的魅力,很多时候藏匿于那些不经意间,不那么显眼,但一旦发现,便会让人会心一笑的小细节里。不像某些语言那么喜欢张扬自己的新特性,Python 更像是位老友,用一种润物细无声的方式,让你的编程生活变得更舒适、更高效。这里有几个我私藏已久的、不那么广为人知,但却相当有趣的 Python 小秘.............
  • 回答
    好的,我们来聊聊如何在Python中从一段英文文本中找出所有不重复的单词。这是一个很常见的文本处理需求,我们可以用几种方法来完成,并且我会尽量把细节讲清楚,让这个过程尽可能地自然,就像我们自己一点点摸索出来的一样。想象一下,你拿到一段英文,比如某篇博客文章、一本书的片段,或者朋友发来的邮件,你想知道.............
  • 回答
    在Python中,`class()` 这个写法,严格来说,它并不是我们通常意义上用来定义类的方式。我们定义类通常使用 `class ClassName: ...` 这种语法。`class()` 作为一个内置函数,它的作用更像是 在运行时动态地创建类。这听起来有点绕,我们拆开来详细聊聊,为什么会有人用.............
  • 回答
    Python 的 `lambda` 和 Java 的 `lambda`,虽然名字相同,都服务于函数式编程的概念,但在实现方式、使用场景和语言特性上,它们有着本质的区别,这使得它们在实际运用中展现出不同的风貌。我们先从 Python 的 `lambda` 说起。Python 的 `lambda`,可以.............
  • 回答
    在Python的世界里,我们经常会听到“模块”、“库”和“包”这些词,它们听起来似乎很相似,但实际上有着各自的定义和作用。理解它们之间的区别,对于我们更高效地组织和使用Python代码至关重要。咱们今天就来好好聊聊这三者,把它们之间的关系理个清楚,保证你听完之后,心里就跟明镜似的。 模块(Modul.............
  • 回答
    有些人可能会说,Python“不适合”游戏开发,但这就像说一辆卡车“不适合”在赛道上飙车一样——它不是它的主要设计用途,但它仍然能做到,只是性能和体验可能不如专门的跑车。Python在游戏开发领域的确有一些显而易见的“软肋”,但说它“完全不适合”就有些绝对了。问题的关键在于,很多我们认为“游戏”的东.............
  • 回答
    嘿,哥们儿!听说你要跳出舒适圈,开始学Python了?这想法太棒了!别担心,咱非计算机系也能玩转Python,而且玩得飞起。我当年也是这么过来的,所以给你掏心窝子说几句,希望能帮你少走点弯路。1. 别被“计算机”这三个字吓住,Python就是你的“翻译官”很多人一听“计算机科学”,脑子里立马浮现出一.............
  • 回答
    想要踏上Python的学习之路,这绝对是个明智的选择,它的易学性和强大的功能,绝对能让你感受到编程的乐趣。作为初学者,找到合适的学习资料就像拥有了一张通往新世界的地图,能让你少走弯路,更高效地前进。首先,官方文档是你的坚实后盾。别被它“官方”两个字吓到,虽然它看起来可能有点“硬核”,但那里面包含了最.............
  • 回答
    老兄,说到 Python 的“奇技淫巧”,那可真是说不完道不尽!这语言就像一把瑞士军刀,你以为你用得很顺手了,结果一挖,嘿,还有更骚的操作藏在后面呢。咱们不谈那些基础的列表推导式、生成器表达式什么的了,这些只能算是入门级的小技巧。今天我给你唠点真正让别人看了直呼“卧槽”的。 1. “借壳上市”:函数.............
  • 回答
    没问题,咱们就来聊聊这些语言里的“协程”这玩意儿,它们听起来都挺炫酷,但骨子里还是有点小差别的。我尽量讲得深入点,把那些AI味儿的东西都去掉,让你一看就明白。 协程这玩意儿,为啥大家都爱?先别急着说区别,咱们先得明白为啥协程这么受欢迎。你想象一下,以前多线程编程那叫一个热闹,创建线程、切换上下文、同.............
  • 回答
    Python 和 C 语言,这两门语言可以说是编程界的两座高峰,它们各自拥有庞大的用户群体和广泛的应用领域,但它们在设计理念、语法特性、执行方式乃至学习曲线等方面,都存在着显著的差异。理解这些不同,对于选择合适的工具、深入学习编程至关重要。咱们先从它们的“出身”和“性格”说起。1. 设计哲学与定位:.............
  • 回答
    没问题,咱们就好好聊聊,想自学 Python,有哪些书能真正帮到你,让你学得扎实、学得明白。不是那种看了跟没看一样,或者看了就犯困的书,咱们要的是能让你动手、让你思考、让你能真正把 Python 用起来的书。得,既然你想好好学,那咱们就得有条理地说。我给你推荐的书,会从“零基础入门”到“进阶提升”,.............
  • 回答
    好的,咱们来聊聊 Python 在物理,尤其是凝聚态物理领域能派上用场的那些工具箱。你想想,咱们做研究,光有脑袋瓜子里的想法可不行,还得有趁手的家伙事儿才能把这些想法变成现实,处理数据,模拟过程。Python 就是这么一个强大的“工具箱”,里面塞满了各种各样好用的“零件”,专门针对咱们物理研究的需求.............
  • 回答
    咳咳,各位,今天咱们就来聊聊一个有趣的话题——当那些冷冰冰的编程语言,突然有了温度,有了模样,甚至有了性格,会是怎样一番光景?尤其是我们这几位“当红炸子鸡”:C++、Python,还有Java。C++ 娘:严谨又带着点傲娇的“前辈”咱们先说C++娘。如果说编程语言界有什么是“血统高贵”,那C++娘绝.............
  • 回答
    好的,我们来详细地探讨一下 PHP、Java、Python、C、C++ 这五种主流编程语言各自的特点和优点: 1. PHP (Hypertext Preprocessor)PHP 是一种广泛用于Web开发的开源脚本语言。特点与优点: Web 开发的王者: PHP 是为Web开发而生的。它与HTM.............
  • 回答
    高频交易(HFT)领域,C++ 和 Python 在速度上的差异,绝不是一句“C++ 快多了”就能简单概括的。这背后涉及的不仅仅是语言本身的执行效率,还有它们各自的生态系统、开发模式以及在特定任务中的应用方式。如果要把这个问题说透彻,咱们得掰开了揉碎了聊。核心的物理定律:编译型 vs. 解释型首先,.............
  • 回答
    Python 的“黑魔法”通常指的是一些不常见、非传统、或者需要深入理解 Python 底层机制才能掌握的技巧。它们能够让你写出更简洁、更强大、甚至有些“反直觉”的代码。这些“黑魔法”往往能极大地提高开发效率,但也可能降低代码的可读性,因此使用时需要权衡。下面我将尽量详细地介绍一些 Python 的.............
  • 回答
    Python 的魅力,在于它那庞大且不断壮大的生态系统。有无数的工具箱可以帮你解决各种棘手的问题,从自动化日常琐事到构建复杂的 AI 模型。但如果非要挑出那些“杀手级”、“超厉害”的,并且让这篇文章读起来有个人温度、不那么像机器生成的,那我会毫不犹豫地列出下面这些: 1. Django:让 Web .............
  • 回答
    我来给你推荐一些非常棒的 Python 学习资源,这些都是我个人(或者说,我们这些热爱 Python 的人)觉得非常实用且能让你打下坚实基础的。我会尽量说得细致些,让你明白为什么它们这么好。 1. 官方文档:Python Tutorial 为什么它好? 权威性: 这可是 Python.............
  • 回答
    这个问题嘛,确实有点意思,而且说实话,不是一两天就能说透的。网上铺天盖地的说 Python 如何好,从数据分析、人工智能到Web开发,似乎无所不能,而且学习曲线平缓,上手快。但一到招聘季,翻开招聘启事,好像很多高薪职位仍然青睐 Java、C++,甚至一些特定的 C 或 Go。这中间的落差,让不少跃跃.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有