私有字段必须在类内部(如__init__中)定义,不可在类外定义或直接访问;应通过@property和@xxx.setter封装读写,双下划线仅触发名称混淆而非强制访问控制。

私有字段本身不能在类外“定义”,只能在类内部声明。你看到的报错,通常是因为试图在类外部访问或修改一个已在类内定义的私有字段(比如 self.__field),而不是在类外“定义”它。
Python 中以双下划线开头的字段(如 __bankAccount)是类内私有字段,它的限制逻辑是:
→ 类外无法通过 obj.__field 直接读写;
→ 但字段本身必须在 __init__ 或其他类方法中定义,否则连类内都用不了。
私有字段只能在类内部定义和初始化
class Student:
def __init__(self, name, account):
self.name = name
self.__account = account # ✅ 正确:在 __init__ 中定义私有字段
s = Student("李四", "123456")
# s.__account # ❌ 报 AttributeError:外部不可见
# s.__new_field = "xxx" # ❌ 这不是“定义私有字段”,只是动态加公有属性,名字不带 __,不算私有
你不能也不该在类外面写 s.__account = ... 来“定义私有字段”——这只会创建一个名为 __account 的普通公有属性(不触发名称混淆),和类内的 self.__account 完全无关。
常见错误与对应处理方式
-
错误:直接点调用私有字段
print(s.__account) # AttributeError
→ 处理:改用
@property提供受控访问@property def account(self): return self.__account调用时:
print(s.account)—— 看似字段,实为方法,安全且简洁。 -
错误:想外部赋值却写成
s.__account = 789s.__account = 789 # ❌ 不报错,但这是新建公有属性,原私有字段没变 print(s.account) # 还是原来的 123456
→ 处理:配
@account.setter@account.setter def account(self, value): if isinstance(value, str) and len(value) >= 6: self.__account = value else: raise ValueError("账号至少6位")调用时:
s.account = "789012"—— 自动走校验逻辑。 -
错误:子类里直接访问父类私有字段
class GradStudent(Student): def show(self): return self.__account # ❌ 找不到,因为被改名为 _Student__account→ 处理:父类应提供
@property,或把字段设计为单下划线_account(约定私有,非强制);真要继承访问,就别用双下划线。
补充:为什么 __field 不是真正封锁?
Python 不做访问拦截,而是自动重命名:self.__account → 实际存为 self._Student__account。
所以技术上可以这么绕过:
print(s._Student__account) # 能打印,但不推荐
这不是 bug,是设计:防误用,不防故意破解。业务代码里不该出现这种写法。
总结关键点
- 私有字段必须在类的方法(通常是
__init__)中定义,不能在类外“定义” - 类外不能用
obj.__xxx访问,会报AttributeError - 推荐统一用
@property+@xxx.setter封装读写,兼顾安全与易用 - 双下划线触发名称混淆,不是权限系统;靠约定和封装意识比靠语法限制更重要











