前言

大家好,我是 倔强青铜三。欢迎关注我,微信公众号:倔强青铜三。欢迎点赞、收藏、关注,一键三连!

欢迎来到 苦练Python第52天

如果你已经跟着我从“青铜”肝到“黄金”,今天咱们直接上 王者局

今天要聊的是 Python 类里 最神秘、最常被忽视、却又最强大 的七把剑——魔术方法(magic methods)

  • 生命周期三兄弟__new____init____del__
  • 字符串四天王__repr____str____format____bytes__

别被名字吓到,它们本质只做一件事:让你100%掌控对象的“生老病死”和“自我介绍”

一口气吃透它们,写类就像写小说,生得优雅,死得体面,自我介绍惊艳全场


生命周期三兄弟:生、老、死

1、 __new__:对象的“出生证明”

触发时机

当你写下 MyClass() 时,Python 先调 __new__,再调 __init__

注意

  • __new__静态方法(即使不写 @staticmethod),第一个参数是 cls
  • 必须返回一个实例,否则 __init__ 不会执行!

实战:单例模式(只生一个娃)

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

输出

 创建新实例
 __init__ 被调用,name=张三
️ 实例已存在,直接返回
 __init__ 被调用,name=李四
True

点评

  • __new__ 控制“生不生”,__init__ 控制“怎么养”。
  • 单例、缓存对象、不可变类(如 tuple, str)都靠它。

2、__init__:对象的“成人礼”

触发时机

__new__ 返回实例后,立即触发。

实战:初始化属性

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)。
  • 不保证一定触发(循环引用、进程崩溃时可能不执行)。

实战:清理资源(文件句柄、网络连接)

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 格式化。

实战:让调试更轻松

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{} 格式化。

实战:优雅打印对象

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(obj, '格式')f"{obj:格式}"

实战:自定义日期格式

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)

实战:序列化对象到字节

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'xffx00x80'

点评

  • 写 socket、文件存储时,直接 bytes(obj) 搞定!

️ 速查表:七剑对比

魔术方法触发场景作用常见用途
__new__创建实例时控制“生不生”单例、缓存、不可变类
__init____new__ 之后初始化属性99%的类都会写
__del__垃圾回收前清理资源文件句柄、网络连接
__repr__调试、repr()开发者友好打印日志、REPL
__str__打印、str()用户友好打印前端展示
__format__format()自定义格式化日期、数字美化
__bytes__bytes()二进制表示网络传输、存储

实战项目:一个“优雅至死”的日志对象

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] xe7xa3x81xe7x9bx98xe5xb7xb2xe6xbbxa1'

常见坑 & 调试技巧

解释解决
__del__ 不执行循环引用、进程崩溃withweakref
__repr__ 写太复杂调试时信息爆炸保持简洁,能还原即可
__str__ 缺失打印时显示 <object at ...>至少写 __repr__
__format__ 不支持f-string 报错实现 __format__

一句话总结

  • 生命周期三兄弟__new__ 管生,__init__ 管养,__del__ 管埋。
  • 字符串四天王__repr__ 给开发者看,__str__ 给用户看,__format__ 化妆,__bytes__ 整容成二进制。

互动时间

你最常用哪个魔术方法?

A. __init__(初始化狂魔)
B. __repr__(调试党)
C. __str__(打印狂魔)
D. __new__(单例控)

评论区告诉我你的答案 + 骚操作!

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]