Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید







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.


/ 394