Ticket #11935: trac11935_weak_pickling_by_construction.patch

File trac11935_weak_pickling_by_construction.patch, 6.3 KB (added by SimonKing, 8 years ago)

Use a weak form of "pickling by construction" for parent and element classes of categories

  • sage/categories/category.py

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1318941258 -7200
    # Node ID d55939d59984b8053eea8c591c44b1f8de0188ef
    # Parent  5a230a7dc86c584e7faf3a5c9665ee6a7547342a
    #11935: Use a weak form of "pickling by construction" for parent and element classes of a category.
    
    diff --git a/sage/categories/category.py b/sage/categories/category.py
    a b  
    704704            <class 'sage.categories.algebras.Algebras.parent_class'>
    705705            sage: type(C)
    706706            <class 'sage.structure.dynamic_class.DynamicMetaclass'>
     707
     708        By trac ticket #11935, a parent class only depends on the base ring
     709        of a category if the choice of the base ring alters the type of the
     710        super categories. A typical example is the category of algebras over
     711        a field versus algebras over a non-field.
     712        ::
     713
     714            sage: Algebras(QQ).parent_class is Algebras(GF(3)).parent_class
     715            True
     716            sage: Algebras(QQ).parent_class is Algebras(ZZ).parent_class
     717            False
     718            sage: Algebras(ZZ['t']).parent_class is Algebras(ZZ['t','x']).parent_class
     719            True
     720
    707721        """
    708         # Remark:
    709         # For now, we directly call the underlying function, avoiding the overhead
    710         # of using a cached function. The rationale: When this lazy method is called
    711         # then we can be sure that the parent class had not been constructed before.
    712         # The parent and element classes belong to a category, and they are pickled
    713         # as such. Hence, they are rightfully cached as an attribute of a category.
    714         #
    715         # However, we should try to "unify" parent classes. They should depend on the
    716         # super categories, but not on the base (except when the super categories depend
    717         # on the base). When that is done, calling the cached function will be needed again.
    718         #return dynamic_class("%s.parent_class"%self.__class__.__name__,
    719         #                     tuple(cat.parent_class for cat in self.super_categories()),
    720         #                     self.ParentMethods,
    721         #                     reduction = (getattr, (self, "parent_class")))
    722         return dynamic_class_internal.f("%s.parent_class"%self.__class__.__name__,
     722        try:
     723            return dynamic_class_internal.cache[("%s.parent_class"%self.__class__.__name__,
    723724                             tuple(cat.parent_class for cat in self.super_categories()),
    724                              self.ParentMethods,
    725                              reduction = (getattr, (self, "parent_class")))
     725                             self.ParentMethods, None, None), ()]
     726        except KeyError:
     727            # Use the cache in a way that does not depend on the reduction data.
     728            # The reduction is inserted afterwards. It is by construction. Since
     729            # the parent class is not only cached as a dynamic class but also as
     730            # an attribute of a category, the cache will not break.
     731            C = dynamic_class_internal("%s.parent_class"%self.__class__.__name__,
     732                             tuple(cat.parent_class for cat in self.super_categories()),
     733                             self.ParentMethods)
     734            C._reduction = (getattr, (self, "parent_class"))
     735            return C
    726736
    727737    @lazy_attribute
    728738    def element_class(self):
     
    735745            <class 'sage.categories.algebras.Algebras.element_class'>
    736746            sage: type(C)
    737747            <class 'sage.structure.dynamic_class.DynamicMetaclass'>
     748
     749        By trac ticket #11935, an element class only depends on the base ring
     750        of a category if the choice of the base ring alters the type of the
     751        super categories. A typical example is the category of algebras over
     752        a field versus algebras over a non-field.
     753        ::
     754
     755            sage: Algebras(QQ).element_class is Algebras(GF(3)).element_class
     756            True
     757            sage: Algebras(QQ).element_class is Algebras(ZZ).element_class
     758            False
     759            sage: Algebras(ZZ['t']).element_class is Algebras(ZZ['t','x']).element_class
     760            True
     761
    738762        """
    739         # Remark:
    740         # For now, we directly call the underlying function, avoiding the overhead
    741         # of using a cached function. The rationale: When this lazy method is called
    742         # then we can be sure that the element class had not been constructed before.
    743         # The parent and element classes belong to a category, and they are pickled
    744         # as such. Hence, they are rightfully cached as an attribute of a category.
    745         #
    746         # However, we should try to "unify" element classes. They should depend on the
    747         # super categories, but not on the base (except when the super categories depend
    748         # on the base). When that is done, calling the cached function will be needed again.
    749         #return dynamic_class("%s.element_class"%self.__class__.__name__,
    750         #                     (cat.element_class for cat in self.super_categories()),
    751         #                     self.ElementMethods,
    752         #                     reduction = (getattr, (self, "element_class"))
    753         #                     )
    754         return dynamic_class_internal.f("%s.element_class"%self.__class__.__name__,
     763        try:
     764            return dynamic_class_internal.cache[("%s.element_class"%self.__class__.__name__,
    755765                             tuple(cat.element_class for cat in self.super_categories()),
    756                              self.ElementMethods,
    757                              reduction = (getattr, (self, "element_class")))
     766                             self.ElementMethods, None, None), ()]
     767        except KeyError:
     768            # Use the cache in a way that does not depend on the reduction data.
     769            # The reduction is inserted afterwards. It is by construction. Since
     770            # the element class is not only cached as a dynamic class but also as
     771            # an attribute of a category, the cache will not break.
     772            C = dynamic_class_internal("%s.element_class"%self.__class__.__name__,
     773                             tuple(cat.element_class for cat in self.super_categories()),
     774                             self.ElementMethods)
     775            C._reduction = (getattr, (self, "element_class"))
     776            return C
    758777
    759778    def required_methods(self):
    760779        """