Python技术迷

Python面试题:为什么不建议以下划线作为标识符的开头?

有一次我在咖啡馆碰到一个刚入行的小伙子,他正准备面试 Python 后端开发。他跟我说,简历上写了“熟悉 Python 命名规范”,结果面试官直接抛了个问题:为什么 Python 里不建议用下划线开头作为标识符?他当场就愣住了。其实这个问题看似细节,但背后涉及 Python 的语法规则、社区约定,还有一些隐藏的坑。

下划线在 Python 里的“暗语”

我们先说下 Python 里的下划线,表面上就是个普通字符,但在不同的语境下,它的意义完全不同。比如:

_var = 10# 单下划线开头
__var = 20# 双下划线开头
__var__ = 30# 双下划线包裹

这三种写法效果差别非常大。单下划线 _var 通常表示“内部使用”的变量,告诉别人这是私有实现细节,不建议直接用。双下划线 __var 会触发名字改写(name mangling),Python 编译时会偷偷把它改成 _类名__var,避免子类误用。至于 __xxx__,这是 Python 官方保留的魔术方法,比如 __init__、__str__,如果你随便乱定义一个 __data__,有可能未来版本 Python 就把它用掉了,到时候代码直接冲突。

所以,当你用下划线开头做变量名,看起来没什么问题,但在别人眼里就会产生歧义:你到底是想写私有变量,还是要搞特殊语法?这就是面试官追问的重点。

单下划线的坑

单下划线 _ 除了标识私有变量之外,还有几个特殊用法。比如在交互式环境里,_ 默认存储上一次计算的结果:

>>> 3 + 4
7
>>> _
7

有一次我在 Jupyter Notebook 里调试,写了个 _temp = get_data(),结果 _temp 的值根本不是我函数返回的,而是继承了 _ 的特殊行为,调试半天才发现问题。这个就是典型的“踩坑现场”。

双下划线改写的“迷惑”

再说 __var。很多人以为这是“更严格的私有变量”,其实不然。它的作用主要是防止子类无意间覆盖父类的变量。比如:

classA:
def__init__(self):
        self.__data = 42

classB(A):
def__init__(self):
        super().__init__()
        self.__data = 99

b = B()
print(b._A__data)  # 42
print(b._B__data)  # 99

你看,这里其实存在两个不同的变量:_A__data 和 _B__data。名字改写虽然避免了冲突,但也让调试和阅读变得非常绕。更坑的是,如果你不知道这个规则,直接访问 b.__data,会发现压根不存在这个属性。面试官要的就是你能说出这种“内部机制”。

双下划线包裹的“魔术域”

Python 内置很多魔术方法,都是 __xxx__ 这种形式,比如:

  • __init__:构造函数
  • __call__:对象可调用
  • __getitem__:支持下标访问

这些名字都是 Python 官方保留的。如果你自己乱写:

classDemo:
def__update__(self):
        print("custom update")

虽然现在没问题,但万一未来 Python 真的引入了 __update__ 作为标准方法,你的代码就直接和官方冲突了。PEP8(Python 官方编码规范)里明确说过:不要随便用双下划线包裹,除非你在实现内置协议。

面试官想听到的答案

总结一下,面试官问这个问题的时候,期望你能从几个角度回答:

  1. 可读性问题:下划线开头表示私有,滥用会让别人误解。
  2. 潜在冲突:__xxx__ 是官方保留,乱用可能和未来版本冲突。
  3. 特殊语义:_ 在 REPL 里有特殊意义,容易踩坑。
  4. 名字改写:双下划线触发 name mangling,调试不便。

能把这几点说清楚,基本就是满分回答了。

实际代码示例

最后我给你们写个完整的小例子,方便记忆:

classUser:
def__init__(self, name):
        self.name = name
        self._token = "secret"# 内部变量
        self.__password = "123456"# 名字改写
        self.__id__ = 1001# 潜在冲突

u = User("Tom")

print(u.name)         # 正常访问
print(u._token)       # 仍然能访问,但不推荐
# print(u.__password) # 报错,实际变量名被改写
print(u._User__password)  # 正确访问方式

你会发现 _token 还是能访问,说明单下划线只是约定俗成。而 __password 被改成 _User__password,这就是 name mangling 的效果。至于 __id__,虽然现在没问题,但它可能会和未来的 Python 特性冲突。

写在最后

我个人建议是:能不用下划线开头就别用。如果确实需要表达“内部变量”,用一个 _ 就够了,不要动不动搞双下划线。__xxx__ 这种形式,更是坚决避免,除非你真的在实现 Python 的魔术方法。

这类问题,面试官其实不是为了考你死记硬背,而是想看你对语言细节是否有理解,能不能避免在实际开发中掉坑里。等哪天你在大项目里维护别人写的代码,遇到一堆 __data__、_temp,你就会明白这个问题的重要性了。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领