# 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: |