You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用pickle加载Python类时出现__init__参数缺失错误

问题解决与替代方案

为什么pickle加载会报错?

pickle反序列化时默认会调用类的__init__方法,但你的Building类__init__要求必填的name和jobs参数,而pickle不会自动传递这些参数,因此抛出TypeError。给参数加默认值虽然能加载,但会跳过原有属性的恢复,导致数据丢失。

修复pickle序列化的方法

方法1:实现__getstate__和__setstate__方法

这两个方法可以自定义序列化/反序列化的属性处理逻辑,绕过__init__的调用,直接恢复实例原有属性:

import pickle

class Building:
    def __init__(self, name, jobs):
        self.name = name
        self.jobs = jobs

    def __getstate__(self):
        # 返回需要保存的实例属性字典
        return self.__dict__

    def __setstate__(self, state):
        # 直接将保存的属性字典赋值给实例,不调用__init__
        self.__dict__.update(state)

class Kingdom:
    def __init__(self):
        self.buildings = []

# 测试代码
kingdom = Kingdom()
kingdom.buildings.append(Building("Castle", ["knight", "archer"]))

# 保存
with open("kingdom.pkl", "wb") as f:
    pickle.dump(kingdom, f)

# 加载
with open("kingdom.pkl", "rb") as f:
    loaded_kingdom = pickle.load(f)
print(loaded_kingdom.buildings[0].name)  # 输出 Castle

方法2:使用__reduce__方法(更底层)

返回一个元组指定反序列化的构造方式,适合需要更精细控制的场景:

import pickle

class Building:
    def __init__(self, name, jobs):
        self.name = name
        self.jobs = jobs

    def __reduce__(self):
        # 返回(类构造函数,构造参数,实例属性字典)
        return (self.__class__, (self.name, self.jobs), self.__dict__)

# Kingdom类和测试代码同上

其他保存包含子类实例对象的方法

1. JSON序列化

JSON是可读的文本格式,适合跨语言场景,但需要自定义编码/解码逻辑:

import json

class Building:
    def __init__(self, name, jobs):
        self.name = name
        self.jobs = jobs

class Kingdom:
    def __init__(self):
        self.buildings = []

# 自定义JSON编码器
class GameEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (Building, Kingdom)):
            return {"__type__": type(obj).__name__, **obj.__dict__}
        return super().default(obj)

# 自定义JSON解码器
def game_decoder(obj):
    if obj.get("__type__") == "Building":
        b = Building(obj["name"], obj["jobs"])
        return b
    elif obj.get("__type__") == "Kingdom":
        k = Kingdom()
        k.buildings = [game_decoder(b) for b in obj["buildings"]]
        return k
    return obj

# 保存
kingdom = Kingdom()
kingdom.buildings.append(Building("Farm", ["farmer", "shepherd"]))
with open("kingdom.json", "w", encoding="utf-8") as f:
    json.dump(kingdom, f, cls=GameEncoder, indent=2)

# 加载
with open("kingdom.json", "r", encoding="utf-8") as f:
    loaded_kingdom = json.load(f, object_hook=game_decoder)

2. 数据类+JSON/pickle

如果你的类以存储数据为主,用dataclasses可以简化序列化逻辑:

from dataclasses import dataclass, asdict
import json

@dataclass
class Building:
    name: str
    jobs: list[str]

@dataclass
class Kingdom:
    buildings: list[Building]

# 保存
kingdom = Kingdom([Building("Mill", ["miller"])])
with open("kingdom.json", "w", encoding="utf-8") as f:
    json.dump(asdict(kingdom), f, indent=2)

# 加载
with open("kingdom.json", "r", encoding="utf-8") as f:
    data = json.load(f)
    loaded_kingdom = Kingdom([Building(**b) for b in data["buildings"]])

3. SQLite数据库存储

适合需要持久化大量数据或需要查询功能的场景,将对象属性映射到数据库表:

import sqlite3

class Building:
    def __init__(self, name, jobs):
        self.name = name
        self.jobs = jobs

class Kingdom:
    def __init__(self, name="MyKingdom"):
        self.name = name
        self.buildings = []

# 初始化数据库连接
conn = sqlite3.connect("game_save.db")
cursor = conn.cursor()

# 创建表
cursor.execute("""
CREATE TABLE IF NOT EXISTS kingdoms (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS buildings (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    jobs TEXT NOT NULL  # 用逗号分隔列表
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS kingdom_buildings (
    kingdom_id INTEGER,
    building_id INTEGER,
    FOREIGN KEY(kingdom_id) REFERENCES kingdoms(id),
    FOREIGN KEY(building_id) REFERENCES buildings(id),
    PRIMARY KEY(kingdom_id, building_id)
)
""")
conn.commit()

# 保存王国
def save_kingdom(kingdom):
    cursor.execute("INSERT INTO kingdoms (name) VALUES (?)", (kingdom.name,))
    kingdom_id = cursor.lastrowid
    for b in kingdom.buildings:
        cursor.execute("INSERT INTO buildings (name, jobs) VALUES (?, ?)", 
                      (b.name, ",".join(b.jobs)))
        building_id = cursor.lastrowid
        cursor.execute("INSERT INTO kingdom_buildings VALUES (?, ?)", 
                      (kingdom_id, building_id))
    conn.commit()

# 加载王国
def load_kingdom(kingdom_id=1):
    cursor.execute("SELECT name FROM kingdoms WHERE id=?", (kingdom_id,))
    kingdom_name = cursor.fetchone()[0]
    kingdom = Kingdom(kingdom_name)
    cursor.execute("""
    SELECT b.name, b.jobs FROM buildings b
    JOIN kingdom_buildings kb ON b.id=kb.building_id
    WHERE kb.kingdom_id=?
    """, (kingdom_id,))
    for name, jobs_str in cursor.fetchall():
        jobs = jobs_str.split(",")
        kingdom.buildings.append(Building(name, jobs))
    return kingdom

# 测试
kingdom = Kingdom("NorthLand")
kingdom.buildings.append(Building("Blacksmith", ["blacksmith", "apprentice"]))
save_kingdom(kingdom)
loaded_kingdom = load_kingdom()
print(loaded_kingdom.name, loaded_kingdom.buildings[0].name)

内容的提问来源于stack exchange,提问作者igr2020

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 18:40:15