Python 常见特殊方法详解:__new__、__init__、__del__、__str__、__repr__ 与 __getattribute__

1. 引言

Python 中的「特殊方法」(也叫魔术方法、双下划线方法)是解释器在特定时机自动调用的钩子。它们让自定义类能够像内置类型一样参与对象创建、销毁、字符串转换、属性访问等操作。本文围绕六个最常见的特殊方法展开,并给出可直接运行的示例。

2. 六个常见特殊方法速览

特殊方法 触发时机 典型用途
__new__() 对象实例化时第一个被调用 控制对象创建过程,常用于不可变类型、单例
__init__() 对象创建完成、返回给调用者之前 初始化实例属性
__del__() 对象被垃圾回收时 释放外部资源(慎用)
__str__() 对实例调用 str() / print() 面向人类可读的输出
__repr__() 对实例调用 repr() / 交互式回显 面向机器可读、可复现的输出
__getattribute__() 每次访问属性时 属性访问拦截、代理、日志

3. __new__():对象创建的第一站

__new__() 是一个静态方法 (即使不写 @staticmethod 也会被隐式当作静态方法),它的第一个参数是类本身 cls,负责创建并返回 实例。只有它返回了该类的实例,__init__() 才会被调用。

python 复制代码
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            print("__new__ 被调用:创建实例")
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, name):
        print("__init__ 被调用")
        self.name = name


a = Singleton("A")
b = Singleton("B")
print(a is b)      # True
print(a.name)      # B(第二次 __init__ 覆盖了属性)

要点:

  • __new__() 返回的不是本类实例时,__init__() 不会被调用。
  • 继承不可变类型(如 int、str、tuple)时,必须重写 __new__() 才能改变创建结果。
python 复制代码
class PositiveInt(int):
    def __new__(cls, value):
        if value < 0:
            raise ValueError("必须是非负数")
        return super().__new__(cls, value)


print(PositiveInt(10))   # 10
# PositiveInt(-1)        # ValueError: 必须是非负数

4. __init__():实例初始化

__init__() 在实例已经创建之后被调用,用于设置初始状态。它不应该返回任何值 (返回非 None 会抛 TypeError)。

python 复制代码
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y


p = Point(1, 2)
print(p.x, p.y)   # 1 2

__new__() 与 __init__() 的执行顺序:
#mermaid-svg-jSCisJESn03KIQUS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jSCisJESn03KIQUS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jSCisJESn03KIQUS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jSCisJESn03KIQUS .error-icon{fill:#552222;}#mermaid-svg-jSCisJESn03KIQUS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jSCisJESn03KIQUS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jSCisJESn03KIQUS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jSCisJESn03KIQUS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jSCisJESn03KIQUS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jSCisJESn03KIQUS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jSCisJESn03KIQUS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jSCisJESn03KIQUS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jSCisJESn03KIQUS .marker.cross{stroke:#333333;}#mermaid-svg-jSCisJESn03KIQUS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jSCisJESn03KIQUS p{margin:0;}#mermaid-svg-jSCisJESn03KIQUS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jSCisJESn03KIQUS .cluster-label text{fill:#333;}#mermaid-svg-jSCisJESn03KIQUS .cluster-label span{color:#333;}#mermaid-svg-jSCisJESn03KIQUS .cluster-label span p{background-color:transparent;}#mermaid-svg-jSCisJESn03KIQUS .label text,#mermaid-svg-jSCisJESn03KIQUS span{fill:#333;color:#333;}#mermaid-svg-jSCisJESn03KIQUS .node rect,#mermaid-svg-jSCisJESn03KIQUS .node circle,#mermaid-svg-jSCisJESn03KIQUS .node ellipse,#mermaid-svg-jSCisJESn03KIQUS .node polygon,#mermaid-svg-jSCisJESn03KIQUS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jSCisJESn03KIQUS .rough-node .label text,#mermaid-svg-jSCisJESn03KIQUS .node .label text,#mermaid-svg-jSCisJESn03KIQUS .image-shape .label,#mermaid-svg-jSCisJESn03KIQUS .icon-shape .label{text-anchor:middle;}#mermaid-svg-jSCisJESn03KIQUS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jSCisJESn03KIQUS .rough-node .label,#mermaid-svg-jSCisJESn03KIQUS .node .label,#mermaid-svg-jSCisJESn03KIQUS .image-shape .label,#mermaid-svg-jSCisJESn03KIQUS .icon-shape .label{text-align:center;}#mermaid-svg-jSCisJESn03KIQUS .node.clickable{cursor:pointer;}#mermaid-svg-jSCisJESn03KIQUS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jSCisJESn03KIQUS .arrowheadPath{fill:#333333;}#mermaid-svg-jSCisJESn03KIQUS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jSCisJESn03KIQUS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jSCisJESn03KIQUS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jSCisJESn03KIQUS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jSCisJESn03KIQUS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jSCisJESn03KIQUS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jSCisJESn03KIQUS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jSCisJESn03KIQUS .cluster text{fill:#333;}#mermaid-svg-jSCisJESn03KIQUS .cluster span{color:#333;}#mermaid-svg-jSCisJESn03KIQUS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jSCisJESn03KIQUS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jSCisJESn03KIQUS rect.text{fill:none;stroke-width:0;}#mermaid-svg-jSCisJESn03KIQUS .icon-shape,#mermaid-svg-jSCisJESn03KIQUS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jSCisJESn03KIQUS .icon-shape p,#mermaid-svg-jSCisJESn03KIQUS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jSCisJESn03KIQUS .icon-shape .label rect,#mermaid-svg-jSCisJESn03KIQUS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jSCisJESn03KIQUS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jSCisJESn03KIQUS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jSCisJESn03KIQUS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
调用 Point(1, 2)
new(cls, 1, 2) 创建实例
返回的是 cls 实例?
init(self, 1, 2) 初始化
跳过 init,直接返回
返回实例给调用者

