diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 8faf491f8c8..26cbfc76da3 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -267,9 +267,11 @@ class of the category, and store the current class of the quotient sage: [s for s in dir(Q.category().element_class) if not s.startswith('_')] ['cartesian_product', 'gcd', 'is_idempotent', 'is_one', 'is_unit', 'lcm', 'lift', 'xgcd'] - As one can see, the elements are now inheriting additional methods: lcm and gcd. Even though - ``Q.an_element()`` belongs to the old and not to the new element class, it still inherits - the new methods from the category of fields:: + As one can see, the elements are now inheriting additional + methods: lcm and gcd. Even though ``Q.an_element()`` belongs to + the old and not to the new element class, it still inherits the + new methods from the category of fields, thanks to + :meth:`Element.__getattr__`:: sage: isinstance(Q.an_element(),Q.element_class) False @@ -287,7 +289,6 @@ class of the category, and store the current class of the quotient sage: TestSuite(Q(x)).run() sage: isinstance(Q(x), Q.element_class) True - """ Element = PolynomialQuotientRingElement diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 2670fcb4161..57521f26433 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -277,16 +277,40 @@ cdef class Element(sage_object.SageObject): def __getattr__(self, str name): """ - Let cat be the category of the parent of ``self``. This - method emulates ``self`` being an instance of both ``Element`` - and ``cat.element_class``, in that order, for attribute - lookup. + Lookup a method or attribute from the category abstract classes. - NOTE: + Let ``P`` be a parent in a category ``C``. Usually the methods + of ``C.element_class`` are made directly available to elements + of ``P`` via standard class inheritance. This is not the case + any more if the elements of ``P`` are instances of an + extension type. See :class:`Category`. for details. + + The purpose of this method is to emulate this inheritance: for + ``e`` and element of ``P``, if an attribute or method + ``e.foo`` is not found in the super classes of ``e``, it's + looked up manually in ``C.element_class`` and bound to ``e``. + + .. NOTES:: + + - Attributes beginning with two underscores but not ending + with an unnderscore are considered private and are thus + exempted from the lookup in ``cat.element_class``. - Attributes beginning with two underscores but not ending with - an unnderscore are considered private and are thus exempted - from the lookup in ``cat.element_class``. + - The attribute or method is actually looked up in + ``P._abstract_element_class``. In most cases this is + just an alias for ``C.element_class``, but some parents, + notably homsets, customizes this to let elements also + inherit from other abstract classes. See + :meth:`Parent._abstract_element_class` and + :meth:`Homset._abstract_element_class` for details. + + - This mechanism may also enter into action when the + category of `P` is refined on the fly, leaving + previously constructed elements in an outdated element + class. + + See :class:`~sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic` + for an example. EXAMPLES: @@ -330,7 +354,6 @@ cdef class Element(sage_object.SageObject): Traceback (most recent call last): ... AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute '__foo' - """ if (name.startswith('__') and not name.endswith('_')): dummy_error_message.cls = type(self)