Re: duplicate of classes (deep copy)

Guido.van.Rossum@cwi.nl
Fri, 24 Sep 1993 11:48:23 +0200

Michel Lesoinne writes:

> I am trying to write a program that uses fcntl.ioctl to change the condition
> of a modem line and then at the end reset it to its original value.
> Is there a simple function in python of all classes that does a deep
> copy of an instance, similarily to dup in smalltalk.
> For example I'd like the following to work correctly:
>
> tio = Termio()
> tio.getfrom(0) # performs a TCGETA on file 0
> oldtio = tio.dup() #Here it is. of course I can write the dup funtion but
> # it would be nice if all classes had it so that
> #it would recursively apply to members
> tio.lflag = 0 ;
> tio.iflag = IGNBRK ;
> tio.oflag = 0 ;
>
> tio.setto(0) # Change the line
>
> ... # do whatever work
>
> oldtio.setto(0) # reset line condition to original value

No, there isn't such a feature. In the light of Python's varying
object semantics (different for each type), deep copies are rarely as
useful as one would initially think they would be: they tend to copy
either too much or not enough, and sometimes both at the same time.
You are better off defining an operation that does exactly what you
need in a particular case.

Also note that a naive deep copy can get in trouble if there are
reference loops. I don't know how smalltalk solves this. Probably by
keeping track of objects already copied during the deep copy? But I
presume it will also be possible to override the dup method for a
particular class, which may spoil the administration used...

Anyway, if you want a deep copy, here's something that should work
reasonably well.

-------------------------------------------------------------------------------
# Make this class a base class of each class you define
class UniversalBase:
def dup(self): return dupinstance(self)

# Function to duplicate an instance
def dupinstance(x):
new = x.__class__()
for key in x.__dict__.keys():
setattr(new, key, dup(getattr(x, key)))
return new

ClassInstanceType = type(UniversalBase())

# Function to duplicate most object instances
def dup(x):
if hasattr(x, dup):
return x.dup()
if type(x) is type(()):
new = ()
for item in x: new = new + (dup(item),)
return new
if type(x) is type([]):
new = []
for item in x: new.append(dup(item))
return new
if type(x) is ClassInstanceType:
return dupinstance(x)
return x # Assume all other interesting types are immutable
# Not completely true but reasonable in most cases
-------------------------------------------------------------------------------

One problem with this: if a class has an __init__() method, it will be
called by the "x.__class__()" in dupinstance(). This may or may not
be what is desired; moreover it will fail with probablility one if the
__init__() takes one or more arguments. The effect is that classes
with such an __init__() method are forced to define their own dup()
method (which still will call the __init__() method, but can call it
with the proper arguments. Possibly it can be given a special
argument signalling to initialize the object from a similar one, like
copy constructors in C++.

Note that this problem has similarities with the problems one
encouters when trying to implement generic persistent objects in
Python. There, too, one of the problems is to define what the
semantics should be in the light of objects that may contain
references to open files, windows, network connections, etc., and to
what extent sharing of sub-objects is incidental or essential. I have
a feeling that this is because Python mixes mutable and immutable
objects and also allows "opaque" objects with arbitrary semantics
(e.g. defined by extension modules).

Maybe Jaap has some thoughts or opinions on this?

--Guido van Rossum, CWI, Amsterdam <Guido.van.Rossum@cwi.nl>

PS: Some very interesting issues were raised on the list while I am
away. I have read and saved everything and intend to come back to
these, if and when I have a little more time... Bear with me...