# HG changeset patch
# User Simon King
# 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/sage/categories/category.py
+++ b/sage/categories/category.py
@@ -704,25 +704,35 @@
sage: type(C)
+
+ By trac ticket #11935, a parent class only depends on the base ring
+ of a category if the choice of the base ring alters the type of the
+ super categories. A typical example is the category of algebras over
+ a field versus algebras over a non-field.
+ ::
+
+ sage: Algebras(QQ).parent_class is Algebras(GF(3)).parent_class
+ True
+ sage: Algebras(QQ).parent_class is Algebras(ZZ).parent_class
+ False
+ sage: Algebras(ZZ['t']).parent_class is Algebras(ZZ['t','x']).parent_class
+ True
+
"""
- # Remark:
- # For now, we directly call the underlying function, avoiding the overhead
- # of using a cached function. The rationale: When this lazy method is called
- # then we can be sure that the parent class had not been constructed before.
- # The parent and element classes belong to a category, and they are pickled
- # as such. Hence, they are rightfully cached as an attribute of a category.
- #
- # However, we should try to "unify" parent classes. They should depend on the
- # super categories, but not on the base (except when the super categories depend
- # on the base). When that is done, calling the cached function will be needed again.
- #return dynamic_class("%s.parent_class"%self.__class__.__name__,
- # tuple(cat.parent_class for cat in self.super_categories()),
- # self.ParentMethods,
- # reduction = (getattr, (self, "parent_class")))
- return dynamic_class_internal.f("%s.parent_class"%self.__class__.__name__,
+ try:
+ return dynamic_class_internal.cache[("%s.parent_class"%self.__class__.__name__,
tuple(cat.parent_class for cat in self.super_categories()),
- self.ParentMethods,
- reduction = (getattr, (self, "parent_class")))
+ self.ParentMethods, None, None), ()]
+ except KeyError:
+ # Use the cache in a way that does not depend on the reduction data.
+ # The reduction is inserted afterwards. It is by construction. Since
+ # the parent class is not only cached as a dynamic class but also as
+ # an attribute of a category, the cache will not break.
+ C = dynamic_class_internal("%s.parent_class"%self.__class__.__name__,
+ tuple(cat.parent_class for cat in self.super_categories()),
+ self.ParentMethods)
+ C._reduction = (getattr, (self, "parent_class"))
+ return C
@lazy_attribute
def element_class(self):
@@ -735,26 +745,35 @@
sage: type(C)
+
+ By trac ticket #11935, an element class only depends on the base ring
+ of a category if the choice of the base ring alters the type of the
+ super categories. A typical example is the category of algebras over
+ a field versus algebras over a non-field.
+ ::
+
+ sage: Algebras(QQ).element_class is Algebras(GF(3)).element_class
+ True
+ sage: Algebras(QQ).element_class is Algebras(ZZ).element_class
+ False
+ sage: Algebras(ZZ['t']).element_class is Algebras(ZZ['t','x']).element_class
+ True
+
"""
- # Remark:
- # For now, we directly call the underlying function, avoiding the overhead
- # of using a cached function. The rationale: When this lazy method is called
- # then we can be sure that the element class had not been constructed before.
- # The parent and element classes belong to a category, and they are pickled
- # as such. Hence, they are rightfully cached as an attribute of a category.
- #
- # However, we should try to "unify" element classes. They should depend on the
- # super categories, but not on the base (except when the super categories depend
- # on the base). When that is done, calling the cached function will be needed again.
- #return dynamic_class("%s.element_class"%self.__class__.__name__,
- # (cat.element_class for cat in self.super_categories()),
- # self.ElementMethods,
- # reduction = (getattr, (self, "element_class"))
- # )
- return dynamic_class_internal.f("%s.element_class"%self.__class__.__name__,
+ try:
+ return dynamic_class_internal.cache[("%s.element_class"%self.__class__.__name__,
tuple(cat.element_class for cat in self.super_categories()),
- self.ElementMethods,
- reduction = (getattr, (self, "element_class")))
+ self.ElementMethods, None, None), ()]
+ except KeyError:
+ # Use the cache in a way that does not depend on the reduction data.
+ # The reduction is inserted afterwards. It is by construction. Since
+ # the element class is not only cached as a dynamic class but also as
+ # an attribute of a category, the cache will not break.
+ C = dynamic_class_internal("%s.element_class"%self.__class__.__name__,
+ tuple(cat.element_class for cat in self.super_categories()),
+ self.ElementMethods)
+ C._reduction = (getattr, (self, "element_class"))
+ return C
def required_methods(self):
"""