Ticket #10467: 10467attribute_lookup.patch

File 10467attribute_lookup.patch, 11.0 KB (added by SimonKing, 12 years ago)

Speedup for attribute lookup of Parents and Elements

  • sage/structure/element.pyx

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1292153319 -3600
    # Node ID 159f6838b49c6abb3906994c2fa56108230bfa11
    # Parent  3a96f07d466b67f92bb399de731c30f9d4daf1a7
    #10467: Speedup of attribute lookup for Parents and Elements
    
    diff -r 3a96f07d466b -r 159f6838b49c sage/structure/element.pyx
    a b  
    186186from types import MethodType
    187187
    188188from sage.categories.category   import Category
    189 from sage.structure.parent      cimport Parent
    190 from sage.structure.parent      import is_extension_type
     189from sage.structure.parent      cimport Parent, raise_attribute_error
     190from sage.structure.parent      import is_extension_type, getattr_from_other_class
    191191from sage.misc.lazy_format      import LazyFormat
    192192
    193193# This classes uses element.pxd.  To add data members, you
     
    272272        and ``cat.element_class``, in that order, for attribute
    273273        lookup.
    274274
     275        NOTE:
     276
     277        Attributes beginning with two underscores but not ending with
     278        an unnderscore are considered private and are thus exempted
     279        from the lookup in ``cat.element_class``.
     280
    275281        EXAMPLES:
    276282
    277283        We test that ``1`` (an instance of the extension type
     
    301307            Traceback (most recent call last):
    302308            ...
    303309            AttributeError: 'LeftZeroSemigroup_with_category.element_class' object has no attribute 'blah_blah'
     310
     311        We test that "private" attributes are not requested from the element class
     312        of the category (trac ticket #10467)::
     313
     314            sage: C = CommutativeRings()
     315            sage: P.<x> = QQ[]
     316            sage: C.element_class.__foo = 'bar'
     317            sage: x.parent().category() is C
     318            True
     319            sage: x.__foo
     320            Traceback (most recent call last):
     321            ...
     322            AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute '__foo'
     323
    304324        """
    305         from sage.structure.parent import getattr_from_other_class
     325        if name.startswith('__') and not name.endswith('_'):
     326            raise_attribute_error(self, name)
    306327        return getattr_from_other_class(self, self.parent().category().element_class, name)
    307328
    308329    def __dir__(self):
  • sage/structure/parent.pxd

    diff -r 3a96f07d466b -r 159f6838b49c sage/structure/parent.pxd
    a b  
    99#cimport sage.categories.object
    1010cimport sage.structure.category_object
    1111
     12cdef inline raise_attribute_error(self, name)
    1213
    1314cdef class Parent(category_object.CategoryObject):
    1415
  • sage/structure/parent.pyx

    diff -r 3a96f07d466b -r 159f6838b49c sage/structure/parent.pyx
    a b  
    8787cdef inline parent_c(x):
    8888    if PY_TYPE_CHECK(x, element.Element):
    8989        return (<element.Element>x)._parent
    90     elif hasattr(x, 'parent'):
    91         return x.parent()
     90#    elif hasattr(x, 'parent'):
     91#        return x.parent()
     92#    else:
     93#        return <object>PY_TYPE(x)
    9294    else:
    93         return <object>PY_TYPE(x)
     95        try:
     96            return x.parent()
     97        except AttributeError:
     98            return <object>PY_TYPE(x)
    9499
    95100cdef _record_exception():
    96101    from element import get_coercion_model
     
    150155    pass
    151156methodwrapper = type(A.__call__)
    152157
    153 def raise_attribute_error(self, name):
     158cdef inline raise_attribute_error(self, name):
    154159    """
    155160    Tries to emulate the standard Python AttributeError exception
    156161
    157162    EXAMPLES::
    158163
    159         sage: sage.structure.parent.raise_attribute_error(1, "bla")
     164        sage: 1.bla  #indirect doctest
    160165        Traceback (most recent call last):
    161166        ...
    162167        AttributeError: 'sage.rings.integer.Integer' object has no attribute 'bla'
    163         sage: sage.structure.parent.raise_attribute_error(QQ[x].gen(), "bla")
     168        sage: QQ[x].gen().bla
    164169        Traceback (most recent call last):
    165170        ...
    166171        AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute 'bla'
     
    272277        raise_attribute_error(self, name)
    273278    if isinstance(attribute, methodwrapper):
    274279        raise_attribute_error(self, name)
    275     if hasattr(attribute, "__get__"):
    276         # Conditionally defined lazy_attributes don't work well with fake subclasses
    277         # (a TypeError is raised if the lazy attribute is not defined)
    278         # For the moment, we ignore that when this occurs
    279         # Other descriptors (including __weakref__) also break.
    280         try:
    281             return attribute.__get__(self, cls)
    282         except TypeError:
    283             raise_attribute_error(self, name)
    284     else:
     280    try:
     281        getter = attribute.__get__
     282    except AttributeError:
    285283        return attribute
     284    # Conditionally defined lazy_attributes don't work well with fake subclasses
     285    # (a TypeError is raised if the lazy attribute is not defined)
     286    # For the moment, we ignore that when this occurs
     287    # Other descriptors (including __weakref__) also break.
     288    try:
     289        return getter(self, cls)
     290    except TypeError:
     291        pass
     292    raise_attribute_error(self, name)
    286293
    287294def dir_with_other_class(self, cls):
    288295    """
     
    500507           self.element_class = dynamic_class("%s.element_class"%self.__class__.__name__, (category.element_class,), self.Element)
    501508         - This should lookup for Element classes in all super classes
    502509        """
    503         if hasattr(self, 'Element'):
     510        try: #if hasattr(self, 'Element'):
    504511            return self.__make_element_class__(self.Element, "%s.element_class"%self.__class__.__name__)
    505         else:
     512        except AttributeError: #else:
    506513            return NotImplemented
    507514
    508515
     
    536543            sage: k = GF(5); k._element_constructor # indirect doctest
    537544            <bound method FiniteField_prime_modn_with_category._element_constructor_ of Finite Field of size 5>
    538545        """
    539         if hasattr(self, '_element_constructor_'):
    540             assert callable(self._element_constructor_)
    541             self._element_constructor = self._element_constructor_
    542             self._element_init_pass_parent = guess_pass_parent(self, self._element_constructor)
     546        try: #if hasattr(self, '_element_constructor_'):
     547            _element_constructor_ = self._element_constructor_
     548        except (AttributeError, TypeError):
     549            # Remark: A TypeError can actually occur;
     550            # it is a possible reason for "hasattr" to return False
     551            return
     552        assert callable(_element_constructor_)
     553        self._element_constructor = _element_constructor_
     554        self._element_init_pass_parent = guess_pass_parent(self, self._element_constructor)
    543555
    544556    def category(self):
    545557        """
     
    664676        ``self`` being an instance of both ``Parent`` and
    665677        ``cat.parent_class``, in that order, for attribute lookup.
    666678
     679        NOTE:
     680
     681        Attributes beginning with two underscores but not ending with
     682        an unnderscore are considered private and are thus exempted
     683        from the lookup in ``cat.parent_class``.
     684
    667685        EXAMPLES:
    668686
    669687        We test that ZZ (an extension type) inherits the methods from
     
    698716            Traceback (most recent call last):
    699717            ...
    700718            AttributeError: 'PrimeNumbers_with_category' object has no attribute 'sadfasdf'
     719
     720        TESTS::
     721
     722        We test that "private" attributes are not requested from the element class
     723        of the category (trac ticket #10467)::
     724
     725            sage: P = Parent(QQ, category=CommutativeRings())
     726            sage: P.category().parent_class.__foo = 'bar'
     727            sage: P.__foo
     728            Traceback (most recent call last):
     729            ...
     730            AttributeError: 'sage.structure.parent.Parent' object has no attribute '__foo'
     731
    701732        """
     733        if name.startswith('__') and not name.endswith('_'):
     734            raise_attribute_error(self, name)
    702735        if self._is_category_initialized():
    703736            return getattr_from_other_class(self, self.category().parent_class, name)
    704737        else:
  • sage/structure/sage_object.pyx

    diff -r 3a96f07d466b -r 159f6838b49c sage/structure/sage_object.pyx
    a b  
    7979           ``cdef public __custom_name`` attribute.
    8080        """
    8181        if x is None:
    82             if hasattr(self, '__custom_name'):
    83                 self.reset_name()
     82            #if hasattr(self, '__custom_name'):
     83            # that's tested in reset_name anyway...
     84            self.reset_name()
    8485        else:
    8586            try:
    8687                self.__custom_name = str(x)
     
    8889                raise NotImplementedError, "object does not support renaming: %s"%self
    8990
    9091    def reset_name(self):
     92        """
     93        Remove the custrom name of an object.
     94
     95        EXAMPLES::
     96
     97            sage: P.<x> = QQ[]
     98            sage: P
     99            Univariate Polynomial Ring in x over Rational Field
     100            sage: P.rename('A polynomial ring')
     101            sage: P
     102            A polynomial ring
     103            sage: P.reset_name()
     104            sage: P
     105            Univariate Polynomial Ring in x over Rational Field
     106        """
    91107        if hasattr(self, '__custom_name'):
    92108            del self.__custom_name
    93109       
    94110
    95111    def __repr__(self):
    96         if hasattr(self, '__custom_name'):
     112        """
     113        Default method for string representation.
     114
     115        NOTE:
     116
     117        Do not overwrite this method. Instead, implement
     118        a ``_repr_`` (single underscore) method.
     119
     120        EXAMPLES:
     121
     122        By default, the string representation coincides with
     123        the output of the single underscore ``_repr_``::
     124
     125            sage: P.<x> = QQ[]
     126            sage: repr(P) == P._repr_()  #indirect doctest
     127            True
     128
     129        Using :meth:`rename`, the string representation can
     130        be customized::
     131
     132            sage: P.rename('A polynomial ring')
     133            sage: repr(P) == P._repr_()
     134            False
     135
     136        The original behaviour is restored with :meth:`reset_name`.
     137
     138            sage: P.reset_name()
     139            sage: repr(P) == P._repr_()
     140            True
     141        """
     142        try:
    97143            name = self.__custom_name
    98144            if name is not None:
    99145                return name
    100         if hasattr(self, '_repr_'):
     146        except:
     147            pass
     148        try:
    101149            return self._repr_()
    102         return str(type(self))
     150        except AttributeError:
     151            return str(type(self))
    103152
    104153    def __hash__(self):
    105154        return hash(self.__repr__())
     
    375424            except (KeyError, ValueError):
    376425                pass
    377426        nm = I.name()
    378         if hasattr(self, '_%s_init_'%nm):
     427        try:
    379428            s = self.__getattribute__('_%s_init_'%nm)()
    380         else:
     429        except AttributeError:
    381430            try:
    382431              s = self._interface_init_(I)
    383432            except: