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