python类中私有成员变量以双下划线开头,推荐用@property装饰器定义getter/setter实现封装;它使方法像属性调用,支持验证、日志等逻辑,且符合pythonic风格。

在 Python 类中,私有成员变量通常以双下划线开头(如 __name),但 Python 并不强制限制访问,而是通过命名约定和属性机制来实现“逻辑上的私有”。定义访问器(Getter/Setter)最推荐的方式是使用 @property 装饰器,它让方法像属性一样被调用,同时保持封装性。
用 @property 定义 Getter 和 Setter
这是最 Pythonic 的做法。它把字段访问变成受控的函数调用,便于后期加入验证、日志或计算逻辑。
class Person:
def __init__(self, name, age):
self.__name = name # 私有成员变量
self.__age = age
# Getter:用 @property 修饰,调用时无需括号
@property
def name(self):
return self.__name
# Setter:用 @.setter 修饰,赋值时自动触发
@name.setter
def name(self, value):
if not isinstance(value, str) or not value.strip():
raise ValueError("Name must be a non-empty string")
self.__name = value.strip()
@property
def age(self):
return self.__age
@age.setter
def age(self, value):
if not isinstance(value, int) or value 150:
raise ValueError("Age must be an integer between 0 and 150")
self.__age = value
使用示例:
p = Person("Alice", 30)
print(p.name) # → "Alice"(调用 getter)
p.name = "Bob" # → 触发 setter,校验后赋值
p.age = 35 # → 合法赋值
# p.__name = "hacked" # 不会修改私有变量,只是新增一个新属性
注意 __name 的名称改写(Name Mangling)
Python 会把 __name 自动重命名为 _Person__name,目的是避免子类意外覆盖。你仍可通过 obj._Person__name 访问(不推荐),但 @property 提供了干净、安全的接口。
为什么不直接用普通方法 get_name() / set_name()?
- 语法不自然:
p.get_name()vsp.name - 失去属性访问的简洁性和一致性
- 无法参与数据绑定、序列化等依赖属性协议的场景
需要只读属性?只定义 @property,不加 setter 即可
@property
def birth_year(self):
return 2024 - self.__age # 只读计算属性
这样 p.birth_year 可读不可写,尝试 p.birth_year = 1990 会报 AttributeError。
不复杂但容易忽略:真正的封装靠的是约定 + @property 控制,而不是靠隐藏字段名。











