Ответ 1
Типы определяют, как экземпляры его получают маринованным путем определения одного или нескольких (довольно больших) наборов методов. У каждого есть свое тонкое поведение. См. документы в протоколе рассола. В случае collections.defaultdict
он использует метод __reduce__
:
>>> l = collections.defaultdict(list)
>>> l.__reduce__()
(<type 'collections.defaultdict'>, (<type 'list'>,), None, None, <dictionary-itemiterator object at 0x7f031fb3c470>)
Первым элементом кортежа является тип, а второй элемент - кортеж аргументов, передаваемый типу при его создании. Если вы не переопределите __reduce__
, первый элемент будет правильно изменен на ваш тип, но второй элемент не будет. Это вызывает ошибку, которую вы видите. Яркий пример того, как вы могли это исправить:
>>> import collections
>>> import pickle
>>> class C(collections.defaultdict):
... def __init__(self):
... collections.defaultdict.__init__(self, list)
... def __reduce__(self):
... t = collections.defaultdict.__reduce__(self)
... return (t[0], ()) + t[2:]
...
>>> c = C()
>>> c[1].append(2)
>>> c[2].append(3)
>>> c2 = pickle.loads(pickle.dumps(c))
>>> c2 == c
True
Это только грубый пример, потому что там больше травления (например, __reduce_ex__
), и все это довольно сложно. В этом случае использование __getinitargs__
может быть более удобным.
В качестве альтернативы вы можете сделать способ класса __init__
взять необязательный вызываемый, по умолчанию - list
, или вы можете просто использовать функцию вместо класса:
def listdict():
return collections.defaultdict(list)