Ticket #10467: 10467attribute_lookup.patch

File 10467attribute_lookup.patch, 11.0 KB (added by SimonKing, 2 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: