# 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
|
|
| 186 | 186 | from types import MethodType |
| 187 | 187 | |
| 188 | 188 | from sage.categories.category import Category |
| 189 | | from sage.structure.parent cimport Parent |
| 190 | | from sage.structure.parent import is_extension_type |
| | 189 | from sage.structure.parent cimport Parent, raise_attribute_error |
| | 190 | from sage.structure.parent import is_extension_type, getattr_from_other_class |
| 191 | 191 | from sage.misc.lazy_format import LazyFormat |
| 192 | 192 | |
| 193 | 193 | # This classes uses element.pxd. To add data members, you |
| … |
… |
|
| 272 | 272 | and ``cat.element_class``, in that order, for attribute |
| 273 | 273 | lookup. |
| 274 | 274 | |
| | 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 | |
| 275 | 281 | EXAMPLES: |
| 276 | 282 | |
| 277 | 283 | We test that ``1`` (an instance of the extension type |
| … |
… |
|
| 301 | 307 | Traceback (most recent call last): |
| 302 | 308 | ... |
| 303 | 309 | 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 | |
| 304 | 324 | """ |
| 305 | | from sage.structure.parent import getattr_from_other_class |
| | 325 | if name.startswith('__') and not name.endswith('_'): |
| | 326 | raise_attribute_error(self, name) |
| 306 | 327 | return getattr_from_other_class(self, self.parent().category().element_class, name) |
| 307 | 328 | |
| 308 | 329 | def __dir__(self): |
diff -r 3a96f07d466b -r 159f6838b49c sage/structure/parent.pxd
|
a
|
b
|
|
| 9 | 9 | #cimport sage.categories.object |
| 10 | 10 | cimport sage.structure.category_object |
| 11 | 11 | |
| | 12 | cdef inline raise_attribute_error(self, name) |
| 12 | 13 | |
| 13 | 14 | cdef class Parent(category_object.CategoryObject): |
| 14 | 15 | |
diff -r 3a96f07d466b -r 159f6838b49c sage/structure/parent.pyx
|
a
|
b
|
|
| 87 | 87 | cdef inline parent_c(x): |
| 88 | 88 | if PY_TYPE_CHECK(x, element.Element): |
| 89 | 89 | 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) |
| 92 | 94 | else: |
| 93 | | return <object>PY_TYPE(x) |
| | 95 | try: |
| | 96 | return x.parent() |
| | 97 | except AttributeError: |
| | 98 | return <object>PY_TYPE(x) |
| 94 | 99 | |
| 95 | 100 | cdef _record_exception(): |
| 96 | 101 | from element import get_coercion_model |
| … |
… |
|
| 150 | 155 | pass |
| 151 | 156 | methodwrapper = type(A.__call__) |
| 152 | 157 | |
| 153 | | def raise_attribute_error(self, name): |
| | 158 | cdef inline raise_attribute_error(self, name): |
| 154 | 159 | """ |
| 155 | 160 | Tries to emulate the standard Python AttributeError exception |
| 156 | 161 | |
| 157 | 162 | EXAMPLES:: |
| 158 | 163 | |
| 159 | | sage: sage.structure.parent.raise_attribute_error(1, "bla") |
| | 164 | sage: 1.bla #indirect doctest |
| 160 | 165 | Traceback (most recent call last): |
| 161 | 166 | ... |
| 162 | 167 | 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 |
| 164 | 169 | Traceback (most recent call last): |
| 165 | 170 | ... |
| 166 | 171 | AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute 'bla' |
| … |
… |
|
| 272 | 277 | raise_attribute_error(self, name) |
| 273 | 278 | if isinstance(attribute, methodwrapper): |
| 274 | 279 | 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: |
| 285 | 283 | 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) |
| 286 | 293 | |
| 287 | 294 | def dir_with_other_class(self, cls): |
| 288 | 295 | """ |
| … |
… |
|
| 500 | 507 | self.element_class = dynamic_class("%s.element_class"%self.__class__.__name__, (category.element_class,), self.Element) |
| 501 | 508 | - This should lookup for Element classes in all super classes |
| 502 | 509 | """ |
| 503 | | if hasattr(self, 'Element'): |
| | 510 | try: #if hasattr(self, 'Element'): |
| 504 | 511 | return self.__make_element_class__(self.Element, "%s.element_class"%self.__class__.__name__) |
| 505 | | else: |
| | 512 | except AttributeError: #else: |
| 506 | 513 | return NotImplemented |
| 507 | 514 | |
| 508 | 515 | |
| … |
… |
|
| 536 | 543 | sage: k = GF(5); k._element_constructor # indirect doctest |
| 537 | 544 | <bound method FiniteField_prime_modn_with_category._element_constructor_ of Finite Field of size 5> |
| 538 | 545 | """ |
| 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) |
| 543 | 555 | |
| 544 | 556 | def category(self): |
| 545 | 557 | """ |
| … |
… |
|
| 664 | 676 | ``self`` being an instance of both ``Parent`` and |
| 665 | 677 | ``cat.parent_class``, in that order, for attribute lookup. |
| 666 | 678 | |
| | 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 | |
| 667 | 685 | EXAMPLES: |
| 668 | 686 | |
| 669 | 687 | We test that ZZ (an extension type) inherits the methods from |
| … |
… |
|
| 698 | 716 | Traceback (most recent call last): |
| 699 | 717 | ... |
| 700 | 718 | 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 | |
| 701 | 732 | """ |
| | 733 | if name.startswith('__') and not name.endswith('_'): |
| | 734 | raise_attribute_error(self, name) |
| 702 | 735 | if self._is_category_initialized(): |
| 703 | 736 | return getattr_from_other_class(self, self.category().parent_class, name) |
| 704 | 737 | else: |
diff -r 3a96f07d466b -r 159f6838b49c sage/structure/sage_object.pyx
|
a
|
b
|
|
| 79 | 79 | ``cdef public __custom_name`` attribute. |
| 80 | 80 | """ |
| 81 | 81 | 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() |
| 84 | 85 | else: |
| 85 | 86 | try: |
| 86 | 87 | self.__custom_name = str(x) |
| … |
… |
|
| 88 | 89 | raise NotImplementedError, "object does not support renaming: %s"%self |
| 89 | 90 | |
| 90 | 91 | 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 | """ |
| 91 | 107 | if hasattr(self, '__custom_name'): |
| 92 | 108 | del self.__custom_name |
| 93 | 109 | |
| 94 | 110 | |
| 95 | 111 | 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: |
| 97 | 143 | name = self.__custom_name |
| 98 | 144 | if name is not None: |
| 99 | 145 | return name |
| 100 | | if hasattr(self, '_repr_'): |
| | 146 | except: |
| | 147 | pass |
| | 148 | try: |
| 101 | 149 | return self._repr_() |
| 102 | | return str(type(self)) |
| | 150 | except AttributeError: |
| | 151 | return str(type(self)) |
| 103 | 152 | |
| 104 | 153 | def __hash__(self): |
| 105 | 154 | return hash(self.__repr__()) |
| … |
… |
|
| 375 | 424 | except (KeyError, ValueError): |
| 376 | 425 | pass |
| 377 | 426 | nm = I.name() |
| 378 | | if hasattr(self, '_%s_init_'%nm): |
| | 427 | try: |
| 379 | 428 | s = self.__getattribute__('_%s_init_'%nm)() |
| 380 | | else: |
| | 429 | except AttributeError: |
| 381 | 430 | try: |
| 382 | 431 | s = self._interface_init_(I) |
| 383 | 432 | except: |