一、一句话结论
| 属性名 | 示例 | 本质 | 能否自定义 |
|---|---|---|---|
xx | name,age | 普通属性,开发者自由定义 | ✅ 随意定义 |
__xx__ | __init__,__str__ | 魔法方法 / 特殊属性,Python 语言内置协议 | ⚠️ 只能实现已有协议,不能乱发明 |
核心区别:
👉xx是写给业务用的
👉__xx__是写给 Python 解释器用的
二、xx—— 普通属性(Normal Attributes)
1️⃣ 是什么
classUser:def__init__(self,name):self.name=name# 普通属性self.age=18# 普通属性- 完全由开发者控制
- 没有任何特殊含义
- 不影响 Python 的对象行为
2️⃣ 能做什么
user=User("Alice")user.name# 读user.name="Bob"# 写user.email="a@b.com"# 动态新增✅用途:存数据、状态、配置
三、__xx__—— 魔法方法 / 特殊属性(Magic Methods)
1️⃣ 是什么
classUser:def__init__(self,name):self.name=namedef__str__(self):returnf"User({self.name})"def__len__(self):returnlen(self.name)这些是Python 数据模型的官方接口,也叫dunder methods(double underscore)。
2️⃣ 为什么叫“魔法”
因为它们会被Python 解释器自动调用:
user=User("Alice")print(user)# 自动调用 user.__str__()len(user)# 自动调用 user.__len__()没有显式调用,但它们被执行了。
四、最常见的__xx__分类速查
🔧 对象生命周期
| 方法 | 触发时机 |
|---|---|
__init__ | 对象初始化 |
__new__ | 对象创建(更底层) |
__del__ | 对象销毁 |
classDemo:def__init__(self):print("对象创建了")def__del__(self):print("对象销毁了")🖨️ 字符串表示
| 方法 | 区别 |
|---|---|
__str__ | str(obj)、print(obj),面向用户 |
__repr__ | repr(obj)、交互式终端,面向开发者 |
classUser:def__init__(self,name):self.name=namedef__str__(self):returnf"👤{self.name}"def__repr__(self):returnf"User(name={self.name!r})"u=User("Alice")print(u)# 👤 Aliceprint(repr(u))# User(name='Alice')✅黄金法则:__repr__应该是无歧义的,__str__应该是可读的
🧮 运算符重载
| 方法 | 对应操作 |
|---|---|
__add__ | + |
__sub__ | - |
__eq__ | == |
__lt__ | < |
__gt__ | > |
classMoney:def__init__(self,amount):self.amount=amountdef__add__(self,other):returnMoney(self.amount+other.amount)def__eq__(self,other):returnself.amount==other.amount m1=Money(10)m2=Money(20)m3=m1+m2print(m3.amount)# 30📦 容器行为
| 方法 | 对应操作 |
|---|---|
__len__ | len(obj) |
__getitem__ | obj[i] |
__setitem__ | obj[i] = x |
__iter__ | for x in obj |
classMyList:def__init__(self,items):self.items=itemsdef__len__(self):returnlen(self.items)def__getitem__(self,index):returnself.items[index]ml=MyList([1,2,3])print(len(ml))# 3print(ml[1])# 2🎯 可调用对象
classAdder:def__call__(self,a,b):returna+b add=Adder()print(add(1,2))# 3 ← 对象像函数一样被调用五、⚠️ 最重要的一条规则
__xx__只能实现 Python 已有的协议,不要自己发明
❌ 错误示范:
classUser:def__my_custom__method__(self):# ❌ 这是灾难pass✅ 正确做法:
classUser:defmy_custom_method(self):# ✅ 普通方法pass原因:
- Python 未来版本可能会占用这个名字
- 破坏代码的可读性和一致性
- 其他开发者会困惑
六、和__xx(双下划线无后缀)的区别
| 命名 | 示例 | 用途 |
|---|---|---|
xx | name | 普通属性 |
__xx | __password | 名称修饰(Name Mangling) |
__xx__ | __init__ | 魔法方法(语言协议) |
classDemo:normal=1__mangled=2# 变成 _Demo__mangleddef__init__(self):pass# 魔法方法def__len__(self):return0# 魔法方法七、内置特殊属性一览
obj.__class__# 对象的类obj.__dict__# 实例属性字典obj.__module__# 所在模块cls.__bases__# 父类元组cls.__mro__# 方法解析顺序func.__name__# 函数名func.__doc__# 文档字符串八、实战判断口诀
🔍看到
__xx__就问自己:
- 这是 Python 官方定义的吗?
- 它会在什么场景下被自动调用?
- 我是不是在实现一个协议?
🔍看到
xx就问自己:
- 这是业务数据吗?
- 需不需要控制访问?(→ property)
- 是不是内部实现?(→
_xx)
九、总结对照表
| 特性 | xx | __xx__ |
|---|---|---|
| 定义权 | 开发者 | Python 官方 |
| 是否魔法 | ❌ 否 | ✅ 是 |
| 是否自动调用 | ❌ 否 | ✅ 是 |
| 能否乱发明 | ✅ 可以 | ❌ 绝对不行 |
| 典型用途 | 业务数据 | 协议实现 |
十、一句话终极总结
xx是给代码用的,__xx__是给 Python 用的。
前者你说了算,后者 Python 说了算。