Limited deep copy of an instance with a container of containers as an attribute
<p>I have a class</p>

<ul>
<li>whose instances have attributes that are containers
<ul>
<li>which themselves contain containers, each containing many items</li>
</ul></li>
<li>has an expensive initialization of these containers</li>
</ul>

<p>I want to create copies of instances such that</p>

<ol>
<li>the container attributes are copied, rather than shared as references, but</li>
<li>the containers within each container are not deeply copied, but are shared references</li>
<li>a call to the class's expensive <code>__init__()</code> method is avoided if possible</li>
</ol>

<p>For an example, let's use the class <code>SetDict</code>, below, which, when creating an instance, initializes a dictionary-like data structure as an attribute, <code>d</code>. <code>d</code> stores integers as keys and sets as values.</p>

<pre><code>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)
</code></pre>

<p>I would like to copy instances of <code>SetDict</code>, such that <code>d</code> is itself copied, but the sets that are its values are <em>not</em> deep-copied, and are instead only references to the sets.</p>

<p>For example, consider the following behavior currently for this class, where <code>copy.copy</code> doesn't copy the attribute <code>d</code> to the new copy, but <code>copy.deepcopy</code> creates completely new copies of the sets that are values of <code>d</code>.</p>

<pre><code>&gt;&gt;&gt; import copy
&gt;&gt;&gt; s = SetDict(3)
&gt;&gt;&gt; s.d
defaultdict(&lt;type 'set'&gt;, {0: set([1]), 1: set([1]), 2: set([1])})
&gt;&gt;&gt; # Try a basic copy
&gt;&gt;&gt; t = copy.copy(s)
&gt;&gt;&gt; # Add a new key, value pair in t.d
&gt;&gt;&gt; t.d[3] = set([2])
&gt;&gt;&gt; t.d
defaultdict(&lt;type 'set'&gt;, {0: set([1]), 1: set([1]), 2: set([1]), 3: set([2])})
&gt;&gt;&gt; # But oh no! We unintentionally also added the new key to s.d!
&gt;&gt;&gt; s.d
defaultdict(&lt;type 'set'&gt;, {0: set([1]), 1: set([1]), 2: set([1]), 3: set([2])})
&gt;&gt;&gt; 
&gt;&gt;&gt; s = SetDict(3)
&gt;&gt;&gt; # Try a deep copy
&gt;&gt;&gt; u = copy.deepcopy(s)
&gt;&gt;&gt; u.d[0].add(2)
&gt;&gt;&gt; u.d[0]
set([1, 2])
&gt;&gt;&gt; # But oh no! 2 didn't get added to s.d[0]'s set
&gt;&gt;&gt; s.d[0]
set([1])
</code></pre>

<p>The behavior I'd like to see instead would be the following:</p>

<pre><code>&gt;&gt;&gt; s = SetDict(3)
&gt;&gt;&gt; s.d
defaultdict(&lt;type 'set'&gt;, {0: set([1]), 1: set([1]), 2: set([1])})
&gt;&gt;&gt; t = copy.copy(s)
&gt;&gt;&gt; # Add a new key, value pair in t.d
&gt;&gt;&gt; t.d[3] = set([2])
&gt;&gt;&gt; t.d
defaultdict(&lt;type 'set'&gt;, {0: set([1]), 1: set([1]), 2: set([1]), 3: set([2])})
&gt;&gt;&gt; # s.d retains the same key-value pairs
&gt;&gt;&gt; s.d
defaultdict(&lt;type 'set'&gt;, {0: set([1]), 1: set([1]), 2: set([1])})
&gt;&gt;&gt; t.d[0].add(2)
&gt;&gt;&gt; t.d[0]
set([1, 2])
&gt;&gt;&gt; # s.d[0] also had 2 added to its set
&gt;&gt;&gt; s.d[0]
set([1, 2])
</code></pre>

<p>This was my first attempt to create a class that would do this, but it fails due to infinite recursion:</p>

<pre><code>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
</code></pre>

<p>I'm not sure how to properly override the <code>copy.copy</code> (or <code>copy.deepcopy</code>) behavior to achieve this. I'm also not entirely sure if I should be overriding <code>copy.copy</code> or <code>copy.deepcopy</code>. How can I go about getting the desired copy behavior?</p>
