2025-10-07 Python不基础 19——私有对象

文章目录

  • [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)------虽无访问限制,但约定俗成表示"内部成员,外部慎用"。

相关推荐
greentea_20133 小时前
Codeforces Round 65 C. Round Table Knights(71)
c语言·开发语言·算法
小秋学嵌入式-不读研版3 小时前
C61-结构体数组
c语言·开发语言·数据结构·笔记·算法
可触的未来,发芽的智生3 小时前
触摸未来2025.10.04:当神经网络拥有了内在记忆……
人工智能·python·神经网络·算法·架构
Evand J4 小时前
组合导航的MATLAB例程,二维平面上的CKF滤波,融合IMU和GNSS数据,仿真,观测为X和Y轴的坐标,附代码下载链接
开发语言·matlab·平面·imu·组合导航
蔗理苦4 小时前
2025-10-07 Python不基础 20——全局变量与自由变量
开发语言·python
xiaohanbao094 小时前
理解神经网络流程
python·神经网络
韩立学长4 小时前
【开题答辩实录分享】以《基于Python的旅游网站数据爬虫研究》为例进行答辩实录分享
python·旅游
-森屿安年-4 小时前
C++ 类与对象
开发语言·c++
小蒜学长5 小时前
springboot基于javaweb的小零食销售系统的设计与实现(代码+数据库+LW)
java·开发语言·数据库·spring boot·后端