diff --git a/src/sage/combinat/species/species.py b/src/sage/combinat/species/species.py index 3e44ded7677..46b46dbad16 100644 --- a/src/sage/combinat/species/species.py +++ b/src/sage/combinat/species/species.py @@ -182,7 +182,7 @@ def __ne__(self, other): """ return not (self == other) - def __getstate__(self): + def _getstate_(self): r""" This is used during the pickling process and returns a dictionary of the data needed to create this object during the unpickling @@ -194,7 +194,7 @@ def __getstate__(self): EXAMPLES:: sage: C = species.CharacteristicSpecies(5) - sage: args, kwds = C.__getstate__() + sage: args, kwds = C._getstate_() sage: args {0: 5} sage: sorted(kwds.items()) @@ -206,10 +206,10 @@ def __getstate__(self): except AttributeError: return ({}, kwds) - def __setstate__(self, state): + def _setstate_(self, state): """ This is used during unpickling to recreate this object from the - data provided by the ``__getstate__`` method. + data provided by the ``_getstate_`` method. TESTS:: @@ -217,7 +217,7 @@ def __setstate__(self, state): sage: C4 = species.CharacteristicSpecies(4) sage: C2 Characteristic species of order 2 - sage: C2.__setstate__(C4.__getstate__()); C2 + sage: C2._setstate_(C4._getstate_()); C2 Characteristic species of order 4 """ args_dict, kwds = state @@ -235,7 +235,7 @@ def weighted(self, weight): sage: C.weighted(t) Cyclic permutation species with weight=t """ - args_dict, kwds = self.__getstate__() + args_dict, kwds = self._getstate_() kwds.update({'weight': weight}) return self.__class__(*[args_dict[i] for i in range(len(args_dict))], **kwds) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index d67b8b0a59b..64475a9d5b9 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -2,18 +2,6 @@ r""" Cached Functions and Methods -AUTHORS: - -- William Stein: initial version, (inspired by conversation with Justin Walker) -- Mike Hansen: added doctests and made it work with class methods. -- Willem Jan Palenstijn: add CachedMethodCaller for binding cached methods to - instances. -- Tom Boothby: added DiskCachedFunction. -- Simon King: improved performance, more doctests, cython version, - CachedMethodCallerNoArgs, weak cached function, cached special methods. -- Julian Rueth (2014-03-19, 2014-05-09, 2014-05-12): added ``key`` parameter, allow caching - for unhashable elements, added ``do_pickle`` parameter - EXAMPLES: By :issue:`11115`, cached functions and methods are now also @@ -430,8 +418,19 @@ Note that shallow copy of mutable objects may behave unexpectedly:: 2 sage: b.f is a.f False -""" +AUTHORS: + +- William Stein: initial version, (inspired by conversation with Justin Walker) +- Mike Hansen: added doctests and made it work with class methods. +- Willem Jan Palenstijn: add CachedMethodCaller for binding cached methods to + instances. +- Tom Boothby: added DiskCachedFunction. +- Simon King: improved performance, more doctests, cython version, + CachedMethodCallerNoArgs, weak cached function, cached special methods. +- Julian Rueth (2014-03-19, 2014-05-09, 2014-05-12): added ``key`` parameter, allow caching + for unhashable elements, added ``do_pickle`` parameter +""" # **************************************************************************** # Copyright (C) 2008 William Stein # Mike Hansen @@ -864,6 +863,27 @@ cdef class CachedFunction(): """ return _cached_function_unpickle, (self.__cached_module__, self.__name__, self.cache) + def is_pickled_with_cache(self): + """ + Return ``True`` if this cached function is pickled with the cache. + + EXAMPLES:: + + sage: @cached_function + ....: def f(x): + ....: return x + ....: + sage: f.is_pickled_with_cache() + False + sage: @cached_function(do_pickle=True) + ....: def f(x): + ....: return x + ....: + sage: f.is_pickled_with_cache() + True + """ + return self.do_pickle + ######### # Introspection # diff --git a/src/sage/structure/factory.pyx b/src/sage/structure/factory.pyx index 31b8e1ac707..8c34017b2f4 100644 --- a/src/sage/structure/factory.pyx +++ b/src/sage/structure/factory.pyx @@ -2,10 +2,6 @@ r""" Factory for cached representations -.. SEEALSO:: - - :mod:`sage.structure.unique_representation` - Using a :class:`UniqueFactory` is one way of implementing a *cached representation behaviour*. In spite of its name, using a :class:`UniqueFactory` is not enough to ensure the *unique representation @@ -36,13 +32,16 @@ sophisticated, then generally :class:`~sage.structure.unique_representation.CachedRepresentation` is much easier to use than a factory. +.. SEEALSO:: + + :mod:`sage.structure.unique_representation` + AUTHORS: -- Robert Bradshaw (2008): initial version. -- Simon King (2013): extended documentation. +- Robert Bradshaw (2008): initial version +- Simon King (2013): extended documentation - Julian Rueth (2014-05-09): use ``_cache_key`` if parameters are unhashable """ - #***************************************************************************** # Copyright (C) 2008 Robert Bradshaw # 2014 Julian Rueth @@ -423,13 +422,11 @@ cdef class UniqueFactory(SageObject): self._cache[version, _cache_key(key)] = obj obj._factory_data = self, version, key, extra_args - # Install a custom __reduce__ method on the instance "obj" - # that we just created. We only do this if the class of - # "obj" has a generic __reduce__ method, which is either - # object.__reduce__ or __reduce_cython__, the - # auto-generated pickling function for Cython. + # Install a custom __reduce_ex__ method (that overrides __reduce__ + # method) on the instance obj that we just created. We only do this + # if the class of obj has a generic object.__reduce__ method. f = obj.__class__.__reduce__ - if f.__objclass__ is object or f.__name__ == "__reduce_cython__": + if f.__objclass__ is object: obj.__reduce_ex__ = types.MethodType(generic_factory_reduce, obj) except AttributeError: pass @@ -734,6 +731,10 @@ def generic_factory_reduce(self, proto): """ Used to provide a ``__reduce__`` method if one does not already exist. + INPUT: + + - ``proto`` -- positive integer; protocol number + EXAMPLES:: sage: V = QQ^6 # needs sage.modules diff --git a/src/sage/structure/parent_gens.pyx b/src/sage/structure/parent_gens.pyx index 3a69fa07be6..5e3430a5150 100644 --- a/src/sage/structure/parent_gens.pyx +++ b/src/sage/structure/parent_gens.pyx @@ -168,7 +168,7 @@ cdef class ParentWithGens(ParentWithBase): return parent.Parent.__getstate__(self) d = [] try: - d = list(self.__dict__.copy().iteritems()) # so we can add elements + d = self.__dict__.copy().items() # so we can add elements except AttributeError: pass d = dict(d) diff --git a/src/sage/structure/unique_representation.py b/src/sage/structure/unique_representation.py index d5208beb1ea..fca974de8eb 100644 --- a/src/sage/structure/unique_representation.py +++ b/src/sage/structure/unique_representation.py @@ -2,18 +2,7 @@ r""" Unique Representation -Abstract classes for cached and unique representation behavior. - -.. SEEALSO:: - - :class:`sage.structure.factory.UniqueFactory` - -AUTHORS: - -- Nicolas M. Thiery (2008): Original version. -- Simon A. King (2013-02): Separate cached and unique representation. -- Simon A. King (2013-08): Extended documentation. - +This modules defines abstract classes for cached and unique representation behavior. What is a cached representation? ================================ @@ -537,6 +526,16 @@ class that inherits from :class:`UniqueRepresentation`: By adding unique representation behaviour, one has to implement hash and equality test accordingly, for example by inheriting from :class:`~sage.misc.fast_methods.WithEqualityById`. + +.. SEEALSO:: + + :class:`sage.structure.factory.UniqueFactory` + +AUTHORS: + +- Nicolas M. Thiery (2008): initial version +- Simon A. King (2013-02): separated cached and unique representation +- Simon A. King (2013-08): extended documentation """ # **************************************************************************** # Copyright (C) 2008 Nicolas M. Thiery @@ -598,6 +597,8 @@ def __classcall__(cls, *args, **options): assert isinstance(instance, cls) if instance.__class__.__reduce__ == WithPicklingByInitArgs.__reduce__: instance._reduction = (cls, args, options) + instance.__class__.__getstate__ = WithPicklingByInitArgs.__getstate__ + instance.__class__.__setstate__ = WithPicklingByInitArgs.__setstate__ return instance def __reduce__(self): @@ -613,9 +614,9 @@ def __reduce__(self): sage: x = UniqueRepresentation() sage: x.__reduce__() # indirect doctest - (, (, (), {})) + (, (, (), {}), {}) """ - return (unreduce, self._reduction) + return (unreduce, self._reduction, self.__getstate__()) def __copy__(self): """ @@ -646,6 +647,78 @@ def __deepcopy__(self, memo): """ return self + def __getstate__(self): + """ + Used for pickling. + + An object of :class:`WithPicklingByInitArgs` does not keep its dictionary + when pickled, because the object is constructed anew upon unpickling. + + Here we make some exceptions so that cached functions with + ``do_pickle=True`` attached to an object are kept in the pickle. + + EXAMPLES:: + + sage: from sage.structure.unique_representation import WithPicklingByInitArgs + sage: class X(WithPicklingByInitArgs): + ....: def __init__(self, x): + ....: self._x = x + ....: @cached_method(do_pickle=True) + ....: def genus(self): + ....: return len(self._x) + ....: + sage: import __main__; __main__.X = X # not needed in an interactive session + sage: a = X((1,2,3)) + sage: a.genus() + 3 + sage: a.genus.cache + 3 + sage: s = dumps(a) + sage: a.genus.clear_cache() + sage: a.genus.cache + sage: b = loads(s) + sage: b.genus.cache + 3 + """ + from sage.misc.cachefunc import CachedFunction + d = {} + try: + for key, value in self.__dict__.items(): + if isinstance(value, CachedFunction) and value.is_pickled_with_cache(): + d[key] = value + except AttributeError: + pass + + return d + + def __setstate__(self, d): + """ + Used for unpickling. + + EXAMPLES:: + + sage: from sage.structure.unique_representation import WithPicklingByInitArgs + sage: class X(WithPicklingByInitArgs): + ....: def __init__(self, x): + ....: self._x = x + ....: @cached_method(do_pickle=False) + ....: def genus(self): + ....: return len(self._x) + ....: + sage: import __main__; __main__.X = X # not needed in an interactive session + sage: a = X((1,2,3)) + sage: a.genus() + 3 + sage: a.genus.cache + 3 + sage: s = dumps(a) + sage: a.genus.clear_cache() + sage: a.genus.cache + sage: b = loads(s) + sage: b.genus.cache + """ + self.__dict__.update(d) + def unreduce(cls, args, keywords): """ @@ -952,7 +1025,7 @@ class CachedRepresentation(WithPicklingByInitArgs): sage: x = MyClass(value = 1) sage: x.__reduce__() - (, (, (), {'value': 1})) + (, (, (), {'value': 1}), {}) sage: x is loads(dumps(x)) True