飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 认识Python

    • Python 历史与特点
    • Python 2 与 Python 3 的核心差异
    • 安装与运行 Python 2.7.18
    • 编码规范 PEP 8
  • 第2章 基础语法

    • 变量与对象
    • 数字类型
    • 字符串 str
    • Unicode 字符串
    • 运算符
    • 空值 None
  • 第3章 流程控制

    • if 条件语句
    • if-else 条件语句
    • if-elif-else 多分支
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 与 xrange
    • 循环控制:break、continue、pass
    • 循环 else 子句
  • 第4章 数据结构

    • 列表基础
    • 列表方法
    • 列表推导式
    • 元组
    • 字典基础
    • 字典方法
    • 字典循环技巧
    • 集合
    • 序列解包
    • 序列比较
  • 第5章 函数

    • 定义函数
    • 参数传递机制
    • 默认参数
    • 关键字参数
    • 可变参数
    • Lambda 表达式
    • 文档字符串
    • 函数对象
  • 第6章 模块与包

    • import 导入
    • 模块搜索路径
    • name 与主程序
    • 编译文件 .pyc 与 .pyo
    • 包结构
    • dir() 函数
  • 第7章 文件与IO

    • 打开与关闭文件
    • 文件读写方法
    • with 上下文管理器
    • 格式化输出:% 操作符
    • 格式化输出:str.format()
    • JSON 序列化
  • 第8章 面向对象

    • 类定义与实例化
    • init 构造方法
    • 类变量与实例变量
    • 方法调用与 self
    • 继承基础
    • 多重继承
    • 新式类与旧式类
    • 私有变量与名称改写
    • 属性装饰器 property
    • 类方法与静态方法
    • 魔术方法
    • 空类与数据记录
  • 第9章 异常处理

    • 异常类型
    • try-except
    • try-except-else-finally
    • 抛出异常 raise
    • 自定义异常
    • with 语句与上下文管理器
  • 第10章 迭代器与生成器

    • 迭代器协议
    • 生成器函数
    • 生成器表达式
    • itertools模块
  • 第11章 标准库精要

    • os模块
    • sys模块
    • datetime模块
    • re模块
    • json模块
    • collections模块
    • math与random模块
    • urllib2与网络请求
    • subprocess与命令执行
    • threading与并发
    • unittest与测试
    • 虚拟环境与包管理
  • 第12章 工程实践

    • 调试技巧
    • 性能分析
    • 文档与注释
    • 下一步学习

私有变量与名称改写

Python 没有真正的"私有"成员(像 C++ 或 Java 的 private)。所有属性都是公开的,但社区约定和语言机制提供了两种层次的"隐私"保护:单下划线前缀(约定)和双下划线前缀(名称改写)。

单下划线前缀:_name

class BankAccount(object):
    def __init__(self):
        self._balance = 0       # "内部使用,不要直接访问"
    
    def deposit(self, amount):
        self._balance += amount
    
    def get_balance(self):
        return self._balance

_balance 前面的单下划线是一种温和提示:它告诉其他程序员"这个属性是内部实现细节,外部不应直接访问"。但技术上仍然可以访问:

account = BankAccount()
account.deposit(100)
print account._balance      # 100,可以访问,但不推荐

单下划线前缀还会影响 from module import * 的行为:以 _ 开头的名称不会被导入(除非在 __all__ 中显式列出)。

双下划线前缀:__name

class Mapping(object):
    def __init__(self, iterable):
        self.items_list = []
        self.__update(iterable)
    
    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)
    
    __update = update     # 私有副本

class MappingSubclass(Mapping):
    def update(self, keys, values):
        for item in zip(keys, values):
            self.items_list.append(item)

m = MappingSubclass([1, 2, 3])
print m.items_list      # [1, 2, 3]

__update 是 Mapping 类的私有方法。当 MappingSubclass 覆盖 update 时,Mapping.__init__ 中调用的 self.__update 仍然指向 Mapping.update,不会被 MappingSubclass.update 覆盖。

名称改写机制

双下划线前缀会触发名称改写(name mangling):Python 把 __name 改写为 _ClassName__name:

class MyClass(object):
    def __init__(self):
        self.__secret = 42

obj = MyClass()
print obj.__secret          # AttributeError: 'MyClass' object has no attribute '__secret'
print obj._MyClass__secret  # 42!名称被改写了

__secret 在类内部被改写为 _MyClass__secret。这种改写发生在编译时,不考虑标识符的句法位置——只要在类定义内部出现,就会被改写。

改写的目的

名称改写不是为了安全(仍然可以通过 _ClassName__name 访问),而是为了避免子类意外覆盖:

class Base(object):
    def __init__(self):
        self.__value = 10
    
    def get_value(self):
        return self.__value

class Derived(Base):
    def __init__(self):
        Base.__init__(self)
        self.__value = 20       # 不会覆盖 Base 的 __value!
    
    def get_derived_value(self):
        return self.__value

d = Derived()
print d.get_value()             # 10,Base 的 __value
print d.get_derived_value()     # 20,Derived 的 __value
print d._Base__value            # 10
print d._Derived__value         # 20

Base.__value 被改写为 _Base__value,Derived.__value 被改写为 _Derived__value,两者互不干扰。

使用建议

前缀含义访问方式
无公开 APIobj.name
_内部实现,可以访问但不推荐obj._name
__私有实现,防止子类覆盖obj._ClassName__name
__xxx__魔术方法,Python 特殊用途obj.__init__()

原则:

  1. 公开 API 不要加下划线
  2. 内部实现用单下划线 _
  3. 需要防止子类覆盖时用双下划线 __
  4. 不要滥用双下划线——它让调试和测试更困难
  5. 绝对不要用 __ 来"保护"数据,Python 没有真正的私有

与 C++/Java 的对比

语言私有机制实际效果
C++private编译期检查,无法访问
Javaprivate编译期检查,反射可绕过
Python__name名称改写,约定保护

Python 的设计哲学是"我们都是成年人"(We're all consenting adults)——相信程序员会遵守约定,而不是用语言机制强制限制。这种设计让元编程、调试、测试更灵活,但也要求程序员有更高的自律。

上一页
新式类与旧式类
下一页
属性装饰器 property