从__init__讲起
很多人从Go/Java转Python的同学会疑惑
为什么python类的默认构造函数是 init? 不是__init 不是init__ 或者_init_ ?
其实是因为python的语法导致的。Python 里面没有pubcli private,甚至默认所有的类的属性都是公开的。让我们回想一下 **Python 的“访问控制”哲学: “We are all consenting adults.”(我们都是有共识的成年人)。
Python 不强制限制你访问任何属性或方法,而是相信开发者知道自己在做什么。
| 特性 | Java / C++ | Python |
|---|---|---|
public | 显式声明 | 默认就是公开的 |
private | 编译器强制禁止外部访问 | 没有真正的私有,只有约定 |
| 封装性 | 通过访问修饰符强制实现 | 通过命名约定 + 文档 + 信任实现 |
单下划线_ 、双下划线__ 开头或者结尾的区别
开头/结尾的命名具有不同的语义和用途。它们不是语法强制的访问控制,而是约定(convention)或语言机制(如名称改写、特殊方法)
| 命名形式 | 是否特殊处理 | 用途说明 | 能否外部访问 | 建议 | 外部使用代码示例 |
|---|---|---|---|---|---|
_xxx | ❌ 否 | 内部实现(约定) | ✅ 能 | 用作“受保护”成员 | print(a._xxx) |
__xxx | ✅ 是(改名) | 避免子类命名冲突 | ✅ 能(改名后) | 用于需隔离的内部属性 | print(a._A__xxx) |
_xxx_ | ❌ 否 | 无特殊含义 | ✅ 能 | 避免使用 | print(a._xxx_) |
__xxx__ | ✅ 是 | 魔术方法(语言保留) | ✅ 能 | 只用于标准魔术方法 | print(a.__xxx__()) |
在 Python 中,以单下划线 _ 或双下划线 __ 开头/结尾的命名具有不同的语义和用途。它们不是语法强制的访问控制,而是约定(convention)或语言机制(如名称改写、特殊方法)。下面详细解释四种常见形式的区别:
✅ 1. _xxx —— “内部使用”约定(Protected by convention)
含义:
- 表示这是一个内部实现细节,不应被外部代码直接依赖。
- 是一种软性提示,不是强制限制。
特点:
- 可以正常访问:
obj._xxx✅ - 不会被
from module import *导入(模块级别) - 常用于“受保护”的属性或方法(类似其他语言的
protected)
示例:
class MyClass:
def __init__(self):
self._internal = 42
obj = MyClass()
print(obj._internal) # 完全合法,但“不推荐”直接用
📌 用途:告诉其他开发者:“这是我的私有实现,未来可能变动,请别依赖它。”
✅ 2. __xxx —— 名称改写(Name Mangling)
含义:
- Python 自动重命名该属性,避免子类意外覆盖父类同名属性。
- 不是为了隐私或安全,而是解决命名冲突。
特点:
- 在类
MyClass中定义__private,实际存储为_MyClass__private - 仍可从外部访问:
obj._MyClass__private✅ - 仅在类定义内生效(必须出现在类作用域中)
示例:
class Parent:
def __init__(self):
self.__secret = "parent"
class Child(Parent):
def __init__(self):
super().__init__()
self.__secret = "child" # 不会覆盖父类的 __secret!
c = Child()
print(c._Parent__secret) # 输出: parent
print(c._Child__secret) # 输出: child
📌 用途:防止子类“不小心”覆盖父类的内部属性。
✅ 3. _xxx_ —— 通常无特殊含义(但建议避免)
含义:
- 前后各一个下划线,Python 不会对此做任何特殊处理。
- 但容易与魔术方法混淆,因此官方建议避免这种命名。
注意:
- 不是魔术方法(魔术方法是
__xxx__) - 可能让人误以为是特殊用途
示例(不推荐):
class BadExample:
def _method_(self): # ❌ 看起来怪怪的,像拼写错误
pass
🚫 PEP 8 建议:不要使用
_xxx_这种形式,除非你有非常特殊的理由。
✅ 4. __xxx__ —— 魔术方法 / 特殊方法(Magic Methods)
含义:
- Python 保留给语言本身使用的特殊方法。
- 用于实现运算符重载、内置函数支持、对象协议等。
特点:
- 由 Python 解释器在特定场景自动调用
- 永远不要自己发明
__xxx__名字(除非是文档定义的标准方法)
示例:
class Vector:
def __init__(self, x):
self.x = x
def __add__(self, other): # 支持 +
return Vector(self.x + other.x)
def __str__(self): # 支持 str() 和 print()
return f"Vector({self.x})"
📌 用途:让自定义对象支持
+,len(),print(),with,for等语言特性。
⚠️ 重要:不要创建自己的
__my_custom_method__!这属于 Python 保留命名空间。
🧾 总结对比表
| 命名形式 | 是否特殊处理 | 用途说明 | 能否外部访问 | 建议 |
|---|---|---|---|---|
_xxx | ❌ 否 | 内部实现(约定) | ✅ 能 | 用作“受保护”成员 |
__xxx | ✅ 是(改名) | 避免子类命名冲突(名称改写) | ✅ 能(改名后) | 用于真正需要隔离的内部属性 |
_xxx_ | ❌ 否 | 无特殊含义 | ✅ 能 | 避免使用 |
__xxx__ | ✅ 是 | 魔术方法(语言保留) | ✅ 能 | 只用于标准魔术方法 |
📚 附加建议(来自 PEP 8)
__double_leading_and_trailing__: 仅用于魔术方法_single_leading_underscore: 表示内部使用__double_leading_underscore: 触发名称改写- 不要使用
_single_trailing_underscore_,除非是为了避免与关键字冲突(如class_)
✅ 记住一句话:
“Python 给你自由,但也期望你遵守约定。”
下划线不是锁,而是礼貌的提醒。