【趣味阅读】Python 文件头的秘密:从编码声明到 Shebang
文章目录
- 一、`# -*- coding: utf-8 -*-` 的用意是什么?
- 二、编码声明的规范写法
- 三、那 Python 3 就不需要编码声明了吗?
- 四、shebang 行是干什么的?
- 五、shebang 这个词从哪来的?
- 六、总结
- 七、思考
在日常写 Python 的时候,你可能见过这样的文件头:
# -*- coding: utf-8 -*-
或者:
#!/usr/bin/env python3
很多人照抄,却未必真的理解它们的来历和规范。今天我们就一步步探索,从带着疑问到豁然开朗,把背后的故事讲清楚。
一、# -*- coding: utf-8 -*-
的用意是什么?
一开始,我也只是机械地在文件头写上这行,后来才知道:
- 这是告诉 Python 解释器 和 编辑器:源文件采用 UTF-8 编码。
- 在 Python 2 中,默认编码是 ASCII,所以写中文就必须加这一行,否则会报错。
- 在 Python 3 中,默认编码已经是 UTF-8(见 [PEP 3120]),因此通常不需要写。
也就是说,如果你只写 Python 3,文件里又没有奇怪的编码,其实可以省略。
二、编码声明的规范写法
-
位置:必须放在文件的 前两行 之一。
-
如果文件开头有
#!
(shebang 行),那么编码声明要写在第二行;#!/usr/bin/env python3 # -*- coding: utf-8 -*-
-
如果没有 shebang 行,就写在第一行。
# -*- coding: utf-8 -*-
-
原因:解释器在扫描源码时,只读取前两行来决定文件编码。
-
-
格式:查了 [PEP 263] 之后,才知道 Python 解释器通过正则表达式识别的模式很严格:
# -*- coding: utf-8 -*- # coding=utf-8
这两种都行。
而有些人写的:
# encoding=utf-8 # -*- encoding=utf-8 -*-
抱歉,这其实不符合[PEP 263]规范!解释器根本不认。可能某些编辑器会看得懂,但 Python 解释器本身会忽略。
所以,请坚持用
coding
而不是encoding
。 -
大小写:
coding
、utf-8
不区分大小写,但社区习惯使用小写。
三、那 Python 3 就不需要编码声明了吗?
没错。Python 3 默认就是 UTF-8,因此:
- 只写 Python 3:编码声明是冗余的。
- 需要兼容 Python 2:那就老老实实写上
# -*- coding: utf-8 -*-
。
这时候我突然意识到:原来我们很多写法,其实是“历史遗留”!
四、shebang 行是干什么的?
再来看另一种常见的文件头:
#!/usr/bin/env python3
这叫 shebang 行(也叫 hashbang)。它的作用是:
- 在 Linux / macOS 下,告诉操作系统用哪个解释器来运行脚本。
- 如果脚本加了可执行权限(
chmod +x script.py
),就能直接./script.py
执行。 - 如果没有 shebang,直接执行会报错(除非你显式写
python script.py
)。
推荐的写法是:
#!/usr/bin/env python3
- 而不是写死成
#!/usr/bin/python3
,因为不同系统,路径可能不同。而env
会自动帮你在PATH
中找解释器。 - 也不建议写成
#!/usr/bin/env python
,因为可能会指向 Python2, 不建议再这样写。 - 此外
#!
后面应该紧跟解释器路径,不能有前导空格,👉 保证在各种 Unix 系统上都能正确运行。 - 在 现代 Linux / macOS 下,
#! /usr/bin/env python3
这种写法虽然能跑,但依赖的是 “宽松实现”,并不是标准行为。 - 因为主流 Linux 内核源码 fs/binfmt_script.c 会 skip_spaces(),自动跳过前导空格。
位置:
-
必须在文件的第一行,并且必须以
#!
开头。 -
因为这是操作系统内核(execve)在运行脚本时直接解析的,而不是 Python 自己处理的。
如果前面有空行或注释,shebang 就失效了。
那现在的必要性如何呢?
- 在 Linux / macOS:如果希望脚本像普通命令一样直接运行,shebang 行仍然非常必要。
- 在 Windows:shebang 行不起作用,系统靠
.py
扩展名来决定解释器,但写上也不影响。
五、shebang 这个词从哪来的?
这部分特别有意思。
-
历史背景
shebang 是 Unix 第七版(1979 年) 加入的功能,用来告诉内核脚本应该用哪个解释器运行。当时 shell 脚本是主流,这个设计解决了“脚本用什么解释器跑”的问题。 -
符号组成
shebang 行的开头是:#!
#
号在 Unix 里叫 hash!
号在口语里常被叫做 bang(Unix 黑客传统用语,源自打印机和电传机时代)
把
hash
+bang
拼在一起,就成了 shebang。 -
she- 的由来
这个前缀并不是“she 她”的意思,而是口语发音的简化:-
“hash-bang” → “sharp-bang” → “sh-bang” → “shebang”
-
后来逐渐就固定写成 shebang 了。
-
-
这两个的读音怎么来的
-
#
为什么叫 hash- 在 Unix 黑客文化里,
#
常被叫做 hash。- “hash” 原意是“切碎、混合”,因为
#
符号看上去像格子网,被形象地称为 hash。 - 这种叫法在程序员社区里流行开来,比如
hash mark
。 - 另一部分人受音乐升半音符号(sharp sign) ♯ 的影响,自然而然将
#
读作 sharp。 - 比如微软的
C#
语言 (读作 C Sharp) 的命名就含有 “比 C 语言高半点” 的程序员幽默。
- “hash” 原意是“切碎、混合”,因为
- 在 Unix 黑客文化里,
-
!
为什么叫 bang-
在英语里,
!
的正式名称是 exclamation mark 或 exclamation point。 -
但在早期 电传打字机(teletypewriters, TTY) 和 Unix 黑客圈子里,大家更喜欢用短促的单词来念标点。
-
当时
!
常用于表示“激烈动作/爆炸声”,所以用 bang(拟声词)来念,既短又形象。 -
其实我自己觉得也可以把
!!
形象地看成哆啦A梦两个小圆手拿着啦啦棒互相拍打发出 “Bang Bang” 响声:
举个例子:
!!
在邮件或新闻组里就念作 “bang bang”- 早期的 UUCP 网络用
!
分隔路径,host1!host2!user
就读成 “host1 bang host2 bang user”。
-
-
组合起来
-
#!
→ 读作 hash bang后来为了更顺口,读音演变成 shebang(hash-bang → sharp-bang → sh-bang → shebang)。
-
-
于是 #!
就被念作 hash bang。久而久之,口语演变成了 shebang(sharp-bang → sh-bang → shebang)。
原来名字背后还有这种黑客幽默!
六、总结
一路探索下来,我们可以这样归纳:
- shebang 行:
- 让脚本在类 Unix 系统下可直接运行。
- 推荐写法是
#!/usr/bin/env python3
。 - 必须在文件的第一行,并且必须以
#!
开头。 #!
和解释器路径之间,推荐是不加空格。- 在 Linux/macOS 下必要,Windows 下可有可无。
- 名字来源于
#
(hash)和!
(bang)。
- 编码声明:
- Python 2 必须写,否则非 ASCII 会报错。
- Python 3 默认 UTF-8,可以不写。
- 规范写法是
# -*- coding: utf-8 -*-
或# coding=utf-8
。 - 必须在文件的前两行之一(一般紧跟在 shebang 后)。
是不是感觉以前机械抄写的几行注释,现在突然有了“灵魂”?😄
七、思考
其实很多编程习惯,背后都有 历史包袱 或 黑客文化。今天我们把它拆开来看,像是解谜:从一行注释,到一段 Unix 传统,最后恍然大悟。
你平时在写 Python 文件头时,会选择 简洁派(不写编码,不写 shebang),还是 传统派(都写上,兼容性更强) 呢?