Recipe6.9.Making a Fast Copy of an Object


Recipe 6.9. Making a Fast Copy of an Object

Credit: Alex Martelli

Problem

You need to implement the special method _ _copy_ _ so that your class can cooperate with the copy.copy function. Because the _ _init_ _ method of your specific class happens to be slow, you need to bypass it and get an "empty", uninitialized instance of the class.

Solution

Here's a solution that works for both new-style and classic classes:

def empty_copy(obj):     class Empty(obj._ _class_ _):         def _ _init_ _(self): pass     newcopy = Empty( )     newcopy._ _class_ _ = obj._ _class_ _     return newcopy

Your classes can use this function to implement _ _copy_ _ as follows:

class YourClass(object):     def _ _init_ _(self):         assume there's a lot of work here     def _ _copy_ _(self):         newcopy = empty_copy(self)         copy some relevant subset of self's attributes to newcopy         return newcopy

Here's a usage example:

if _ _name_ _ == '_ _main_ _':     import copy     y = YourClass( )    # This, of course, does run _ _init_ _     print y     z = copy.copy(y)   # ...but this doesn't     print z

Discussion

As covered in Recipe 4.1, Python doesn't implicitly copy your objects when you assign them, which is a great thing because it gives fast, flexible, and uniform semantics. When you need a copy, you explicitly ask for it, often with the copy.copy function, which knows how to copy built-in types, has reasonable defaults for your own objects, and lets you customize the copying process by defining a special method _ _copy_ _ in your own classes. If you want instances of a class to be noncopyable, you can define _ _copy_ _ and raise a TypeError there. In most cases, you can just let copy.copy's default mechanisms work, and you get free clonability for most of your classes. This is quite a bit nicer than languages that force you to implement a specific clone method for every class whose instances you want to be clonable.

A _ _copy_ _ method often needs to start with an "empty" instance of the class in question (e.g., self), bypassing _ _init_ _ when that is a costly operation. The simplest general way to do this 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 the new object's _ _class_ _ attribute, as the recipe's code shows. Inheriting class Empty from obj._ _class_ _ is redundant (but quite innocuous) for old-style (classic) classes, but that inheritance makes the recipe compatible with all kinds of objects of classic or new-style classes (including built-in and extension types). Once you choose to inherit from obj's class, you must override _ _init_ _ in class Empty, or else the whole purpose of the recipe is defeated. The override means that the _ _init_ _ method of obj's class won't execute, since Python, fortunately, does not automatically execute ancestor classes' initializers.

Once you have an "empty" object of the required class, you typically need to copy a subset of self's attributes. When you need all of the attributes, you're better off not defining _ _copy_ _ explicitly, since copying all instance attributes is exactly copy.copy's default behavior. Unless, of course, you need to do a little bit more than just copying instance attributes; in this case, these two alternative techniques to copy all attributes are both quite acceptable:

newcopy._ _dict_ _.update(self._ _dict_ _) newcopy._ _dict_ _ = dict(self._ _dict_ _)

An instance of a new-style class doesn't necessarily keep all of its state in _ _dict_ _, so you may need to do some class-specific state copying in such cases.

Alternatives based on the new standard module can't be made transparent across classic and new-style classes, and neither can the _ _new_ _ static method that generates an empty instancethe latter is only defined in new-style classes, not classic ones. Fortunately, this recipe obviates any such issues.

A good alternative to implementing _ _copy_ _ is often to implement the methods _ _getstate_ _ and _ _setstate_ _ instead: these special methods define your object's state very explicitly and intrinsically bypass _ _init_ _. Moreover, they also support serialization (i.e., pickling) of your class instances: see Recipe 7.4 for more information about these methods.

So far we have been discussing shallow copies, which is what you want most of the time. With a shallow copy, your object is copied, but objects it refers to (attributes or items) are not, so the newly copied object and the original object refer to the same items or attributes objectsa fast and lightweight operation. A deep copy is a heavyweight operation, potentially duplicating a large graph of objects that refer to each other. You get a deep copy by calling copy.deepcopy on an object. If you need to customize the way in which instances of your class are deep-copied, you can define the special method _ _deepcopy_ _:

class YourClass(object):     ...     def _ _deepcopy_ _(self, memo):         newcopy = empty_copy(self)         # use copy.deepcopy(self.x, memo) to get deep copies of elements         # in the relevant subset of self's attributes, to set in newcopy         return newcopy

If you choose to implement _ _deepcopy_ _, remember to respect the memoization protocol that is specified in the Python documentation for standard module copyget deep copies of all the attributes or items that are needed by calling copy.deepcopy with a second argument, the same memo dictionary that is passed to the _ _deepcopy_ _ method. Again, implementing _ _getstate_ _ and _ _setstate_ _ is often a good alternative, since these methods can also support deep copying: Python takes care of deeply copying the "state" object that _ _getstate_ _ returns, before passing it to the _ _setstate_ _ method of a new, empty instance. See Recipe 7.4 for more information about these special methods.

See Also

Recipe 4.1 about shallow and deep copies; Recipe 7.4 about _ _getstate_ _ and _ _setstate_ _; the Library Reference and Python in a Nutshell sections on the copy module.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net