当前位置:首页>python>init 方法在 Python 中有什么作用?

init 方法在 Python 中有什么作用?

  • 2026-10-10 17:16:27
init 方法在 Python 中有什么作用?

TypeError: __init__() should return None, not 'dict'

这个报错我见过不止一次。一般不是 Python 难,是有人把 __init__ 当成“创建对象的地方”了,还想顺手把结果返回出去。

在 Python 里,真正把对象造出来的是 __new__,__init__ 干的是后半段活:对象已经有了,给它塞字段、做校验、补默认值。

所以 __init__ 不能返回业务对象,不能返回 dict,不能返回 True。它只能老老实实返回 None。

看一段更像业务里的代码。

classPayRecord:
def__init__(self, line_no, raw):
        self.line_no = line_no
        self.order_id = raw.get("order_id", "").strip()
        self.amount = self._parse_amount(raw.get("amount"))
        self.channel = raw.get("channel", "unknown").lower()

ifnot self.order_id:
raise ValueError(f"第{line_no}行缺少 order_id")

def_parse_amount(self, value):
try:
            amount = int(value)
except (TypeError, ValueError):
raise ValueError(f"第{self.line_no}行 amount 非法: {value}")

if amount <= 0:
raise ValueError(f"第{self.line_no}行 amount 必须大于0")

return amount

这段代码里,PayRecord(...) 一调用,Python 会先弄出一个空对象,然后把这个对象交给 __init__。

__init__ 拿到 self 后,开始填:

record = PayRecord(12, {
"order_id": "  P20260418001 ",
"amount": "399",
"channel": "WX"
})

print(record.order_id)
print(record.amount)
print(record.channel)

输出大概是:

P20260418001
399
wx

我一般不喜欢把这种字段清洗散落在业务代码里。

比如导入支付流水时,有些人会这样写:

order_id = row["order_id"].strip()
amount = int(row["amount"])
channel = row.get("channel", "unknown").lower()

# 后面还有一堆 if

一开始看着没事,等 CSV 字段多了,Excel 导入也来了,补单接口也复用这套逻辑,字段清洗就会散得到处都是。最后线上报一个金额转换异常,你连是哪一行数据脏了都得翻半天。

放到 __init__ 里,至少对象一进系统就是干净的。脏数据在门口就拦住。

不过这里有个坑,挺隐蔽。

classImportTask:
def__init__(self, task_id, errors=[]):
        self.task_id = task_id
        self.errors = errors

这代码第一眼看不出问题,跑久了就恶心。

t1 = ImportTask("A")
t2 = ImportTask("B")

t1.errors.append("A文件第3行金额为空")

print(t2.errors)

你可能以为 t2.errors 是空的,实际它也会带上这条错误。

因为 errors=[] 这个列表在函数定义时就创建了,不是每次调用 __init__ 都重新创建。

我现场一般直接改成这样:

classImportTask:
def__init__(self, task_id, errors=None):
        self.task_id = task_id
        self.errors = [] if errors isNoneelse list(errors)

defadd_error(self, msg):
        self.errors.append(msg)

别嫌这几行啰嗦,这种默认参数坑,排起来比写起来贵多了。

还有一个问题:__init__ 里要不要查数据库、调接口、读文件?

我的习惯是,少做。

__init__ 适合做轻量初始化,不适合塞重 IO。对象初始化本来应该很快,如果里面藏了一个接口调用,排查超时时你会很别扭。

比如这种我不太信:

classUserProfile:
def__init__(self, user_id, api_client):
        self.user_id = user_id
        self.detail = api_client.query_user(user_id)

调用方只是想创建对象,结果偷偷发了请求。哪天接口慢了,堆栈里看见卡在构造对象上,很不舒服。

我更愿意拆成这样:

classUserProfile:
def__init__(self, user_id, nickname, level):
        self.user_id = user_id
        self.nickname = nickname
        self.level = level

    @classmethod
defload(cls, user_id, api_client):
        detail = api_client.query_user(user_id)
return cls(
            user_id=user_id,
            nickname=detail.get("nickname", ""),
            level=detail.get("level", 0)
        )

这样一眼就知道:UserProfile(...) 是普通初始化,UserProfile.load(...) 才会访问外部服务。

__init__ 还有一个很重要的作用:把对象的不变量立住。

比如订单不能没有订单号,金额不能小于等于 0,状态不能乱传。这些判断不要全靠调用方自觉。

classRefundApply:
    ALLOWED_STATUS = {"created", "checking", "rejected"}

def__init__(self, refund_no, order_no, amount, status="created"):
        self.refund_no = refund_no.strip()
        self.order_no = order_no.strip()
        self.amount = int(amount)
        self.status = status

ifnot self.refund_no ornot self.order_no:
raise ValueError("退款单号和订单号不能为空")

if self.amount <= 0:
raise ValueError(f"退款金额异常: {self.amount}")

if self.status notin self.ALLOWED_STATUS:
raise ValueError(f"退款状态非法: {self.status}")

这个对象只要创建成功,后面的代码就不用每一步都怀疑它是不是半残数据。

所以 __init__ 的作用别想复杂。

它不是返回结果的函数,也不是越重越好的万能入口。它就是对象创建后的初始化现场:字段放进去,默认值补好,脏数据挡住,别把 IO 和一堆业务流程塞进去。

写 Python 类,__init__ 写得干净,后面的代码一般也不会太难看。

最新文章

随机文章