前言
大家好,我是 倔强青铜三 。欢迎关注我,微信公众号:倔强青铜三。欢迎点赞、收藏、关注,一键三连!
欢迎来到 苦练Python第52天!
如果你已经跟着我从"青铜"肝到"黄金",今天咱们直接上 王者局!
今天要聊的是 Python 类里 最神秘、最常被忽视、却又最强大 的七把剑------魔术方法(magic methods):
- 生命周期三兄弟 :
__new__
、__init__
、__del__
- 字符串四天王 :
__repr__
、__str__
、__format__
、__bytes__
别被名字吓到,它们本质只做一件事:让你100%掌控对象的"生老病死"和"自我介绍"!
一口气吃透它们,写类就像写小说,生得优雅,死得体面,自我介绍惊艳全场!
🧬 生命周期三兄弟:生、老、死
1、 __new__
:对象的"出生证明"
官方定义 :
__new__
是 真正创建实例 的方法,返回一个裸对象 ,之后才轮到__init__
初始化。
🔍 触发时机
当你写下 MyClass()
时,Python 先调 __new__
,再调 __init__
。
🚨 注意
__new__
是 静态方法 (即使不写@staticmethod
),第一个参数是cls
。- 必须返回一个实例 ,否则
__init__
不会执行!
🎯 实战:单例模式(只生一个娃)
python
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
print("🔥 创建新实例")
cls._instance = super().__new__(cls)
else:
print("⚠️ 实例已存在,直接返回")
return cls._instance
def __init__(self, name):
self.name = name
print(f"🎁 __init__ 被调用,name={name}")
# 测试
a = Singleton("张三")
b = Singleton("李四")
print(a is b) # True
输出:
ini
🔥 创建新实例
🎁 __init__ 被调用,name=张三
⚠️ 实例已存在,直接返回
🎁 __init__ 被调用,name=李四
True
点评:
__new__
控制"生不生",__init__
控制"怎么养"。- 单例、缓存对象、不可变类(如
tuple
,str
)都靠它。
2、__init__
:对象的"成人礼"
官方定义:初始化实例,给对象"贴标签"。
🔍 触发时机
__new__
返回实例后,立即触发。
🎯 实战:初始化属性
python
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
print(f"🐶 汪汪!我是 {name},今年 {age} 岁!")
dog = Dog("旺财", 3)
输出:
🐶 汪汪!我是 旺财,今年 3 岁!
点评:
- 99%的类都会写
__init__
,但记住:它只是"装修队",不是"建筑队"!
3、 __del__
:对象的"临终遗言"
官方定义 :对象被垃圾回收前,最后一根救命稻草。
🔍 触发时机
- 引用计数为0时(CPython)。
- 不保证一定触发(循环引用、进程崩溃时可能不执行)。
🎯 实战:清理资源(文件句柄、网络连接)
python
class FileHandler:
def __init__(self, filepath):
self.file = open(filepath, 'w')
print("📂 文件已打开")
def __del__(self):
self.file.close()
print("🗑️ 文件已关闭")
f = FileHandler("test.txt")
del f # 手动触发
输出:
📂 文件已打开
🗑️ 文件已关闭
点评:
- 不要依赖
__del__
做关键逻辑 !推荐用with
语句或contextlib
。 - 调试时可在
__del__
打印日志,观察对象生命周期。
🎭 字符串四天王:自我介绍的艺术
4、 __repr__
:程序员的"身份证"
官方定义 :返回 开发者友好 的字符串,必须能还原对象!
🔍 触发时机
- 直接输入变量名(REPL)。
repr(obj)
、%r
、!r
格式化。
🎯 实战:让调试更轻松
python
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return f"Point({self.x}, {self.y})"
p = Point(3, 4)
print(repr(p)) # Point(3, 4)
点评:
- 黄金法则 :
eval(repr(obj)) == obj
尽量成立! - 没写
__repr__
时,默认打印<__main__.Point object at 0x...>
,调试想打人!
5、 __str__
:用户的"名片"
官方定义 :返回 用户友好 的字符串,面向人类阅读。
🔍 触发时机
print(obj)
、str(obj)
、%s
、{}
格式化。
🎯 实战:优雅打印对象
python
class Student:
def __init__(self, name, score):
self.name, self.score = name, score
def __str__(self):
return f"学生:{self.name},成绩:{self.score}"
s = Student("小明", 95)
print(s) # 学生:小明,成绩:95
点评:
- 如果没写
__str__
,Python 会退而求其次找__repr__
。 - 写日志、前端展示,全靠它!
6、 __format__
:格式化"化妆师"
官方定义 :支持
format()
和 f-string 的自定义格式。
🔍 触发时机
format(obj, '格式')
、f"{obj:格式}"
。
🎯 实战:自定义日期格式
python
from datetime import datetime
class MyDate:
def __init__(self, year, month, day):
self.date = datetime(year, month, day)
def __format__(self, fmt):
if fmt == "iso":
return self.date.strftime("%Y-%m-%d")
elif fmt == "us":
return self.date.strftime("%m/%d/%Y")
return str(self.date)
d = MyDate(2025, 8, 4)
print(f"ISO格式:{d:iso}") # ISO格式:2025-08-04
print(f"美式格式:{d:us}") # 美式格式:08/04/2025
点评:
- 金融、科学计算必备!让对象支持
f"{price:.2f}"
这种骚操作。
7、 __bytes__
:二进制"写真"
官方定义 :返回对象的 二进制表示,用于网络传输或存储。
🔍 触发时机
bytes(obj)
。
🎯 实战:序列化对象到字节
python
class Color:
def __init__(self, r, g, b):
self.r, self.g, self.b = r, g, b
def __bytes__(self):
return bytes([self.r, self.g, self.b])
c = Color(255, 0, 128)
print(bytes(c)) # b'\xff\x00\x80'
点评:
- 写 socket、文件存储时,直接
bytes(obj)
搞定!
🛠️ 速查表:七剑对比
魔术方法 | 触发场景 | 作用 | 常见用途 |
---|---|---|---|
__new__ |
创建实例时 | 控制"生不生" | 单例、缓存、不可变类 |
__init__ |
__new__ 之后 |
初始化属性 | 99%的类都会写 |
__del__ |
垃圾回收前 | 清理资源 | 文件句柄、网络连接 |
__repr__ |
调试、repr() | 开发者友好打印 | 日志、REPL |
__str__ |
打印、str() | 用户友好打印 | 前端展示 |
__format__ |
format() | 自定义格式化 | 日期、数字美化 |
__bytes__ |
bytes() | 二进制表示 | 网络传输、存储 |
🧪 实战项目:一个"优雅至死"的日志对象
python
class LogEntry:
def __init__(self, level, msg):
self.level, self.msg = level, msg
def __repr__(self):
return f"LogEntry({self.level!r}, {self.msg!r})"
def __str__(self):
return f"[{self.level}] {self.msg}"
def __format__(self, fmt):
if fmt == "json":
return f'{{"level":"{self.level}", "msg":"{self.msg}"}}'
return str(self)
def __bytes__(self):
return str(self).encode("utf-8")
# 测试
log = LogEntry("ERROR", "磁盘已满")
print(repr(log)) # LogEntry('ERROR', '磁盘已满')
print(str(log)) # [ERROR] 磁盘已满
print(f"{log:json}") # {"level":"ERROR", "msg":"磁盘已满"}
print(bytes(log)) # b'[ERROR] \xe7\xa3\x81\xe7\x9b\x98\xe5\xb7\xb2\xe6\xbb\xa1'
🧩 常见坑 & 调试技巧
坑 | 解释 | 解决 |
---|---|---|
__del__ 不执行 |
循环引用、进程崩溃 | 用 with 或 weakref |
__repr__ 写太复杂 |
调试时信息爆炸 | 保持简洁,能还原即可 |
__str__ 缺失 |
打印时显示 <object at ...> |
至少写 __repr__ |
__format__ 不支持 |
f-string 报错 | 实现 __format__ |
✅ 一句话总结
- 生命周期三兄弟 :
__new__
管生,__init__
管养,__del__
管埋。 - 字符串四天王 :
__repr__
给开发者看,__str__
给用户看,__format__
化妆,__bytes__
整容成二进制。
掌握这七剑,你的对象 生得优雅,死得体面,自我介绍惊艳全场!
🚀 互动时间
你最常用哪个魔术方法?
A. __init__
(初始化狂魔)
B. __repr__
(调试党)
C. __str__
(打印狂魔)
D. __new__
(单例控)
评论区告诉我你的答案 + 骚操作!
最后感谢阅读!欢迎关注我,微信公众号 :
倔强青铜三
。一键三连(点赞、收藏、关注)!