5. __del__():对象销毁器

__del__() 在对象被垃圾回收时调用。需要注意:执行 del xxx 只是删除一个引用,并不会主动调用 __del__(),只有当该对象的引用计数降为 0(且被 GC 回收)时才会触发。

python 复制代码
import gc


class Resource:
    def __init__(self, name):
        self.name = name
        print(f"打开资源 {self.name}")

    def __del__(self):
        print(f"释放资源 {self.name}")


r1 = Resource("db")
r2 = r1
del r1              # 只是删掉一个引用,引用计数仍为 1,不会触发 __del__
print("删除 r1 之后")
del r2              # 引用计数归零,触发 __del__
print("删除 r2 之后")
gc.collect()

输出大致为:

text 复制代码
打开资源 db
删除 r1 之后
释放资源 db
删除 r2 之后

使用建议:

  • __del__() 的调用时机不确定,不要依赖它做关键资源释放 ,优先使用 with 语句与上下文管理器。
  • 存在循环引用时,__del__() 可能延迟甚至不被调用。

6. __str__() 与 __repr__():两种可读性

  • __str__():面向人类 ,追求可读性,print() 和 str() 会调用它。
  • __repr__():面向机器/开发者 ,理想情况下输出能作为有效的 Python 代码重新构造该对象,交互式环境回显和 repr() 会调用它。
python 复制代码
class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"{self.name}({self.age} 岁)"

    def __repr__(self):
        return f"User(name={self.name!r}, age={self.age!r})"


u = User("Alice", 30)
print(str(u))    # Alice(30 岁)
print(repr(u))   # User(name='Alice', age=30)
print(u)         # Alice(30 岁),print 优先使用 __str__

补充规则:

  • 只定义 __repr__() 时,str() 会退化为调用 __repr__();反之不成立。
  • 容器(list、dict 等)打印元素时使用的是元素的 __repr__()。
python 复制代码
class OnlyRepr:
    def __repr__(self):
        return "OnlyRepr()"


print(str(OnlyRepr()))        # OnlyRepr()
print([OnlyRepr()])           # [OnlyRepr()]

7. __getattribute__():属性访问拦截器

__getattribute__() 会在每一次 属性访问时被调用(无论属性是否存在),是属性访问的最外层入口。它必须返回属性值,否则会抛 AttributeError。

