Ticket #12808: trac_12808-classcall_speedup-fh.patch

File trac_12808-classcall_speedup-fh.patch, 37.3 KB (added by hivert, 10 years ago)

Tentative patch

  • module_list.py

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1335085507 -7200
    # Node ID 433e5a21d451f84b072e3e0c8346e0a35b246027
    # Parent  fd1a61a6490cd3e0bf11c5348f59da75a876ddb4
    #12808: Optimize ClasscallMetaclass using Cython
    
    diff --git a/module_list.py b/module_list.py
    a b ext_modules = [ 
    10921092    Extension('sage.misc.sage_timeit_class',
    10931093              sources = ['sage/misc/sage_timeit_class.pyx']),
    10941094   
     1095    Extension('sage.misc.classcall_metaclass',
     1096              sources = ['sage/misc/classcall_metaclass.pyx']),
     1097
    10951098    Extension('sage.misc.sagex_ds',
    10961099              sources = ['sage/misc/sagex_ds.pyx']),
    10971100   
  • new file sage/misc/classcall_metaclass.pxd

    diff --git a/sage/misc/classcall_metaclass.pxd b/sage/misc/classcall_metaclass.pxd
    new file mode 100644
    - +  
     1#*****************************************************************************
     2#  Copyright (C) 2012 Florent Hivert <Florent.Hivert at lri.fr>
     3#
     4#  Distributed under the terms of the GNU General Public License (GPL)
     5#                  http://www.gnu.org/licenses/
     6#*****************************************************************************
     7
     8cdef extern from "object.h":
     9    ctypedef class __builtin__.type [object PyHeapTypeObject]:
     10        pass
     11
     12cdef class ClasscallType(type):
     13    cdef object classcall
     14    cdef object classget
     15    cdef object classcontains
  • (a) a/sage/misc/classcall_metaclass.py vs. (b) b/sage/misc/classcall_metaclass.pyx

    diff --git a/sage/misc/classcall_metaclass.py b/sage/misc/classcall_metaclass.pyx
    rename from sage/misc/classcall_metaclass.py
    rename to sage/misc/classcall_metaclass.pyx
    a b  
    11r"""
    22Special Methods for Classes
     3
     4AUTHORS:
     5
     6- Nicolas M. Thiery (2009-2011) implementation of
     7  ``__classcall__``, ``__classget__``, ``__classcontains__``;
     8- Florent Hivert (2010-2012): implementation of ``__classcall_private__``,
     9  documentation, Cythonization and optimization.
    310"""
    411#*****************************************************************************
    5 #  Copyright (C) 2009    Nicolas M. Thiery <nthiery at users.sf.net>
    6 #  Copyright (C) 2010    Florent Hivert <Florent.Hivert at univ-rouen.fr>
     12#  Copyright (C) 2009      Nicolas M. Thiery <nthiery at users.sf.net>
     13#  Copyright (C) 2010-2012 Florent Hivert <Florent.Hivert at lri.fr>
    714#
    815#  Distributed under the terms of the GNU General Public License (GPL)
    916#                  http://www.gnu.org/licenses/
    10 #******************************************************************************
     17#*****************************************************************************
    1118
    12 from nested_class import NestedClassMetaclass
     19include '../ext/python.pxi'
    1320
    14 class ClasscallMetaclass(NestedClassMetaclass):
     21cdef extern from "Python.h":
     22    ctypedef PyObject *(*callfunc)(type, object, object) except NULL
     23    ctypedef struct PyTypeObject_call "PyTypeObject":
     24        callfunc tp_call # needed to call type.__call__ at very high speed.
     25    cdef PyTypeObject_call PyType_Type # Python's type
     26
     27__all__ = ['ClasscallType', 'ClasscallMetaclass', 'typecall', 'timeCall']
     28
     29cdef class ClasscallType(type):
     30    r"""
     31    Extension of ``type`` for class special methods
     32
     33    This is the Cython base ``type`` for the metaclass
     34    :class:`ClasscallMetaclass`.  See there for more informations.
    1535    """
    16     A metaclass providing support for special methods for classes.
     36    _included_private_doc_ = ['__call__', '__contains__', '__get__']
    1737
    18     From the Section ``Special method names`` of the Python Reference
    19     Manual: \`a class ``cls`` can implement certain operations on its
    20     instances that are invoked by special syntax (such as arithmetic
    21     operations or subscripting and slicing) by defining methods with
    22     special names\'. The purpose of this metaclass is to allow for the
    23     class ``cls`` to implement analogues of those special methods for
    24     the operations on the class itself.
     38    def __cinit__(self, *args, **opts):
     39        r"""
     40        TESTS::
    2541
    26     Currently, the following special methods are supported:
     42            sage: from sage.misc.classcall_metaclass import ClasscallType
     43            sage: class FOO(object):
     44            ...       __metaclass__ = ClasscallType
     45            sage: isinstance(FOO, ClasscallType)
     46            True
     47        """
     48        if '__classcall_private__' in self.__dict__:
     49            self.classcall = self.__classcall_private__
     50        elif hasattr(self, "__classcall__"):
     51            self.classcall = self.__classcall__
     52        else:
     53            self.classcall = None
    2754
    28      - ``.__classcall__`` (and ``.__classcall_private__``) for
    29        customizing ``cls(...)`` (analogue of ``.__call__``).
     55        self.classcontains = getattr(self, "__classcontains__", None)
     56        self.classget = getattr(self, "__classget__", None)
    3057
    31      - ``.__classcontains__`` for customizing membership testing
    32        ``x in cls`` (analogue of ``.__contains__``).
     58    def __call__(cls, *args, **opts):
     59        r"""
     60        This method implements ``cls(<some arguments>)``.
    3361
    34      - ``.__classget__`` for customizing the binding behavior in
    35        ``foo.cls`` (analogue of ``.__get__``).
     62        Let ``cls`` be a class in :class:`ClasscallMetaclass`, and
     63        consider a call of the form::
    3664
    37     See the documentation of :meth:`.__call__` and of :meth:`.__get__`
    38     and :meth:`.__contains__` for the description of the respective
    39     protocols.
     65            cls(<some arguments>)
    4066
    41     .. warning:: for technical reasons, ``__classcall__``,
    42         ``__classcall_private__``, ``__classcontains__``, and
    43         ``__classget__`` must be defined as :func:`staticmethod`'s, even
    44         though they receive the class itself as their first argument.
     67        - If ``cls`` defines a method ``__classcall_private__``, then
     68          this results in a call to::
    4569
    46     TODO: find a good name for this metaclass.
     70            cls.__classcall_private__(cls, <some arguments>)
    4771
    48     AUTHORS:
     72        - Otherwise, if ``cls`` has a method ``__classcall__``, then instead
     73          the following is called::
    4974
    50      - Nicolas M. Thiery (2009-2011) implementation of
    51        ``__classcall__``, ``__classget__``, ``__classcontains__``;
    52      - Florent Hivert (2010-01): implementation of ``__classcall_private__``,
    53        documentation.
    54     """
     75            cls.__classcall__(cls, <some arguments>)
     76
     77        - If neither of these two methods are implemented, then the standard
     78          ``type.__call__(cls, <some arguments>)`` is called, which in turn
     79          uses :meth:`~object.__new__` and :meth:`~object.__init__` as usual
     80          (see Section :python:`Basic Customization
     81          <reference/datamodel.html#basic-customization>` in the Python
     82          Reference Manual).
     83
     84        .. warning:: for technical reasons, ``__classcall__`` must be
     85            defined as a :func:`staticmethod`, even though it receives
     86            the class itself as its first argument.
     87
     88        EXAMPLES::
     89
     90            sage: from sage.misc.classcall_metaclass import ClasscallMetaclass
     91            sage: class Foo(object):
     92            ...       __metaclass__ = ClasscallMetaclass
     93            ...       @staticmethod
     94            ...       def __classcall__(cls):
     95            ...           print "calling classcall"
     96            ...           return type.__call__(cls)
     97            ...       def __new__(cls):
     98            ...           print "calling new"
     99            ...           return super(Foo, cls).__new__(cls)
     100            ...       def __init__(self):
     101            ...           print "calling init"
     102            sage: Foo()
     103            calling classcall
     104            calling new
     105            calling init
     106            <__main__.Foo object at ...>
     107
     108        This behavior is inherited::
     109
     110            sage: class Bar(Foo): pass
     111            sage: Bar()
     112            calling classcall
     113            calling new
     114            calling init
     115            <__main__.Bar object at ...>
     116
     117        We now show the usage of ``__classcall_private__``::
     118
     119            sage: class FooNoInherits(object):
     120            ...       __metaclass__ = ClasscallMetaclass
     121            ...       @staticmethod
     122            ...       def __classcall_private__(cls):
     123            ...           print "calling private classcall"
     124            ...           return type.__call__(cls)
     125            ...
     126            sage: FooNoInherits()
     127            calling private classcall
     128            <__main__.FooNoInherits object at ...>
     129
     130        Here the behavior is not inherited::
     131
     132            sage: class BarNoInherits(FooNoInherits): pass
     133            sage: BarNoInherits()
     134            <__main__.BarNoInherits object at ...>
     135
     136        We now show the usage of both::
     137
     138            sage: class Foo2(object):
     139            ...       __metaclass__ = ClasscallMetaclass
     140            ...       @staticmethod
     141            ...       def __classcall_private__(cls):
     142            ...           print "calling private classcall"
     143            ...           return type.__call__(cls)
     144            ...       @staticmethod
     145            ...       def __classcall__(cls):
     146            ...           print "calling classcall with %s"%cls
     147            ...           return type.__call__(cls)
     148            ...
     149            sage: Foo2()
     150            calling private classcall
     151            <__main__.Foo2 object at ...>
     152
     153            sage: class Bar2(Foo2): pass
     154            sage: Bar2()
     155            calling classcall with <class '__main__.Bar2'>
     156            <__main__.Bar2 object at ...>
     157
     158
     159        .. rubric:: Discussion
     160
     161        Typical applications include the implementation of factories or of
     162        unique representation (see :class:`UniqueRepresentation`). Such
     163        features are traditionaly implemented by either using a wrapper
     164        function, or fiddling with :meth:`~object.__new__`.
     165
     166        The benefit, compared with fiddling directly with
     167        :meth:`~object.__new__` is a clear separation of the three distinct
     168        roles:
     169
     170        - ``cls.__classcall__``: what ``cls(<...>)`` does
     171        - ``cls.__new__``: memory allocation for a *new* instance
     172        - ``cls.__init__``: initialization of a newly created instance
     173
     174        The benefit, compared with using a wrapper function, is that the
     175        user interface has a single handle for the class::
     176
     177            sage: x = Partition([3,2,2])
     178            sage: isinstance(x, Partition)          # todo: not implemented
     179
     180        instead of::
     181
     182            sage: isinstance(x, sage.combinat.partition.Partition_class)
     183            True
     184
     185        Another difference is that ``__classcall__`` is inherited by
     186        subclasses, which may be desirable, or not. If not, one should
     187        instead define the method ``__classcall_private__`` which will
     188        not be called for subclasses. Specifically, if a class ``cls``
     189        defines both methods ``__classcall__`` and
     190        ``__classcall_private__`` then, for any subclass ``sub`` of ``cls``:
     191
     192        - ``cls(<args>)`` will call ``cls.__classcall_private__(cls, <args>)``
     193        - ``sub(<args>)`` will call ``cls.__classcall__(sub, <args>)``
     194
     195
     196        TESTS:
     197
     198        We check that the idiom ``method_name in cls.__dict__`` works
     199        for extension types::
     200
     201           sage: "_sage_" in SageObject.__dict__, "_sage_" in Parent.__dict__
     202           (True, False)
     203
     204        We check for memory leaks::
     205
     206            sage: class NOCALL(object):
     207            ...      __metaclass__ = ClasscallMetaclass
     208            ...      pass
     209            sage: sys.getrefcount(NOCALL())
     210            1
     211
     212        We check that exception are correctly handled::
     213
     214            sage: class Exc(object):
     215            ...       __metaclass__ = ClasscallMetaclass
     216            ...       @staticmethod
     217            ...       def __classcall__(cls):
     218            ...           raise ValueError, "Calling classcall"
     219            sage: Exc()
     220            Traceback (most recent call last):
     221            ...
     222            ValueError: Calling classcall
     223        """
     224        if cls.classcall is not None:
     225            return cls.classcall(cls,  *args, **opts)
     226        else:
     227            ###########################################################
     228            # This is  type.__call__(cls, *args, **opts)  twice faster
     229            # Using the following test code:
     230            #
     231            #    sage: class NOCALL(object):
     232            #    ...      __metaclass__ = ClasscallMetaclass
     233            #    ...      pass
     234            #
     235            # with  type.__call__ :
     236            #    sage: %timeit [NOCALL() for i in range(10000)]
     237            #    125 loops, best of 3: 3.59 ms per loop
     238            # with this ugly C call:
     239            #    sage: %timeit [NOCALL() for i in range(10000)]
     240            #    125 loops, best of 3: 1.76 ms per loop
     241            #
     242            # Note: compared to a standard void Python class the slow down is
     243            # only 5%:
     244            #    sage: %timeit [Rien() for i in range(10000)]
     245            #    125 loops, best of 3: 1.7 ms per loop
     246            res = <object> PyType_Type.tp_call(cls, args, opts)
     247            Py_XDECREF(res) # During the cast to <object> Cython did INCREF(res)
     248            return res
    55249
    56250    def __get__(cls, instance, owner):
    57251        r"""
    class ClasscallMetaclass(NestedClassMeta 
    71265            defined as a :func:`staticmethod`, even though it receives
    72266            the class itself as its first argument.
    73267
    74         For technical details, and in particular the description of
    75         the ``owner`` argument, see the Section ``Implementing
    76         Descriptors`` in the Python reference manual.
     268        For technical details, and in particular the description of the
     269        ``owner`` argument, see the Section :python:`Implementing Descriptor
     270        <reference/datamodel.html#implementing-descriptors>` in the Python
     271        reference manual.
    77272
    78273        EXAMPLES:
    79274
    class ClasscallMetaclass(NestedClassMeta 
    116311
    117312        .. warning:: Inner has to be a new style class (i.e. a subclass of object).
    118313
    119         .. warning:: calling ``obj.Inner`` does no longer return a class::
     314        .. warning::
    120315
    121             sage: bind = obj.Inner
    122             calling __classget__(<class '__main__.Outer.Inner'>, <__main__.Outer object at 0x...>, <class '__main__.Outer'>)
    123             sage: bind
    124             <functools.partial object at 0x...>
     316            calling ``obj.Inner`` does no longer return a class::
     317
     318                sage: bind = obj.Inner
     319                calling __classget__(<class '__main__.Outer.Inner'>, <__main__.Outer object at 0x...>, <class '__main__.Outer'>)
     320                sage: bind
     321                <functools.partial object at 0x...>
    125322        """
    126         if hasattr(cls, "__classget__"):
    127             return cls.__classget__(cls, instance, owner)
     323        if cls.classget:
     324            return cls.classget(cls, instance, owner)
    128325        else:
    129326            return cls
    130327
    131     def __call__(cls, *args, **options):
    132         r"""
    133         This method implements ``cls(<some arguments>)``.
    134 
    135         Let ``cls`` be a class in :class:`ClasscallMetaclass`, and
    136         consider a call of the form:
    137 
    138             ``cls(<some arguments>)``
    139 
    140         If ``cls`` defines a method ``__classcall_private__``, then
    141         this results in a call to::
    142 
    143          - ``cls.__classcall_private__(cls, <some arguments>)``
    144 
    145         Otherwise, if ``cls`` has a method ``__classcall__``, then instead
    146         the following is called:
    147 
    148          - ``cls.__classcall__(cls, <some arguments>)``
    149 
    150         If neither of these two methods are implemented, then the standard
    151         ``type.__call__(cls, <some arguments>)`` is called, which in turn
    152         uses :meth:`__new__` and :meth:`__init__` as usual (see Section
    153         "Basic Customization" in the Python Reference Manual).
    154 
    155         .. warning:: for technical reasons, ``__classcall__`` must be
    156             defined as a :func:`staticmethod`, even though it receives
    157             the class itself as its first argument.
    158 
    159         EXAMPLES::
    160 
    161             sage: from sage.misc.classcall_metaclass import ClasscallMetaclass
    162             sage: class Foo(object):
    163             ...       __metaclass__ = ClasscallMetaclass
    164             ...       @staticmethod
    165             ...       def __classcall__(cls):
    166             ...           print "calling classcall"
    167             ...           return type.__call__(cls)
    168             ...       def __new__(cls):
    169             ...           print "calling new"
    170             ...           return super(Foo, cls).__new__(cls)
    171             ...       def __init__(self):
    172             ...           print "calling init"
    173             ...
    174             sage: Foo()
    175             calling classcall
    176             calling new
    177             calling init
    178             <__main__.Foo object at ...>
    179 
    180        This behavior is inherited::
    181 
    182             sage: class Bar(Foo): pass
    183             sage: Bar()
    184             calling classcall
    185             calling new
    186             calling init
    187             <__main__.Bar object at ...>
    188 
    189        We now show the usage of :meth:`__classcall_private__`::
    190 
    191             sage: class FooNoInherits(object):
    192             ...       __metaclass__ = ClasscallMetaclass
    193             ...       @staticmethod
    194             ...       def __classcall_private__(cls):
    195             ...           print "calling private classcall"
    196             ...           return type.__call__(cls)
    197             ...
    198             sage: FooNoInherits()
    199             calling private classcall
    200             <__main__.FooNoInherits object at ...>
    201 
    202             sage: class BarNoInherits(FooNoInherits): pass
    203             sage: BarNoInherits()
    204             <__main__.BarNoInherits object at ...>
    205 
    206        We now show the usage of both::
    207 
    208             sage: class Foo2(object):
    209             ...       __metaclass__ = ClasscallMetaclass
    210             ...       @staticmethod
    211             ...       def __classcall_private__(cls):
    212             ...           print "calling private classcall"
    213             ...           return type.__call__(cls)
    214             ...       @staticmethod
    215             ...       def __classcall__(cls):
    216             ...           print "calling classcall with %s"%cls
    217             ...           return type.__call__(cls)
    218             ...
    219             sage: Foo2()
    220             calling private classcall
    221             <__main__.Foo2 object at ...>
    222 
    223             sage: class Bar2(Foo2): pass
    224             sage: Bar2()
    225             calling classcall with <class '__main__.Bar2'>
    226             <__main__.Bar2 object at ...>
    227 
    228 
    229         ..rubric:: Discussion
    230 
    231         Typical applications include the implementation of factories or of
    232         unique representation (see :class:`UniqueRepresentation`). Such
    233         features are traditionaly implemented by either using a wrapper
    234         function, or fiddling with :meth:`__new__`.
    235 
    236         The benefit, compared with fiddling directly with :meth:`__new__`
    237         is a clear separation of the three distinct roles:
    238 
    239          - :meth:`cls.__classcall__`: what cls(<...>) does
    240          - :meth:`cls.__new__`: memory allocation for a *new* instance
    241          - :meth:`cls.__init__`: initialization of a newly created instance
    242 
    243         The benefit, compared with using a wrapper function, is that the
    244         user interface has a single handle for the class::
    245 
    246             sage: x = Partition([3,2,2])
    247             sage: isinstance(x, Partition)          # todo: not implemented
    248 
    249         instead of::
    250 
    251             sage: isinstance(x, sage.combinat.partition.Partition_class)
    252             True
    253 
    254         Another difference is that :meth:`__classcall__` is inherited by
    255         subclasses, which may be desirable, or not. If not, one should
    256         instead define the method :meth:`__classcall_private__` which will
    257         not be called for subclasses. Specifically, if a class ``cls``
    258         defines both methods ``__classcall__`` and
    259         ``__classcall_private__`` then, for any subclass ``sub`` of ``cls``:
    260 
    261          - ``cls(<args>)`` will call ``cls.__classcall_private__(cls, <args>)``
    262          - ``sub(<args>)`` will call ``cls.__classcall__(sub, <args>)``
    263 
    264 
    265         TESTS:
    266 
    267         We check that the idiom ``method_name in cls.__dict__`` works
    268         for extension types::
    269 
    270            sage: "_sage_" in SageObject.__dict__, "_sage_" in Parent.__dict__
    271            (True, False)
    272         """
    273         if '__classcall_private__' in cls.__dict__:
    274             return cls.__classcall_private__(cls, *args, **options)
    275         elif hasattr(cls, "__classcall__"):
    276             return cls.__classcall__(cls, *args, **options)
    277         else:
    278             return type.__call__(cls, *args, **options)
    279 
    280328    def __contains__(cls, x):
    281329        r"""
    282330        This method implements membership testing for a class
    283331
    284332        Let ``cls`` be a class in :class:`ClasscallMetaclass`, and consider
    285         a call of the form:
     333        a call of the form::
    286334
    287             ``x in cls``
     335            x in cls
    288336
    289337        If ``cls`` defines a method ``__classcontains__``, then this
    290338        results in a call to::
    291339
    292          - ``cls.__classcontains__(cls, x)``
     340           cls.__classcontains__(cls, x)
    293341
    294342        .. warning:: for technical reasons, ``__classcontains__`` must
    295343            be defined as a :func:`staticmethod`, even though it
    class ClasscallMetaclass(NestedClassMeta 
    322370            ...
    323371            TypeError: argument of type 'type' is not iterable
    324372        """
    325         if hasattr(cls, "__classcontains__"):
    326             return cls.__classcontains__(cls, x)
     373        if cls.classcontains:
     374            return cls.classcontains(cls, x)
    327375        else:
    328376            return x in object
     377
     378from nested_class import NestedClassMetaclass
     379class ClasscallMetaclass(ClasscallType, NestedClassMetaclass):
     380    """
     381    A metaclass providing support for special methods for classes.
     382
     383    From the Section :python:`Special method names
     384    <reference/datamodel.html#special-method-names>` of the Python Reference
     385    Manual:
     386
     387        \`a class ``cls`` can implement certain operations on its instances
     388        that are invoked by special syntax (such as arithmetic operations or
     389        subscripting and slicing) by defining methods with special
     390        names\'.
     391
     392    The purpose of this metaclass is to allow for the class ``cls`` to
     393    implement analogues of those special methods for the operations on the
     394    class itself.
     395
     396    Currently, the following special methods are supported:
     397
     398     - ``.__classcall__`` (and ``.__classcall_private__``) for
     399       customizing ``cls(...)`` (analogue of ``.__call__``).
     400
     401     - ``.__classcontains__`` for customizing membership testing
     402       ``x in cls`` (analogue of ``.__contains__``).
     403
     404     - ``.__classget__`` for customizing the binding behavior in
     405       ``foo.cls`` (analogue of ``.__get__``).
     406
     407    See the documentation of :meth:`.__call__` and of :meth:`.__get__`
     408    and :meth:`.__contains__` for the description of the respective
     409    protocols.
     410
     411    .. warning:: for technical reasons, ``__classcall__``,
     412        ``__classcall_private__``, ``__classcontains__``, and
     413        ``__classget__`` must be defined as :func:`staticmethod`'s, even
     414        though they receive the class itself as their first argument.
     415
     416    ``ClasscallMetaclass`` is implemented using an extension of the base
     417    :class:`type` called :class:`ClasscallType`.
     418
     419    .. SEEALSO:: :class:`ClasscallType`
     420
     421    TODO: find a good name for this metaclass.
     422
     423    TESTS::
     424
     425        sage: PerfectMatchings(2).list()
     426        [PerfectMatching [(2, 1)]]
     427
     428    .. note::
     429
     430        If a class is put in this metaclass it automatically becomes a
     431        new-style class::
     432
     433            sage: from sage.misc.classcall_metaclass import ClasscallMetaclass
     434            sage: class Foo:
     435            ...       __metaclass__ = ClasscallMetaclass
     436            sage: x = Foo(); x
     437            <__main__.Foo object at 0x...>
     438            sage: issubclass(Foo, object)
     439            True
     440            sage: isinstance(Foo, type)
     441            True
     442    """
     443    pass
     444
     445def typecall(type cls, *args, **opts):
     446    r"""
     447    Object construction
     448
     449    This is a faster equivalent to ``type.__call__(cls, <some arguments>)``.
     450
     451    INPUT:
     452
     453    - ``cls`` -- the class used for constructing the instance. It must be
     454      a builtin type or a new style class (inheriting from :class:`object`).
     455
     456    EXAMPLES::
     457
     458        sage: from sage.misc.classcall_metaclass import typecall
     459        sage: class Foo(object): pass
     460        sage: typecall(Foo)
     461        <__main__.Foo object at 0x...>
     462        sage: typecall(list)
     463        []
     464        sage: typecall(Integer, 2)
     465        2
     466
     467    .. warning::
     468
     469        :func:`typecall` doesn't work for old style class (not inheriting from
     470        :class:`object`)::
     471
     472            sage: class Bar: pass
     473            sage: typecall(Bar)
     474            Traceback (most recent call last):
     475            ...
     476            TypeError: Argument 'cls' has incorrect type (expected type, got classobj)
     477    """
     478    # See remarks in ClasscallType.__call__(cls, *args, **opts) for speed.
     479    res = <object> PyType_Type.tp_call(cls, args, opts)
     480    Py_XDECREF(res) # During the cast to <object> Cython did INCREF(res)
     481    return res
     482
     483# Class for timing::
     484
     485class CRef(object):
     486    def __init__(self, i):
     487        """
     488        TESTS::
     489
     490            sage: from sage.misc.classcall_metaclass import CRef
     491            sage: P = CRef(2); P.i
     492            3
     493        """
     494        self.i = i+1
     495
     496class C2(object):
     497    __metaclass__ = ClasscallMetaclass
     498    def __init__(self, i):
     499        """
     500        TESTS::
     501
     502            sage: from sage.misc.classcall_metaclass import C2
     503            sage: P = C2(2); P.i
     504            3
     505        """
     506        self.i = i+1
     507
     508class C3(object, metaclass = ClasscallMetaclass):
     509    def __init__(self, i):
     510        """
     511        TESTS::
     512
     513            sage: from sage.misc.classcall_metaclass import C3
     514            sage: P = C3(2); P.i
     515            3
     516        """
     517        self.i = i+1
     518
     519class C2C(object):
     520    __metaclass__ = ClasscallMetaclass
     521    @staticmethod
     522    def __classcall__(cls, i):
     523        """
     524        TESTS::
     525
     526            sage: from sage.misc.classcall_metaclass import C2C
     527            sage: C2C(2)
     528            3
     529        """
     530        return i+1
     531
     532def timeCall(T, int n, *args):
     533    r"""
     534    We illustrate some timing when using the classcall mechanism.
     535
     536    EXAMPLES::
     537
     538        sage: from sage.misc.classcall_metaclass import (
     539        ...       ClasscallMetaclass, CRef, C2, C3, C2C, timeCall)
     540        sage: timeCall(object, 1000)
     541
     542    For reference let construct basic objects and a basic Python class::
     543
     544        sage: %timeit timeCall(object, 1000)   # not tested
     545        625 loops, best of 3: 41.4 µs per loop
     546
     547        sage: i1 = int(1); i3 = int(3) # don't use Sage's Integer
     548        sage: class PRef(object):
     549        ...       def __init__(self, i):
     550        ...           self.i = i+i1
     551
     552    For a Python class, compared to the reference class there is a 10%
     553    overhead in using :class:`ClasscallMetaclass` if there is no classcall
     554    defined::
     555
     556        sage: class P(object):
     557        ...       __metaclass__ = ClasscallMetaclass
     558        ...       def __init__(self, i):
     559        ...           self.i = i+i1
     560
     561        sage: %timeit timeCall(PRef, 1000, i3)   # not tested
     562        625 loops, best of 3: 420 µs per loop
     563        sage: %timeit timeCall(P, 1000, i3)      # not tested
     564        625 loops, best of 3: 458 µs per loop
     565
     566    For a Cython class (not cdef since they doesn't allows metaclasses), the
     567    overhead is a little larger::
     568
     569        sage: %timeit timeCall(CRef, 1000, i3)   # not tested
     570        625 loops, best of 3: 266 µs per loop
     571        sage: %timeit timeCall(C2, 1000, i3)     # not tested
     572        625 loops, best of 3: 298 µs per loop
     573
     574    Let's now compare when there is a classcall defined::
     575
     576        sage: class PC(object):
     577        ...       __metaclass__ = ClasscallMetaclass
     578        ...       @staticmethod
     579        ...       def __classcall__(cls, i):
     580        ...           return i+i1
     581        sage: %timeit timeCall(C2C, 1000, i3)   # not tested
     582        625 loops, best of 3: 148 µs per loop
     583        sage: %timeit timeCall(PC, 1000, i3)    # not tested
     584        625 loops, best of 3: 289 µs per loop
     585
     586    The overhead of the indirection ( ``C(...) ->
     587    ClasscallMetaclass.__call__(...) -> C.__classcall__(...)``) is
     588    unfortunately quite large in this case (two method calls instead of
     589    one). In reasonable usecases, the overhead should be mostly hidden by the
     590    computations inside the classcall::
     591
     592        sage: %timeit timeCall(C2C.__classcall__, 1000, C2C, i3)  # not tested
     593        625 loops, best of 3: 33 µs per loop
     594        sage: %timeit timeCall(PC.__classcall__, 1000, PC, i3)    # not tested
     595        625 loops, best of 3: 131 µs per loop
     596
     597    Finally, there is no significant difference between Cython's V2 and V3
     598    syntax for metaclass::
     599
     600        sage: %timeit timeCall(C2, 1000, i3)   # not tested
     601        625 loops, best of 3: 330 µs per loop
     602        sage: %timeit timeCall(C3, 1000, i3)   # not tested
     603        625 loops, best of 3: 328 µs per loop
     604    """
     605    cdef int i
     606    for 0<=i<n:
     607        T(*args)
  • sage/structure/unique_representation.py

    diff --git a/sage/structure/unique_representation.py b/sage/structure/unique_representation.py
    a b  
    11r"""
    22Unique Representation
     3
     4Abstract class for singleton and unique representation behavior.
     5
     6.. SEEALSO::
     7
     8   :class:`sage.structure.factory.UniqueFactory`
    39"""
    410#*****************************************************************************
    511#  Copyright (C) 2008 Nicolas M. Thiery <nthiery at users.sf.net>
    Unique Representation 
    1723#******************************************************************************
    1824
    1925from sage.misc.cachefunc import cached_function
    20 from sage.misc.classcall_metaclass import ClasscallMetaclass
     26from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall
    2127
    2228class UniqueRepresentation:
    2329    """
    class UniqueRepresentation: 
    123129    Similarly, the identity is used as hash function, which is also as
    124130    fast as it can get. However this means that the hash function may
    125131    change in between Sage sessions, or even within the same Sage
    126     session (see below). Subclasses should overload :meth:`__hash__`
     132    session (see below). Subclasses should overload :meth:`__hash__ <object.__hash__>`
    127133    if this could be a problem.
    128134
    129135
    130136    The arguments can consist of any combination of positional or
    131     keyword arguments, as taken by a usual :meth:`__init__`
     137    keyword arguments, as taken by a usual
     138    :meth:`__init__ <object.__init__>`
    132139    function. However, all values passed in should be hashable::
    133140
    134141        sage: MyClass(value = [1,2,3])
    class UniqueRepresentation: 
    195202    implementation of ``__classcall__`` provided by
    196203    :class:`UniqueRepresentation`. This one in turn handles the caching
    197204    and, if needed, constructs and initializes a new object in the
    198     class using :meth:`__new__` and :meth:`__init__` as usual.
     205    class using :meth:`__new__<object.__new__>` and :meth:`__init__<object.__init__>` as usual.
    199206
    200207    Constraints:
    201208
    202      - :meth:`__classcall__` is a staticmethod (like, implicitly, :meth:`__new__`)
     209     - :meth:`__classcall__` is a staticmethod (like, implicitly, :meth:`__new__<object.__new__>`)
    203210
    204211     - the preprocessing on the arguments should be
    205        idempotent. Namely, If :meth:`MyClass2.__classcall__` calls
     212       idempotent. Namely, If ``MyClass2.__classcall__`` calls
    206213       ``UniqueRepresentation.__classcall__(<some_arguments>)``, then
    207214       it should accept <some_arguments> as its own input, and pass it
    208215       down unmodified to :meth:`UniqueRepresentation.__classcall__`.
    209      - :meth:`MyClass2.__classcall__` should return the result of
     216     - ``MyClass2.__classcall__`` should return the result of
    210217       :meth:`UniqueRepresentation.__classcall__` without modifying it.
    211218
    212     Other than that :meth:`MyClass2.__classcall__` may play any tricks,
     219    Other than that ``MyClass2.__classcall__`` may play any tricks,
    213220    like acting as a Factory and returning object from other classes.
    214221
    215222    .. rubric:: Unique representation and mutability
    class UniqueRepresentation: 
    260267    the same object already exists.
    261268
    262269    :class:`UniqueRepresentation` tries to ensure appropriate pickling by
    263     implementing a :meth:`__reduce__` method returning the arguments
     270    implementing a :meth:`__reduce__ <object.__reduce__>` method returning the arguments
    264271    passed to the constructor::
    265272
    266273        sage: import __main__             # Fake MyClass being defined in a python module
    class UniqueRepresentation: 
    269276        sage: loads(dumps(x)) is x
    270277        True
    271278
    272     :class:`UniqueRepresentation` uses the :meth:`__reduce__` pickle
    273     protocol rather than :meth:`__getnewargs__` because the later does
    274     not handle keyword arguments::
     279    :class:`UniqueRepresentation` uses the :meth:`__reduce__
     280    <object.__reduce__>` pickle protocol rather than :meth:`__getnewargs__
     281    <object.__getnewargs__>` because the later does not handle keyword
     282    arguments::
    275283
    276284        sage: x = MyClass(value = 1)
    277285        sage: x.__reduce__()
    class UniqueRepresentation: 
    279287        sage: x is loads(dumps(x))
    280288        True
    281289
    282     Caveat: the default implementation of :meth:`__reduce__` in
    283     :class:`UniqueRepresentation` requires to store the constructor's
    284     arguments in the instance dictionary upon construction::
     290    .. warning::
    285291
    286         sage: x.__dict__
    287         {'_reduction': (<class '__main__.MyClass'>, (), {'value': 1}), 'value': 1}
     292        the default implementation of :meth:`__reduce__ <object.__reduce__>`
     293        in :class:`UniqueRepresentation` requires to store the constructor's
     294        arguments in the instance dictionary upon construction::
    288295
    289     It is often easy in a derived subclass to reconstruct the
    290     constructors arguments from the instance data structure. When this
    291     is the case, :meth:`__reduce__` should be overridden; automagically
     296            sage: x.__dict__
     297            {'_reduction': (<class '__main__.MyClass'>, (), {'value': 1}), 'value': 1}
     298
     299    It is often easy in a derived subclass to reconstruct the constructors
     300    arguments from the instance data structure. When this is the case,
     301    :meth:`__reduce__ <object.__reduce__>` should be overridden; automagically
    292302    the arguments won't be stored anymore::
    293303
    294304        sage: class MyClass3(UniqueRepresentation):
    class UniqueRepresentation: 
    387397    :class:`~sage.misc.classcall_metaclass.ClasscallMetaclass`
    388398    of the standard Python type. The following example explains why.
    389399
    390     We define a variant of ``MyClass`` where the calls to :meth:`__init__`
    391     are traced::
     400    We define a variant of ``MyClass`` where the calls to
     401    :meth:`__init__<object.__init__>` are traced::
    392402
    393403        sage: class MyClass(UniqueRepresentation):
    394404        ...       def __init__(self, value):
    class UniqueRepresentation: 
    402412        initializing object
    403413        sage: z = MyClass(1)
    404414
    405     As desired the __init__ method was only called the first time,
    406     which is an important feature.
     415    As desired the :meth:`__init__<object.__init__>` method was only called
     416    the first time, which is an important feature.
    407417
    408418    As far as we can tell, this is not achievable while just using
    409     :meth:`__new__` and :meth:`__init__` (as defined by type; see
    410     Section "Basic Customization" in the Python Reference
    411     Manual). Indeed, :meth:`__init__` is called systematically on the
    412     result of :meth:`__new__` whenever the result is an instance of
    413     the class.
     419    :meth:`__new__<object.__new__>` and :meth:`__init__<object.__init__>` (as
     420    defined by type; see Section :python:`Basic Customization
     421    <reference/datamodel.html#basic-customization>` in the Python Reference
     422    Manual). Indeed, :meth:`__init__<object.__init__>` is called
     423    systematically on the result of :meth:`__new__<object.__new__>` whenever
     424    the result is an instance of the class.
    414425
    415     Another difficulty is that argument preprocessing (as in the
    416     example above) cannot be handled by :meth:`__new__`, since the
    417     unprocessed arguments will be passed down to :meth:`__init__`.
     426    Another difficulty is that argument preprocessing (as in the example
     427    above) cannot be handled by :meth:`__new__<object.__new__>`, since the
     428    unprocessed arguments will be passed down to
     429    :meth:`__init__<object.__init__>`.
    418430
    419431    .. rubric:: Mixing super types and super classes
    420432
    class UniqueRepresentation: 
    432444
    433445    __metaclass__ = ClasscallMetaclass
    434446
     447    _included_private_doc_ = ["__classcall__"]
     448
    435449    @cached_function # automatically a staticmethod
    436450    def __classcall__(cls, *args, **options):
    437451        """
    class UniqueRepresentation: 
    446460            sage: x is y
    447461            True
    448462        """
    449         instance = type.__call__(cls, *args, **options)
     463        instance = typecall(cls, *args, **options)
    450464        assert isinstance( instance, cls )
    451465        if instance.__class__.__reduce__ == UniqueRepresentation.__reduce__:
    452466            instance._reduction = (cls, args, options)
    class UniqueRepresentation: 
    537551
    538552    def __reduce__(self):
    539553        """
    540         Returns the argument that have been passed to :meth:`__new__`
     554        Returns the argument that have been passed to :meth:`__new__<object.__new__>`
    541555        to construct this object, as per the pickle protocol.
    542556
    543557        See also :class:`UniqueRepresentation` for a discussion.