Ticket #11935: trac11935_named_class.patch

File trac11935_named_class.patch, 9.4 KB (added by SimonKing, 8 years ago)

Refactor parent/element class creation: Use _make_named_class

  • sage/categories/category.py

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1321445885 -3600
    # Node ID a2f6e18fff168a863794da76d3d3edce86055214
    # Parent  e56b48be49f3626e5002176c242ce1cc1c53c0d0
    #11935: _make_named_class providing a unified way to create parent and element classes
    
    diff --git a/sage/categories/category.py b/sage/categories/category.py
    a b  
    838838        """
    839839        pass
    840840
     841    def _make_named_class(self, name, method_provider):
     842        """
     843        Purpose: Create the parent/element/... class of self.
     844
     845        INPUT:
     846
     847        - ``name``: String, the name of the class as an attribute of self.
     848        - ``method_provider``: The name of an attribute of self
     849          that provides methods for the new class (in addition to
     850          what comes from the super categories).
     851
     852        ASSUMPTION:
     853
     854        It is assumed that this method is only called from a lazy
     855        attribute whose name coincides with the given ``name``
     856
     857        OUTPUT:
     858
     859        A dynamic class that has the corresponding named classes of
     860        self's super_categories as bases and adds the method provided
     861        by `getattr(self,method_provider)`.
     862
     863        NOTE:
     864
     865        In this default implementation, the reduction data of the
     866        named class make it depend on self. :class:`CategoryWithParameters`
     867        overrides this method such that the named classes can be
     868        shared between different categories.
     869
     870        EXAMPLES::
     871
     872            sage: PC = Algebras(QQ).parent_class   # indirect doctest
     873            sage: PC
     874            <class 'sage.categories.algebras.Algebras.parent_class'>
     875            sage: type(PC)
     876            <class 'sage.structure.dynamic_class.DynamicMetaclass'>
     877            sage: PC.__bases__
     878            (<class 'sage.categories.rings.Rings.parent_class'>,
     879             <class 'sage.categories.vector_spaces.VectorSpaces.parent_class'>)
     880            sage: loads(dumps(PC)) is PC
     881            True
     882
     883        """
     884        # Remark:
     885        # We directly call the underlying function, avoiding the overhead
     886        # of using a cached function. The rationale:
     887        # By assumption, this method is called by a lazy method. Hence,if it is called
     888        # then we can be sure that the parent class had not been constructed before.
     889        # The parent and element classes belong to a category, and they are pickled
     890        # as such. Hence, they are rightfully cached as an attribute of a category.
     891        return dynamic_class_internal.f("%s.%s"%(self.__class__.__name__,name),
     892                             tuple(getattr(cat,name) for cat in self._super_categories),
     893                             getattr(self,method_provider),
     894                             reduction = (getattr, (self, name)))
     895
     896
    841897    @lazy_attribute
    842898    def parent_class(self):
    843899        """
     
    873929        :class:`~sage.categories.category_types.Category_over_base` and
    874930        :class:`sage.categories.category.JoinCategory`.
    875931        """
    876         # Remark:
    877         # We directly call the underlying function, avoiding the overhead
    878         # of using a cached function. The rationale: When this lazy method is called
    879         # then we can be sure that the parent class had not been constructed before.
    880         # The parent and element classes belong to a category, and they are pickled
    881         # as such. Hence, they are rightfully cached as an attribute of a category.
    882         return dynamic_class_internal.f("%s.parent_class"%self.__class__.__name__,
    883                              tuple(cat.parent_class for cat in self._super_categories),
    884                              self.ParentMethods,
    885                              reduction = (getattr, (self, "parent_class")))
     932        return self._make_named_class('parent_class', 'ParentMethods')
    886933
    887934    @lazy_attribute
    888935    def element_class(self):
     
    912959            True
    913960
    914961        """
    915         # Remark:
    916         # We directly call the underlying function, avoiding the overhead
    917         # of using a cached function. The rationale: When this lazy method is called
    918         # then we can be sure that the element class had not been constructed before.
    919         # The parent and element classes belong to a category, and they are pickled
    920         # as such. Hence, they are rightfully cached as an attribute of a category.
    921         return dynamic_class_internal.f("%s.element_class"%self.__class__.__name__,
    922                              tuple(cat.element_class for cat in self._super_categories),
    923                              self.ElementMethods,
    924                              reduction = (getattr, (self, "element_class")))
     962        return self._make_named_class('element_class', 'ElementMethods')
    925963
    926964    def required_methods(self):
    927965        """
     
    16161654        False
    16171655
    16181656    """
    1619     @lazy_attribute
    1620     def parent_class(self):
     1657    def _make_named_class(self, name, method_provider):
    16211658        """
    1622         Return a parent class that only depends on the parent classes of the super-categories.
     1659        Purpose: Create the parent/element/... class of self.
     1660
     1661        INPUT:
     1662
     1663        - ``name``: String, the name of the class as an attribute of self.
     1664        - ``method_provider``: The name of an attribute of self
     1665          that provides methods for the new class (in addition to
     1666          what comes from the super categories).
     1667
     1668        ASSUMPTION:
     1669
     1670        It is assumed that this method is only called from a lazy
     1671        attribute whose name coincides with the given ``name``
     1672
     1673        OUTPUT:
     1674
     1675        A dynamic class that has the corresponding named classes of
     1676        self's super_categories as bases and adds the method provided
     1677        by `getattr(self,method_provider)`.
     1678
     1679        NOTE:
     1680
     1681        The returned class *only* depends on the corresponding named
     1682        classes of the super categories and on the provided methods.
     1683        So, some categories will share their named classes. The typical
     1684        use case is to make the named class indendent of the choice
     1685        of a base field.
    16231686
    16241687        EXAMPLE::
    16251688
    1626             sage: Bimodules(ZZ,ZZ['x']).parent_class is Bimodules(ZZ,ZZ).parent_class
     1689            sage: Bimodules(ZZ,ZZ['x']).parent_class is Bimodules(ZZ,ZZ).parent_class #indirect doctest
    16271690            True
    16281691            sage: Modules(GF(3),dispatch=False).parent_class is Modules(ZZ).parent_class
    16291692            True
     
    16381701        # If C1.parent_class is identic with C2.parent_class, and C1.parent_class was
    16391702        # requested first, then C2.parent_class is pickled via getattr(C1,"parent_class").
    16401703        try:
    1641             return dynamic_class_internal.cache[("%s.parent_class"%self.__class__.__name__,
    1642                              tuple(cat.parent_class for cat in self._super_categories),
    1643                              self.ParentMethods, None, None), ()]
     1704            return dynamic_class_internal.cache[("%s.%s"%(self.__class__.__name__,name),
     1705                             tuple(getattr(cat,name) for cat in self._super_categories),
     1706                             getattr(self, method_provider), None, None), ()]
    16441707        except KeyError:
    16451708            # Use the cache in a way that does not depend on the reduction data.
    16461709            # The reduction is inserted afterwards. It is by construction. Since
    16471710            # the parent class is not only cached as a dynamic class but also as
    16481711            # an attribute of a category, the cache will not break.
    1649             C = dynamic_class_internal("%s.parent_class"%self.__class__.__name__,
    1650                              tuple(cat.parent_class for cat in self._super_categories),
    1651                              self.ParentMethods)
    1652             C._reduction = (getattr, (self, "parent_class"))
    1653             return C
    1654 
    1655     @lazy_attribute
    1656     def element_class(self):
    1657         """
    1658         Return an element class that only depends on the element classes of the super-categories.
    1659 
    1660         EXAMPLE::
    1661 
    1662             sage: Bimodules(ZZ,ZZ['x']).element_class is Bimodules(ZZ,ZZ).element_class
    1663             True
    1664             sage: Modules(GF(3),dispatch=False).element_class is Modules(ZZ).element_class
    1665             True
    1666             sage: Modules(GF(3)).element_class is Modules(ZZ).element_class
    1667             False
    1668 
    1669         """
    1670         try:
    1671             return dynamic_class_internal.cache[("%s.element_class"%self.__class__.__name__,
    1672                              tuple(cat.element_class for cat in self._super_categories),
    1673                              self.ElementMethods, None, None), ()]
    1674         except KeyError:
    1675             # Use the cache in a way that does not depend on the reduction data.
    1676             # The reduction is inserted afterwards. It is by construction. Since
    1677             # the element class is not only cached as a dynamic class but also as
    1678             # an attribute of a category, the cache will not break.
    1679             C = dynamic_class_internal("%s.element_class"%self.__class__.__name__,
    1680                              tuple(cat.element_class for cat in self._super_categories),
    1681                              self.ElementMethods)
    1682             C._reduction = (getattr, (self, "element_class"))
     1712            C = dynamic_class_internal("%s.%s"%(self.__class__.__name__,name),
     1713                             tuple(getattr(cat,name) for cat in self._super_categories),
     1714                             getattr(self, method_provider))
     1715            C._reduction = (getattr, (self, name))
    16831716            return C
    16841717
    16851718