p.secret → 自动传 "secret" → name = "secret"
p.public → 自动传 "public" → name = "public"

python 复制代码
class Logged:
    def __init__(self):
        self.value = 42

    def __getattribute__(self, name):
        print(f"访问属性:{name}")
        return super().__getattribute__(name)


obj = Logged()
print(obj.value)     # 先打印「访问属性:value」,再打印 42

一个更实用的例子:给属性访问加白名单与日志。

python 复制代码
class Protected:
    def __init__(self):
        self.public = "公开数据"
        self._secret = "机密数据"

    def __getattribute__(self, name):
        if name.startswith("_"):
            raise AttributeError(f"禁止访问私有属性:{name}")
        return super().__getattribute__(name)


p = Protected()
print(p.public)      # 公开数据
# print(p._secret)   # AttributeError: 禁止访问私有属性:_secret

注意事项:

  • 在 __getattribute__() 内部访问 self.xxx 会递归调用自身 ,务必使用 super().__getattribute__(name) 或 object.__getattribute__(self, name)。
  • 若只想在属性不存在 时兜底,应使用 __getattr__(),它只在常规查找失败后调用。
python 复制代码
class Defaults:
    def __getattr__(self, name):
        return f"{name} 不存在,返回默认值"


d = Defaults()
print(d.anything)    # anything 不存在,返回默认值

8. 综合示例:一个带完整生命周期的类

python 复制代码
class Tracked:
    def __new__(cls, *args, **kwargs):
        print("1. __new__")
        return super().__new__(cls)

    def __init__(self, name):
        print("2. __init__")
        self.name = name

    def __getattribute__(self, item):
        if item == "name":
            print("3. __getattribute__ 拦截 name")
        return super().__getattribute__(item)

    def __str__(self):
        return f"Tracked({self.name})"

    def __repr__(self):
        return f"Tracked(name={self.name!r})"

    def __del__(self):
        print("5. __del__")


t = Tracked("demo")   # 1 -> 2
print(str(t))         # 3 -> Tracked(demo)
print(repr(t))        # 3 -> Tracked(name='demo')
del t                 # 5

9. 小结

  • __new__() 负责创建 ,__init__() 负责初始化,前者返回非本类实例时后者不执行。
  • __del__() 只在引用计数归零、对象被回收时触发,del 本身不保证调用它。
  • __str__() 面向人类,__repr__() 面向机器;只写 __repr__() 时 str() 会退化使用它。
  • __getattribute__() 拦截所有 属性访问,内部务必用 super().__getattribute__() 避免递归;仅需兜底缺失属性时用 __getattr__()。
相关推荐
一个简单的TTS开发者2 小时前
#有声小说多角色对话 TTS 配置与选型指南
人工智能·开源·indextts
没落之王2 小时前
历史系统建模:宋初太祖太宗「再立天庭」秩序重构推演
人工智能·算法·重构
海宇服务2 小时前
零信任架构实战:基于海宇全国车辆配置查验构建自动化二手商用车出厂参数建档网关
运维·人工智能·架构·自动化·1024程序员节
xyouyuweizhu2 小时前
字节流与字符流:Java IO 的两条主线该怎么选
java·开发语言·python
迪菲赫尔曼2 小时前
【基础篇 04】第一次使用终端:命令在哪里输入,电脑现在又“站”在哪里?
python·yolo·目标检测
代码方舟2 小时前
零信任架构实战:基于天远人企关联构建自动化供应链核心企业关联穿透与利益冲突审查网关
java·人工智能·架构·自动化
m4Rk_2 小时前
【论文阅读】Agent 记忆机制(113):Agentic Memory Management——从“检索相关信息”走向“管理 Agent 内部记忆”
论文阅读·人工智能·学习·开源·github
奈落242 小时前
Dify 管对话、n8n 管执行:把‘大脑+手脚‘的组合架构对照三方平台资料读清楚
人工智能·架构·agent·ai 办公
虎鲸不是鱼2 小时前
AI 自动化逆向(三十一)函数体不可读 ≠ 函数不可命名
人工智能·自动化·大模型·逆向·ai逆向