Стандартное поведение при откреплении напрямую назначает атрибуты - оно не использует __init__
или __new__
. Поэтому аргументы по умолчанию не применяются.
Когда экземпляр класса не выбран, его метод __init__()
обычно не вызывается. 1
Вызов __init__
может иметь побочные эффекты и может принимать дополнительные, меньшие или другие параметры, чем атрибуты. Это делает его небезопасным по умолчанию. По сути, pickle использует object.__new__(cls)
для создания экземпляра, а затем обновляет его __dict__
.
Вы должны явно указать pickle
, чтобы использовать __init__
, если хотите.
При использовании copyreg
вы должны передать ему constructor
параметр . Обратите внимание, что эта подпись отличается от вашей unpickle_character
.
В противном случае ваша функция травления (pickle_character
) статически определяет функцию, используемую для расслоения. Поскольку для класса Character
не зарегистрирован конструктор, а старый метод pickle не включает его, загрузка старого метода не вызывает ваш конструктор.
def pickle_character(state):
kwargs = state.__dict__
return unpickle_character, (kwargs, )
# ^ unpickler stored for *newly pickled instance*!
# no constuctor stored for *Character class* v
copyreg.pickle(Character, pickle_character)
Легче определить __setstate__
для вашего класса. Это напрямую получает состояние, даже от старших солений.
class Character:
def __init__(self, race, health):
self.race = race
self.health = health
# load state with defaults for missing attributes
def __setstate__(self, state):
self.race = state.get('race', 'Ork')
self. health = state.get('health', 100)
Если вы знаете, что __init__
является безопасным и обратно совместимым, вы можете также использовать его для инициализации из протравленного состояния.
class Character:
# defaults for every initialisation
def __init__(self, race='Ork', health=100):
self.race = race
self.health = health
def __setstate__(self, state):
# re-use __init__ for initialisation
self.__init__(**state)