文章目录
- [1. 私有访问限制](#1. 私有访问限制)
- [2. 私有的意义](#2. 私有的意义)
- [3. 实现原理:Name Mangling](#3. 实现原理:Name Mangling)
- [4. 注意事项](#4. 注意事项)
本文参考视频链接:
Python 中没有像 Java、C++ 那样通过 private
关键字声明私有变量的语法,而是通过变量/方法的命名规则实现私有特性,核心规则如下:
将变量/方法的名称前添加「至少两个下划线」,且名称后至多添加一个下划线,即可将其定义为私有成员。
最常用的形式是「前加两个下划线」:
python
class A:
def __init__(self):
# 定义私有变量 __v(前加两个下划线)
self.__v = 0 # 私有变量
self.public_v = 1 # 普通公有变量
# 定义私有方法 __print_v(前加两个下划线)
def __print_v(self):
print(self.__v)
# 公有方法:内部访问私有变量和私有方法
def access_private(self):
self.__v += 1 # 类内可修改私有变量
self.__print_v() # 类内可调用私有方法
1. 私有访问限制
私有变量/方法的核心作用是限制外部直接访问,仅允许在类的内部(即类的成员函数中)使用,具体表现为:
① 类外直接访问私有成员会报错
若在类的外部尝试直接读取或修改私有变量/调用私有方法,Python 会抛出 AttributeError
,提示"对象没有该属性":
python
obj = A()
# 1. 访问普通公有变量:正常
print(obj.public_v) # 输出:1
# 2. 直接访问私有变量:报错
print(obj.__v) # 报错:AttributeError: 'A' object has no attribute '__v'
# 3. 直接调用私有方法:报错
obj.__print_v() # 报错:AttributeError: 'A' object has no attribute '__print_v'
② 类内可自由访问私有成员
通过类的公有方法,可以间接访问或修改私有成员(符合"封装"思想,控制访问入口):
python
obj = A()
obj.access_private() # 输出:1(类内成功修改并打印私有变量 __v)
2. 私有的意义
私有变量的设计并非"为了隐藏而隐藏",而是为了解决实际开发中的问题,主要有两大价值:
① 强化封装性,防止误用
- 通过命名明确告知代码使用者:"该成员仅为类内部逻辑服务,外部不应直接操作",避免因外部随意修改导致类的内部逻辑混乱。
- 例如:一个"用户类"中,密码
__password
设为私有,仅通过set_password()
(校验密码复杂度)和get_password()
(加密返回)两个公有方法访问,确保密码的安全性。
② 避免继承中的成员覆盖问题
当子类与父类存在同名成员时,子类会覆盖父类的成员,可能导致父类的逻辑失效。私有成员不会被继承覆盖,可解决此问题。
python
class A:
# 父类的公有类属性:存储合法关键字
valid_keywords = ["A"]
def __init__(self, **kwargs):
# 仅处理 valid_keywords 中的关键字
for k, v in kwargs.items():
if k in self.valid_keywords:
print(f"A: {k}={v}")
class B(A):
# 子类覆盖父类的 valid_keywords
valid_keywords = ["B"]
def __init__(self, **kwargs):
# 子类处理自己的关键字
for k, v in kwargs.items():
if k in self.valid_keywords:
print(f"B: {k}={v}")
# 调用父类初始化
super().__init__(** kwargs)
# 实例化 B,传入 A=2、B=3
obj = B(A=2, B=3)
# 输出:B: B=3(父类 A 的 __init__ 中,self.valid_keywords 被子类覆盖为 ["B"],故 A=2 未被处理)

将父类和子类的 valid_keywords
改为私有变量 __valid_keywords
,私有成员不会被覆盖,父类逻辑正常:
python
class A:
# 父类的私有类属性(前加两个下划线)
__valid_keywords = ["A"]
def __init__(self, **kwargs):
# 访问父类私有属性 __valid_keywords(不会被子类覆盖)
for k, v in kwargs.items():
if k in self.__valid_keywords:
print(f"A: {k}={v}")
class B(A):
# 子类的私有类属性(与父类私有属性名称相同,但不会覆盖)
__valid_keywords = ["B"]
def __init__(self, **kwargs):
# 访问子类私有属性 __valid_keywords
for k, v in kwargs.items():
if k in self.__valid_keywords:
print(f"B: {k}={v}")
super().__init__(** kwargs)
# 实例化 B,传入 A=2、B=3
obj = B(A=2, B=3)
# 输出:B: B=3 → A: A=2(父类私有属性未被覆盖,正常处理 A=2)

③ 防止类内方法被意外重载
若将类内核心方法设为私有(如 __core_logic()
),子类无法重载该方法,可确保类内部调用时始终执行自身定义的逻辑,避免子类重载导致的逻辑混乱。
3. 实现原理:Name Mangling
Python 的私有机制并非"强制禁止访问",而是通过编译期的名字改编(Name Mangling)实现,本质是"给私有成员改了个名字",具体规则如下:
当 Python 编译代码时,会将类中定义的私有成员 __name
(前两个下划线)自动改名为:
_类名__name
(格式:下划线 + 类名 + 原私有成员名)
例如:
- 类
A
中的私有变量__v
,会被改编为_A__v
; - 类
B
中的私有变量__valid_keywords
,会被改编为_B__valid_keywords
。

通过访问改编后的名称,可在类外"绕过"私有限制(不推荐,违背封装设计):
python
obj = A()
# 直接访问改编后的私有变量(类外)
print(obj._A__v) # 输出:0(未报错,成功读取私有变量 __v)
# 直接修改改编后的私有变量(类外)
obj._A__v = 5
obj.access_private() # 输出:6(私有变量被修改)
- Python 的私有机制是「语法层面的命名隐藏」,而非「底层强制限制」,无法完全阻止外部访问(与 Java/C++ 的
private
不同); - 名字改编发生在编译期 (将源代码转为字节码时),仅对类定义内部的私有成员生效,动态操作(如
setattr
、直接修改__dict__
)不会触发改编。
4. 注意事项
由于私有机制依赖编译期的名字改编,以下场景中私有特性会失效,需特别注意:
① 动态赋值私有成员不触发名字改编
通过 setattr
函数动态给对象添加"私有命名"的属性,不会触发名字改编,本质是普通公有属性:
python
obj = A()
# 动态添加属性 __v1(前两个下划线)
setattr(obj, "__v1", 10)
# 直接访问 __v1:正常(未被改编)
print(obj.__v1) # 输出:10(无报错,说明是公有属性)
② 直接操作 __dict__
不触发名字改编
对象的 __dict__
属性存储了其所有实例属性,直接修改 __dict__
添加"私有命名"的键,不会触发名字改编:
python
obj = A()
# 直接给 __dict__ 添加键 __v2
obj.__dict__["__v2"] = 20
# 直接访问 __v2:正常
print(obj.__v2) # 输出:20(无报错)
③ 外部函数赋值给类方法不触发名字改编
若将类外定义的函数赋值给类的"私有方法名",该方法不会被改编,本质是公有方法:
python
# 类外定义函数
def external_func(self):
print("外部函数")
class A:
# 将外部函数赋值给"私有方法名" __ext_func
__ext_func = external_func
obj = A()
# 直接调用 __ext_func:正常(未被改编)
obj.__ext_func() # 输出:外部函数(无报错)
Python 社区的设计哲学是"信任开发者",私有机制更多是"提醒"而非"强制"。实际开发中,若仅需"告知外部不应访问",也可使用「单下划线命名」(如 _v
)------虽无访问限制,但约定俗成表示"内部成员,外部慎用"。