你为什么还在写 __init__,99% 的大厂程序员早就不用了

不是我吓你,现在再去翻某些大厂的 Python 项目源码,你会发现:
__init__ 基本上都"消失"了,取而代之的是一套更优雅、更易维护的写法。

而你可能还在每个类里写那种千篇一律的 def __init__(self, a, b, c): ...

每次写都像在复制粘贴,代码又臭又长,还不灵活。

一旦要写异步?测试?依赖注入?瞬间原地爆炸。

我之前也一样,直到有一次写个简单的文件读取类,被 init 限制得死死的,

测试 patch 半天,结果代码还是一堆锅。

后来一怒之下,我决定换一种写法------没想到,一下子就通透了。

不用 __init__,反而让代码更清爽、逻辑更聚焦、测试更优雅。

今天这篇文章,我就带你完整拆解这套写法:


传统方式的问题

举个例子,我以前经常这样写一个读取文件的类:

python 复制代码
class FileReader:
    def __init__(self, path):
        self._fd = open_file(path)

    def read(self, length):
        return read_file(self._fd, length)

    def close(self):
        close_file(self._fd)

刚开始看起来一切正常:构造对象时自动打开文件,使用起来也挺方便。

但项目稍微一复杂,问题就来了:

  • 有时候我拿到的是一个现成的文件描述符,不是文件路径;
  • 有时候我只是想构造对象,不希望立刻执行打开文件操作;
  • 有时候是在做测试,我根本不想触碰实际的文件系统。

为了兼容这些场景,我的 __init__ 方法就开始变得越来越复杂,参数越来越多,逻辑越来越难维护。

更关键的是:这些代码本质上只是"把数据装进类",完全没有必要手写。


我现在怎么写?

我的新写法主要依赖两个Python特性:

  • @dataclass
  • @classmethod

通过这两个工具,我几乎不再手写 __init__

来看一个等价但更简洁的实现:

python 复制代码
from dataclasses import dataclass

@dataclass
class FileReader:
    _fd: int

    def read(self, length: int):
        return read_file(self._fd, length)

    def close(self):
        close_file(self._fd)

使用时你可以这样构造对象:

python 复制代码
fd = open_file("data.txt")
reader = FileReader(fd)

如果想进一步简化调用体验,可以加一个工厂方法:

python 复制代码
@dataclass
class FileReader:
    _fd: int

    @classmethod
    def from_path(cls, path: str):
        return cls(open_file(path))

调用时就变成:

python 复制代码
reader = FileReader.from_path("data.txt")

清晰、自然、语义明确。类的职责更单一:它只是"用来持有数据和行为",不再强行耦合初始化逻辑。


测试也更简单

以前写测试,要 mock 掉 open_file,处理不好还容易出错。

现在有了 dataclass,我完全可以直接传入一个假的文件描述符,用于测试:

python 复制代码
reader = FileReader(9999)

无需真正打开文件,测试更轻量,也不需要修改类的任何代码。


支持异步构造

你可能会问:如果初始化过程是异步的,比如调用接口登录、异步打开连接,该怎么办?

这就是 @classmethod 的强大之处。它完全可以是异步的:

python 复制代码
@dataclass
class ApiClient:
    token: str

    @classmethod
    async def login(cls, user, password):
        token = await get_token(user, password)
        return cls(token)

相比之下,Python 并不允许 __init__ 是异步的。这种模式大大增强了灵活性。


类型安全也能做到

还有个问题:如果你担心 _fd 是个普通的 int,容易被误传怎么办?

这时候可以用 NewType 来提高类型的区分度:

python 复制代码
from typing import NewType
FileDescriptor = NewType("FileDescriptor", int)

@dataclass
class FileReader:
    _fd: FileDescriptor

配合类型检查工具(如 mypy),可以提前发现类型使用错误,避免传错参数。


小结

现在我写类,基本遵循以下原则:

  1. @dataclass 自动生成 __init__
  2. @classmethod 创建命名明确的构造方式;
  3. 所有依赖都通过属性注入,确保类始终是"完整、有效"的;
  4. 如有必要,用 NewType 让参数类型更明确。

这种方式不仅让代码更易维护,也让测试、扩展、阅读体验显著提升。

如果你写Python类还在第一时间写 __init__,不妨试试换个思路,可能你会发现一条完全不一样的路径。

这不是偷懒,而是更合理地利用语言特性,让我们专注于真正有价值的逻辑,而不是重复劳动。

如果这篇文章对你有启发,欢迎转发给正在苦写 __init__ 的朋友。

我们下次再聊。

相关推荐
闲人编程11 分钟前
Elasticsearch搜索引擎集成指南
python·elasticsearch·搜索引擎·jenkins·索引·副本·分片
痴儿哈哈20 分钟前
自动化机器学习(AutoML)库TPOT使用指南
jvm·数据库·python
宋小黑29 分钟前
JDK 6到25 全版本网盘合集 (Windows + Mac + Linux)
java·后端
花酒锄作田33 分钟前
SQLAlchemy中使用UPSERT
python·sqlalchemy
SoleMotive.34 分钟前
一个准程序员的健身日志:用算法调试我的增肌计划
python·程序员·健身·职业转型
念何架构之路39 分钟前
Go进阶之panic
开发语言·后端·golang
先跑起来再说41 分钟前
Git 入门到实战:一篇搞懂安装、命令、远程仓库与 IDEA 集成
ide·git·后端·elasticsearch·golang·intellij-idea
亓才孓42 分钟前
[Properties]写配置文件前,必须初始化Properties(引用变量没执行有效对象,调用方法会报空指针错误)
开发语言·python
Bruk.Liu1 小时前
(LangChain 实战14):基于 ChatMessageHistory 自定义实现对话记忆功能
人工智能·python·langchain·agent
大江东去浪淘尽千古风流人物1 小时前
【VLN】VLN(Vision-and-Language Navigation视觉语言导航)算法本质,范式难点及解决方向(1)
人工智能·python·算法