Welcome, guest | Sign In | My Account | Store | Cart

Special method __copy__ is the easiest way for an object to cooperate with the copy.copy function, but how do you bypass the object's __init__, if it's slow, to get an 'empty' object of this class? Easy -- here's how.

Python, 16 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# a function makes the idiom available
# non-invasively, everywhere:
def empty_copy(object):
    class Empty: pass
    newcopy = Empty()
    newcopy.__class__ = object.__class__
    return newcopy

# now your class can easily use this function
class YourClass:
    def __init__(self):
        print "assume there's a lot of work here"
    def __copy__(self):
        newcopy = empty_copy(self)
        print "now you can easily copy a relevant"
        print "subset of self's attributes to newcopy"

Python doesn't implicitly copy your objects when you assign them -- a great thing, too, fast and flexible and uniform semantics. When you need copies, you explicitly ask for them, ideally with function copy.copy, which knows how to copy built-in types, has reasonable defaults for your own objects, and also lets you customize the copying process by defining a special method __copy__.

The latter typically needs to start with an empty instance of the same class as self's -- bypassing __init__, when that is a costly operation. How? The simplest way is to use the ability that Python gives you to change an instance's class on the fly -- create a new object in a local empty class, then set its __class__ attribute, as the above code shows. (After that, you typically need to copy a subset of self's attributes -- if you need them all you're better off NOT defining __copy__, as that is copy.copy's default, unless you need to do a little bit more besides the copy; if you do need to get all of self's attributes, newcopy.__dict__.update(self.__dict__) can work, or you can use newcopy.__dict__=self.__dict__.copy()).

An alternative is to "import new" and use newcopy=new.instance(self.__class__, None) (or, new.instance(self.__class__, self.__dict__)). That's fine, too, if you're familiar and comfortable with module new -- however, module new is often thought of as dangerous black magic, and you can easily avoid using it for this purpose if you follow this recipe.

3 comments

Jürgen Hermann 22 years, 7 months ago  # | flag

new.instance does this already. The module "new" already contains this.

Jürgen Hermann 22 years, 7 months ago  # | flag

hmmmm... Sorry, I should have read till the end, and you cannot remove comments... :/

Emmanuel Astier 17 years, 7 months ago  # | flag

new.instance ? Shouldn't it rather be : new.instance(self.__class__, self.__dict__.copy() )

Created by Alex Martelli on Fri, 10 Aug 2001 (PSF)
Python recipes (4591)
Alex Martelli's recipes (27)

Required Modules

  • (none specified)

Other Information and Tasks