У меня есть классLimited глубокая копия экземпляра с контейнером контейнеров в качестве атрибута
- , экземпляры которого имеют атрибуты, которые являются контейнерами
- которые сами по себе содержат контейнеры, каждый из которых содержит много элементов
- имеет дорогую инициализацию этих контейнеров
Я хочу создать копии экземпляров таких, что
- атрибуты контейнера копируются, а не общие, как ссылки, но
- контейнеры внутри каждого контейнера не глубоко копируются, но общие референции
- звоните в дорогой
__init__()
метода класса можно избежать, если это возможно
для примера, давайте использовать класс SetDict
, ниже, который, при создании экземпляра, инициализирует словарь-подобную структуру данных в качестве атрибута, d
. d
хранит целые числа как ключи и устанавливает их как значения.
import collections
class SetDict(object):
def __init__(self, size):
self.d = collections.defaultdict(set)
# Do some initialization; if size is large, this is expensive
for i in range(size):
self.d[i].add(1)
Я хотел бы, чтобы скопировать экземпляры SetDict
, так что d
сами копируются, но наборы, которые являются его значением не глубоко скопировано, и вместо этого только ссылок на наборы.
Для примера рассмотрим следующее поведение в настоящее время для этого класса, где copy.copy
не копировать атрибут d
к новой копии, но copy.deepcopy
полностью создает новые копии множеств, являющихся значениями d
.
>>> import copy
>>> s = SetDict(3)
>>> s.d
defaultdict(<type 'set'>, {0: set([1]), 1: set([1]), 2: set([1])})
>>> # Try a basic copy
>>> t = copy.copy(s)
>>> # Add a new key, value pair in t.d
>>> t.d[3] = set([2])
>>> t.d
defaultdict(<type 'set'>, {0: set([1]), 1: set([1]), 2: set([1]), 3: set([2])})
>>> # But oh no! We unintentionally also added the new key to s.d!
>>> s.d
defaultdict(<type 'set'>, {0: set([1]), 1: set([1]), 2: set([1]), 3: set([2])})
>>>
>>> s = SetDict(3)
>>> # Try a deep copy
>>> u = copy.deepcopy(s)
>>> u.d[0].add(2)
>>> u.d[0]
set([1, 2])
>>> # But oh no! 2 didn't get added to s.d[0]'s set
>>> s.d[0]
set([1])
Поведение Я хотел бы видеть вместо этого будет следующее:
>>> s = SetDict(3)
>>> s.d
defaultdict(<type 'set'>, {0: set([1]), 1: set([1]), 2: set([1])})
>>> t = copy.copy(s)
>>> # Add a new key, value pair in t.d
>>> t.d[3] = set([2])
>>> t.d
defaultdict(<type 'set'>, {0: set([1]), 1: set([1]), 2: set([1]), 3: set([2])})
>>> # s.d retains the same key-value pairs
>>> s.d
defaultdict(<type 'set'>, {0: set([1]), 1: set([1]), 2: set([1])})
>>> t.d[0].add(2)
>>> t.d[0]
set([1, 2])
>>> # s.d[0] also had 2 added to its set
>>> s.d[0]
set([1, 2])
Это была моя первая попытка создать класс, который будет делать это, но он не из-за бесконечной рекурсии:
class CopiableSetDict(SetDict):
def __copy__(self):
import copy
# This version gives infinite recursion, but conveys what we
# intend to do.
#
# First, create a shallow copy of this instance
other = copy.copy(self)
# Then create a separate shallow copy of the d
# attribute
other.d = copy.copy(self.d)
return other
Я не уверен, как правильно переопределить поведение copy.copy
(или copy.deepcopy
) для достижения этой цели. Я также не совсем уверен, стоит ли мне переопределять copy.copy
или copy.deepcopy
. Как я могу получить желаемое поведение копии?