What is __reduce__? (was: copy.deepcopy(): is it bug or intended behavior?)

Benjamin Han bhan at andrew.cmu.edu
Thu Aug 1 07:04:46 CEST 2002


So I traced a little bit to uncover the mystery. in copy.py the deepcopy() is 
defined as:


def deepcopy(x, memo = None):
    """Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """

    if memo is None:
        memo = {}
    d = id(x)
    if memo.has_key(d):
        return memo[d]
    try:
        copierfunction = _deepcopy_dispatch[type(x)]
    except KeyError:
        try:
            copier = x.__deepcopy__
        except AttributeError:
            try:
                reductor = x.__reduce__
            except AttributeError:
                raise error, \
                      "un-deep-copyable object of type %s" % type(x)
            else:
                y = _reconstruct(x, reductor(), 1, memo)
        else:
            y = copier(memo)
    else:
        y = copierfunction(x, memo)
    memo[d] = y
    return y


At the point when Bar instance is deepcopied, type(x) returns <class 
'__main__.Bar'> therefore statement 

copierfunction = _deepcopy_dispatch[type(x)]

generated KeyErro. The chain reaction of exceptions stopped when

reductor = x.__reduce__

But I can't find __reduce__ in Library and Langauge references. Hint anyone? 
Thanks!


PS. this explained why defining my own __deepcopy__ worked. Is it a standard 
practice that whenever you derive a class you have to write __deepcopy__ to 
take care of the super-class copying?


Ben



On Wednesday 31 July 2002 11:59 pm, you wrote:
> Curiously if I added __deepcopy__ to class Bar (replace Bar with the
> following class def):
>
> class Bar (list):
>     def __deepcopy__ (self,memo):
>         obj=Bar()
>         memo[id(self)]=obj
>         for e in self: obj.append(copy.deepcopy(e,memo))
>
>         for k,v in self.__dict__.iteritems():
>             obj.__dict__[k]=copy.deepcopy(v,memo)
>
>         return obj
>
>
> then the result is correct:
>
> [[1]] 135743596
> [[2]] 135787484
> ----------------------------------------------------------------------
> [[1]] 135760188
> [[2]] 135626380
>
>
> Ben
>
> On Wednesday 31 July 2002 07:59 pm, you wrote:
> > This is an abstract of the actual code:
> >
> > --- cut here ---
> > import copy
> >
> > class Foo:
> >     def clone (self):
> >         return copy.deepcopy(self)
> >
> > class Bar (list):
> >     pass
> >
> > class Const:
> >     def __init__ (self, data):
> >         self.data=data
> >     def __repr__ (self):
> >         return str(self.data)
> >
> > n1=Foo()
> > n1.t=[]
> > c=Bar()
> > c.append(Const('1'))
> > n1.t.append(c)
> >
> > n2=Foo()
> > n2.t=[]
> > c=Bar()
> > c.append(Const('2'))
> > n2.t.append(c)
> >
> > g=Foo()
> > g.hd=[]
> > g.hd.append(n1)
> > g.hd.append(n2)
> >
> > print g.hd[0].t,id(g.hd[0].t)
> > print g.hd[1].t,id(g.hd[1].t)
> >
> > print
> > '----------------------------------------------------------------------'
> >
> > h=g.clone()
> >
> > print h.hd[0].t,id(h.hd[0].t)
> > print h.hd[1].t,id(h.hd[1].t)
> >
> > --- cut here ---
> >
> >
> > Running on Python 2.2.1 gave the following output:
> >
> > [[1]] 135743596
> > [[2]] 135759124
> > ----------------------------------------------------------------------
> > [[1]] 135505252
> > [[1]] 135793916
> >
> >
> > But the last line should have "[[2]]...". Am I missing something here?
> >
> > Thanks,
> >
> > Ben




More information about the Python-list mailing list