Copy with __slots__

Griebel, Peer Peer.Griebel at entire.de
Wed Sep 18 10:12:27 EDT 2002


> Griebel, Peer wrote [I'm only leaving the relevant parts of 
> the script]:
> 
> > class C1(object):
> >     __slots__ = "s1";
> > 
> > class C2(C1):
> >     __slots__ = "s2";
> 
> Funny use of semicolons here.  I'd suggest omitting them!

Yes sure... This is the result of constantly switching between Java and
Python. Nomally I don't use semicolons...
 
> > o2 = C2()
> > 
> > import copy
> > p2 = copy.copy(o2)
> > print p2.s2           # attribute s2 does not exist (has 
> not been copied)
> > 
> > The last print statement fails with the an AttributeError. Since
> 
> Traceback (most recent call last):
>   File "sloco.py", line 25, in ?
>     p2 = copy.copy(o2)
>   File "/usr/local/lib/python2.2/copy.py", line 84, in copy
>     y = _reconstruct(x, reductor(), 0)
>   File "/usr/local/lib/python2.2/copy_reg.py", line 68, in _reduce
>     dict = getstate()
> TypeError: a class that defines __slots__ without defining 
> __getstate__ 
> cannot be pickled

I'm using Python 2.2 (Cygwin). There I don't get the exception you
mentioned.
 
> This is with Python 2.2.1 -- as you see, it doesn't get anywhere
> as far as the print statement.  The error message is somewhat
> peculiar (who's trying to _pickle_ anything...?) -- and if you
> do try to define __getstate__ (and possibly __setstate__), you'll
> see they're no use for copy.copy (nor copy.deepcopy).  _reconstruct
> gets in the way.
> 
> If you only care about copy.copy, the simplest alternative is:
> 
> class C2(C1):
>     __slots__ = "s2"
>     def __copy__(self):
>         x = self.__class__()
>         x.s1 = self.s1
>         x.s2 = self.s2
>         return x

Yes I know this works. But this hat two drawbacks (at least in my eyes):

1. Formerly I used the approach of copy: I simply updated the dict of a
newly created empty object. This is fast and works well - but not with the
new classes. I think your approach runs much more slowly.

2. The approach is error prone. When simply updating a dict I'm not going to
miss a newly added attribute. But using your method I have to remember the
method whenever I add an attribute.

> The way to cover copy.copy, copy.deepcopy AND pickle is to have the
> class supply a special method __reduce__.  __reduce__ can return a
> pair where the first item is callable, the second item is the tuple
> to pass as arguments to the first.  Thus, for example:

Here once again I have the two disadvantages mentioned above.

> The nicest thing about it is that a class with slots will often
> already supply some nice function to build instances of the class
> given values to put in the slots -- you don't necessarily have
> to write factory functions such as _build just for the purpose
> of supporting copy, deepcopy and pickle... you often have them
> lying around!-).  For example, a more typical case might be:
> 
> class C2(C1):
>     __slots__ = 's2'
>     def __init__(self, s1=None, s2=None):
>         C1.__init__(self, s1)
>         self.s2 = s2
>     def __reduce__(self):
>         return self.__class__, (self.s1, self.s2)
> 
> Here I'm assuming C1 has a similar __init__, but that's a detail.
> The point is that __reduce__ can use class C2 itself as the
> first item of its result -- the class IS callable, after all.

Yes this would work. But I'm looking for speed (as you already abserved).
And my constructor is a little more heavy weight. So it is much faster to
use the old mechanism (update dict) than always calling the constructor.
 
> Alex

Thanks Alex!
I'm still in hope to find a fast and secure solution...

Peer




More information about the Python-list mailing list