passagemath-objects 10.6.45__cp313-cp313-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-objects might be problematic. Click here for more details.
- passagemath_objects/__init__.py +3 -0
- passagemath_objects-10.6.45.dist-info/METADATA +115 -0
- passagemath_objects-10.6.45.dist-info/RECORD +280 -0
- passagemath_objects-10.6.45.dist-info/WHEEL +5 -0
- passagemath_objects-10.6.45.dist-info/top_level.txt +3 -0
- passagemath_objects.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- sage/all__sagemath_objects.py +37 -0
- sage/arith/all__sagemath_objects.py +5 -0
- sage/arith/long.pxd +411 -0
- sage/arith/numerical_approx.cpython-313-x86_64-linux-musl.so +0 -0
- sage/arith/numerical_approx.pxd +35 -0
- sage/arith/numerical_approx.pyx +75 -0
- sage/arith/power.cpython-313-x86_64-linux-musl.so +0 -0
- sage/arith/power.pxd +31 -0
- sage/arith/power.pyx +127 -0
- sage/categories/action.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/action.pxd +29 -0
- sage/categories/action.pyx +641 -0
- sage/categories/algebra_functor.py +745 -0
- sage/categories/all__sagemath_objects.py +33 -0
- sage/categories/basic.py +62 -0
- sage/categories/cartesian_product.py +295 -0
- sage/categories/category.py +3401 -0
- sage/categories/category_cy_helper.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/category_cy_helper.pxd +8 -0
- sage/categories/category_cy_helper.pyx +322 -0
- sage/categories/category_singleton.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/category_singleton.pxd +3 -0
- sage/categories/category_singleton.pyx +342 -0
- sage/categories/category_types.py +637 -0
- sage/categories/category_with_axiom.py +2876 -0
- sage/categories/covariant_functorial_construction.py +703 -0
- sage/categories/facade_sets.py +228 -0
- sage/categories/functor.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/functor.pxd +7 -0
- sage/categories/functor.pyx +691 -0
- sage/categories/homset.py +1338 -0
- sage/categories/homsets.py +364 -0
- sage/categories/isomorphic_objects.py +73 -0
- sage/categories/map.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/map.pxd +34 -0
- sage/categories/map.pyx +2106 -0
- sage/categories/morphism.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/morphism.pxd +14 -0
- sage/categories/morphism.pyx +895 -0
- sage/categories/objects.py +167 -0
- sage/categories/primer.py +1696 -0
- sage/categories/pushout.py +4834 -0
- sage/categories/quotients.py +64 -0
- sage/categories/realizations.py +200 -0
- sage/categories/sets_cat.py +3290 -0
- sage/categories/sets_with_partial_maps.py +52 -0
- sage/categories/subobjects.py +64 -0
- sage/categories/subquotients.py +21 -0
- sage/categories/with_realizations.py +311 -0
- sage/cpython/__init__.py +19 -0
- sage/cpython/_py2_random.py +619 -0
- sage/cpython/all.py +3 -0
- sage/cpython/atexit.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/atexit.pyx +269 -0
- sage/cpython/builtin_types.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/builtin_types.pyx +7 -0
- sage/cpython/cython_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/cython_metaclass.h +117 -0
- sage/cpython/cython_metaclass.pxd +3 -0
- sage/cpython/cython_metaclass.pyx +130 -0
- sage/cpython/debug.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/debug.pyx +302 -0
- sage/cpython/dict_del_by_value.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/dict_del_by_value.pxd +9 -0
- sage/cpython/dict_del_by_value.pyx +191 -0
- sage/cpython/dict_internal.h +245 -0
- sage/cpython/getattr.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/getattr.pxd +9 -0
- sage/cpython/getattr.pyx +439 -0
- sage/cpython/pycore_long.h +97 -0
- sage/cpython/pycore_long.pxd +10 -0
- sage/cpython/python_debug.h +44 -0
- sage/cpython/python_debug.pxd +47 -0
- sage/cpython/pyx_visit.h +13 -0
- sage/cpython/string.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/string.pxd +76 -0
- sage/cpython/string.pyx +34 -0
- sage/cpython/string_impl.h +60 -0
- sage/cpython/type.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/type.pxd +2 -0
- sage/cpython/type.pyx +40 -0
- sage/cpython/wrapperdescr.pxd +67 -0
- sage/ext/all__sagemath_objects.py +3 -0
- sage/ext/ccobject.h +64 -0
- sage/ext/cplusplus.pxd +17 -0
- sage/ext/mod_int.h +30 -0
- sage/ext/mod_int.pxd +24 -0
- sage/ext/stdsage.pxd +39 -0
- sage/groups/all__sagemath_objects.py +1 -0
- sage/groups/group.cpython-313-x86_64-linux-musl.so +0 -0
- sage/groups/group.pxd +14 -0
- sage/groups/group.pyx +322 -0
- sage/groups/old.cpython-313-x86_64-linux-musl.so +0 -0
- sage/groups/old.pxd +14 -0
- sage/groups/old.pyx +219 -0
- sage/libs/all__sagemath_objects.py +3 -0
- sage/libs/gmp/__init__.py +1 -0
- sage/libs/gmp/all.pxd +6 -0
- sage/libs/gmp/binop.pxd +23 -0
- sage/libs/gmp/misc.pxd +8 -0
- sage/libs/gmp/mpf.pxd +88 -0
- sage/libs/gmp/mpn.pxd +57 -0
- sage/libs/gmp/mpq.pxd +57 -0
- sage/libs/gmp/mpz.pxd +202 -0
- sage/libs/gmp/pylong.cpython-313-x86_64-linux-musl.so +0 -0
- sage/libs/gmp/pylong.pxd +12 -0
- sage/libs/gmp/pylong.pyx +150 -0
- sage/libs/gmp/random.pxd +25 -0
- sage/libs/gmp/randomize.pxd +59 -0
- sage/libs/gmp/types.pxd +53 -0
- sage/libs/gmpxx.pxd +19 -0
- sage/misc/abstract_method.py +276 -0
- sage/misc/all__sagemath_objects.py +43 -0
- sage/misc/bindable_class.py +253 -0
- sage/misc/c3_controlled.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/c3_controlled.pxd +2 -0
- sage/misc/c3_controlled.pyx +1402 -0
- sage/misc/cachefunc.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/cachefunc.pxd +43 -0
- sage/misc/cachefunc.pyx +3781 -0
- sage/misc/call.py +188 -0
- sage/misc/classcall_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/classcall_metaclass.pxd +14 -0
- sage/misc/classcall_metaclass.pyx +599 -0
- sage/misc/constant_function.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/constant_function.pyx +130 -0
- sage/misc/decorators.py +747 -0
- sage/misc/fast_methods.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/fast_methods.pxd +20 -0
- sage/misc/fast_methods.pyx +351 -0
- sage/misc/flatten.py +90 -0
- sage/misc/fpickle.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/fpickle.pyx +177 -0
- sage/misc/function_mangling.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/function_mangling.pxd +11 -0
- sage/misc/function_mangling.pyx +308 -0
- sage/misc/inherit_comparison.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/inherit_comparison.pxd +5 -0
- sage/misc/inherit_comparison.pyx +105 -0
- sage/misc/instancedoc.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/instancedoc.pyx +331 -0
- sage/misc/lazy_attribute.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_attribute.pyx +607 -0
- sage/misc/lazy_format.py +135 -0
- sage/misc/lazy_import.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_import.pyx +1299 -0
- sage/misc/lazy_import_cache.py +36 -0
- sage/misc/lazy_list.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_list.pxd +19 -0
- sage/misc/lazy_list.pyx +1187 -0
- sage/misc/lazy_string.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_string.pxd +7 -0
- sage/misc/lazy_string.pyx +546 -0
- sage/misc/misc.py +1066 -0
- sage/misc/misc_c.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/misc_c.pxd +3 -0
- sage/misc/misc_c.pyx +766 -0
- sage/misc/namespace_package.py +37 -0
- sage/misc/nested_class.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/nested_class.pxd +3 -0
- sage/misc/nested_class.pyx +394 -0
- sage/misc/persist.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/persist.pyx +1251 -0
- sage/misc/prandom.py +418 -0
- sage/misc/randstate.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/randstate.pxd +30 -0
- sage/misc/randstate.pyx +1059 -0
- sage/misc/repr.py +203 -0
- sage/misc/reset.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/reset.pyx +196 -0
- sage/misc/sage_ostools.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/sage_ostools.pyx +323 -0
- sage/misc/sage_timeit.py +275 -0
- sage/misc/sage_timeit_class.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/sage_timeit_class.pyx +120 -0
- sage/misc/sage_unittest.py +637 -0
- sage/misc/sageinspect.py +2768 -0
- sage/misc/session.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/session.pyx +392 -0
- sage/misc/superseded.py +557 -0
- sage/misc/test_nested_class.py +228 -0
- sage/misc/timing.py +264 -0
- sage/misc/unknown.py +222 -0
- sage/misc/verbose.py +253 -0
- sage/misc/weak_dict.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/weak_dict.pxd +15 -0
- sage/misc/weak_dict.pyx +1231 -0
- sage/modules/all__sagemath_objects.py +1 -0
- sage/modules/module.cpython-313-x86_64-linux-musl.so +0 -0
- sage/modules/module.pxd +5 -0
- sage/modules/module.pyx +329 -0
- sage/rings/all__sagemath_objects.py +3 -0
- sage/rings/integer_fake.h +22 -0
- sage/rings/integer_fake.pxd +55 -0
- sage/sets/all__sagemath_objects.py +3 -0
- sage/sets/pythonclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/sets/pythonclass.pxd +9 -0
- sage/sets/pythonclass.pyx +247 -0
- sage/structure/__init__.py +4 -0
- sage/structure/all.py +30 -0
- sage/structure/category_object.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/category_object.pxd +28 -0
- sage/structure/category_object.pyx +1087 -0
- sage/structure/coerce.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce.pxd +44 -0
- sage/structure/coerce.pyx +2107 -0
- sage/structure/coerce_actions.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_actions.pxd +27 -0
- sage/structure/coerce_actions.pyx +988 -0
- sage/structure/coerce_dict.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_dict.pxd +51 -0
- sage/structure/coerce_dict.pyx +1557 -0
- sage/structure/coerce_exceptions.py +23 -0
- sage/structure/coerce_maps.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_maps.pxd +28 -0
- sage/structure/coerce_maps.pyx +718 -0
- sage/structure/debug_options.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/debug_options.pxd +6 -0
- sage/structure/debug_options.pyx +54 -0
- sage/structure/dynamic_class.py +541 -0
- sage/structure/element.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/element.pxd +272 -0
- sage/structure/element.pyx +4772 -0
- sage/structure/element_wrapper.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/element_wrapper.pxd +12 -0
- sage/structure/element_wrapper.pyx +582 -0
- sage/structure/factorization.py +1422 -0
- sage/structure/factorization_integer.py +105 -0
- sage/structure/factory.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/factory.pyx +786 -0
- sage/structure/formal_sum.py +489 -0
- sage/structure/gens_py.py +73 -0
- sage/structure/global_options.py +1743 -0
- sage/structure/indexed_generators.py +863 -0
- sage/structure/list_clone.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone.pxd +65 -0
- sage/structure/list_clone.pyx +1867 -0
- sage/structure/list_clone_demo.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone_demo.pyx +248 -0
- sage/structure/list_clone_timings.py +179 -0
- sage/structure/list_clone_timings_cy.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone_timings_cy.pyx +86 -0
- sage/structure/mutability.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/mutability.pxd +21 -0
- sage/structure/mutability.pyx +348 -0
- sage/structure/nonexact.py +69 -0
- sage/structure/parent.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent.pxd +112 -0
- sage/structure/parent.pyx +3093 -0
- sage/structure/parent_base.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_base.pxd +13 -0
- sage/structure/parent_base.pyx +44 -0
- sage/structure/parent_gens.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_gens.pxd +22 -0
- sage/structure/parent_gens.pyx +377 -0
- sage/structure/parent_old.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_old.pxd +25 -0
- sage/structure/parent_old.pyx +294 -0
- sage/structure/proof/__init__.py +1 -0
- sage/structure/proof/all.py +243 -0
- sage/structure/proof/proof.py +300 -0
- sage/structure/richcmp.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/richcmp.pxd +213 -0
- sage/structure/richcmp.pyx +495 -0
- sage/structure/sage_object.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/sage_object.pxd +3 -0
- sage/structure/sage_object.pyx +988 -0
- sage/structure/sage_object_test.py +19 -0
- sage/structure/sequence.py +937 -0
- sage/structure/set_factories.py +1178 -0
- sage/structure/set_factories_example.py +527 -0
- sage/structure/support_view.py +179 -0
- sage/structure/test_factory.py +56 -0
- sage/structure/unique_representation.py +1359 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
"""
|
|
3
|
+
Lazy attributes
|
|
4
|
+
|
|
5
|
+
AUTHORS:
|
|
6
|
+
|
|
7
|
+
- Nicolas Thiery (2008): Initial version
|
|
8
|
+
- Nils Bruin (2013-05): Cython version
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# ****************************************************************************
|
|
12
|
+
# Copyright (C) 2008 Nicolas M. Thiery <nthiery at users.sf.net>
|
|
13
|
+
#
|
|
14
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
15
|
+
#
|
|
16
|
+
# This code is distributed in the hope that it will be useful,
|
|
17
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
19
|
+
# General Public License for more details.
|
|
20
|
+
#
|
|
21
|
+
# The full text of the GPL is available at:
|
|
22
|
+
#
|
|
23
|
+
# https://www.gnu.org/licenses/
|
|
24
|
+
# ****************************************************************************
|
|
25
|
+
|
|
26
|
+
cdef class _lazy_attribute():
|
|
27
|
+
"""
|
|
28
|
+
Cython base class for lazy attributes.
|
|
29
|
+
|
|
30
|
+
EXAMPLES:
|
|
31
|
+
|
|
32
|
+
Only Python subclasses of this class are supposed to be instantiated::
|
|
33
|
+
|
|
34
|
+
sage: from sage.misc.lazy_attribute import _lazy_attribute
|
|
35
|
+
sage: _lazy_attribute(lambda x:1)
|
|
36
|
+
Traceback (most recent call last):
|
|
37
|
+
...
|
|
38
|
+
NotImplementedError: Only instantiate wrapper python class
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
cdef public f
|
|
42
|
+
cdef public str __name__
|
|
43
|
+
|
|
44
|
+
def __init__(self, f):
|
|
45
|
+
r"""
|
|
46
|
+
Constructor for lazy attributes.
|
|
47
|
+
|
|
48
|
+
EXAMPLES::
|
|
49
|
+
|
|
50
|
+
sage: def f(x):
|
|
51
|
+
....: "doc of f"
|
|
52
|
+
....: return 1
|
|
53
|
+
....:
|
|
54
|
+
sage: x = lazy_attribute(f); x
|
|
55
|
+
<sage.misc.lazy_attribute.lazy_attribute object at ...>
|
|
56
|
+
sage: x.__doc__
|
|
57
|
+
'doc of f'
|
|
58
|
+
sage: x.__name__
|
|
59
|
+
'f'
|
|
60
|
+
sage: x.__module__
|
|
61
|
+
'__main__'
|
|
62
|
+
|
|
63
|
+
TESTS:
|
|
64
|
+
|
|
65
|
+
We check that :issue:`9251` is solved::
|
|
66
|
+
|
|
67
|
+
sage: Parent.element_class
|
|
68
|
+
<sage.misc.lazy_attribute.lazy_attribute object at 0x...>
|
|
69
|
+
sage: "The (default) class for the elements of this parent" in Parent.element_class.__doc__
|
|
70
|
+
True
|
|
71
|
+
sage: Parent.element_class.__name__
|
|
72
|
+
'element_class'
|
|
73
|
+
sage: Parent.element_class.__module__
|
|
74
|
+
'sage.misc.lazy_attribute'
|
|
75
|
+
"""
|
|
76
|
+
raise NotImplementedError("Only instantiate wrapper python class")
|
|
77
|
+
|
|
78
|
+
def _sage_src_lines_(self):
|
|
79
|
+
r"""
|
|
80
|
+
Return the source code location for the wrapped function.
|
|
81
|
+
|
|
82
|
+
EXAMPLES::
|
|
83
|
+
|
|
84
|
+
sage: from sage.misc.sageinspect import sage_getsourcelines
|
|
85
|
+
sage: g = lazy_attribute(sage.misc.banner.banner)
|
|
86
|
+
sage: (src, lines) = sage_getsourcelines(g)
|
|
87
|
+
sage: src[0]
|
|
88
|
+
'def banner():\n'
|
|
89
|
+
sage: lines
|
|
90
|
+
95
|
|
91
|
+
"""
|
|
92
|
+
from sage.misc.sageinspect import sage_getsourcelines
|
|
93
|
+
return sage_getsourcelines(self.f)
|
|
94
|
+
|
|
95
|
+
def __get__(self, a, cls):
|
|
96
|
+
"""
|
|
97
|
+
Implement the attribute access protocol.
|
|
98
|
+
|
|
99
|
+
EXAMPLES::
|
|
100
|
+
|
|
101
|
+
sage: class A: pass
|
|
102
|
+
sage: def f(x): return 1
|
|
103
|
+
...
|
|
104
|
+
sage: f = lazy_attribute(f)
|
|
105
|
+
sage: f.__get__(A(), A)
|
|
106
|
+
1
|
|
107
|
+
"""
|
|
108
|
+
cdef CM
|
|
109
|
+
cdef result
|
|
110
|
+
if a is None: # when doing cls.x for cls a class and x a lazy attribute
|
|
111
|
+
return self
|
|
112
|
+
try:
|
|
113
|
+
# _cached_methods is supposed to be a public Cython attribute.
|
|
114
|
+
# Apparently, these are *not* subject to name mangling.
|
|
115
|
+
CM = getattr(a, '_cached_methods')
|
|
116
|
+
if CM is None:
|
|
117
|
+
CM = {}
|
|
118
|
+
setattr(a, '_cached_methods', CM)
|
|
119
|
+
except AttributeError as msg:
|
|
120
|
+
CM = None
|
|
121
|
+
if CM is not None:
|
|
122
|
+
try:
|
|
123
|
+
return CM[self.__name__]
|
|
124
|
+
except KeyError:
|
|
125
|
+
pass
|
|
126
|
+
result = self.f(a)
|
|
127
|
+
if result is NotImplemented:
|
|
128
|
+
# Workaround: we make sure that cls is the class
|
|
129
|
+
# where the lazy attribute self is actually defined.
|
|
130
|
+
# This avoids running into an infinite loop
|
|
131
|
+
# See About descriptor specifications
|
|
132
|
+
for supercls in cls.__mro__:
|
|
133
|
+
if self.__name__ in supercls.__dict__ and self is supercls.__dict__[self.__name__]:
|
|
134
|
+
cls = supercls
|
|
135
|
+
return getattr(super(cls, a), self.__name__)
|
|
136
|
+
try:
|
|
137
|
+
setattr(a, self.__name__, result)
|
|
138
|
+
except AttributeError:
|
|
139
|
+
if CM is not None:
|
|
140
|
+
CM[self.__name__] = result
|
|
141
|
+
return result
|
|
142
|
+
raise
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class lazy_attribute(_lazy_attribute):
|
|
147
|
+
r"""
|
|
148
|
+
A lazy attribute for an object is like a usual attribute, except
|
|
149
|
+
that, instead of being computed when the object is constructed
|
|
150
|
+
(i.e. in ``__init__``), it is computed on the fly the first time it
|
|
151
|
+
is accessed.
|
|
152
|
+
|
|
153
|
+
For constant values attached to an object, lazy attributes provide
|
|
154
|
+
a shorter syntax and automatic caching (unlike methods), while
|
|
155
|
+
playing well with inheritance (like methods): a subclass can
|
|
156
|
+
easily override a given attribute; you don't need to call the
|
|
157
|
+
super class constructor, etc.
|
|
158
|
+
|
|
159
|
+
Technically, a :class:`lazy_attribute` is a non-data descriptor (see
|
|
160
|
+
Invoking Descriptors in the Python reference manual).
|
|
161
|
+
|
|
162
|
+
EXAMPLES:
|
|
163
|
+
|
|
164
|
+
We create a class whose instances have a lazy attribute ``x``::
|
|
165
|
+
|
|
166
|
+
sage: class A():
|
|
167
|
+
....: def __init__(self):
|
|
168
|
+
....: self.a=2 # just to have some data to calculate from
|
|
169
|
+
....:
|
|
170
|
+
....: @lazy_attribute
|
|
171
|
+
....: def x(self):
|
|
172
|
+
....: print("calculating x in A")
|
|
173
|
+
....: return self.a + 1
|
|
174
|
+
....:
|
|
175
|
+
|
|
176
|
+
For an instance ``a`` of ``A``, ``a.x`` is calculated the first time it
|
|
177
|
+
is accessed, and then stored as a usual attribute::
|
|
178
|
+
|
|
179
|
+
sage: a = A()
|
|
180
|
+
sage: a.x
|
|
181
|
+
calculating x in A
|
|
182
|
+
3
|
|
183
|
+
sage: a.x
|
|
184
|
+
3
|
|
185
|
+
|
|
186
|
+
.. rubric:: Implementation details
|
|
187
|
+
|
|
188
|
+
We redo the same example, but opening the hood to see what happens to
|
|
189
|
+
the internal dictionary of the object::
|
|
190
|
+
|
|
191
|
+
sage: a = A()
|
|
192
|
+
sage: a.__dict__
|
|
193
|
+
{'a': 2}
|
|
194
|
+
sage: a.x
|
|
195
|
+
calculating x in A
|
|
196
|
+
3
|
|
197
|
+
sage: a.__dict__
|
|
198
|
+
{'a': 2, 'x': 3}
|
|
199
|
+
sage: a.x
|
|
200
|
+
3
|
|
201
|
+
sage: timeit('a.x') # random
|
|
202
|
+
625 loops, best of 3: 89.6 ns per loop
|
|
203
|
+
|
|
204
|
+
This shows that, after the first calculation, the attribute ``x``
|
|
205
|
+
becomes a usual attribute; in particular, there is no time penalty
|
|
206
|
+
to access it.
|
|
207
|
+
|
|
208
|
+
A lazy attribute may be set as usual, even before its first access,
|
|
209
|
+
in which case the lazy calculation is completely ignored::
|
|
210
|
+
|
|
211
|
+
sage: a = A()
|
|
212
|
+
sage: a.x = 4
|
|
213
|
+
sage: a.x
|
|
214
|
+
4
|
|
215
|
+
sage: a.__dict__
|
|
216
|
+
{'a': 2, 'x': 4}
|
|
217
|
+
|
|
218
|
+
Class binding results in the lazy attribute itself::
|
|
219
|
+
|
|
220
|
+
sage: A.x
|
|
221
|
+
<sage.misc.lazy_attribute.lazy_attribute object at ...>
|
|
222
|
+
|
|
223
|
+
.. rubric:: Conditional definitions
|
|
224
|
+
|
|
225
|
+
The function calculating the attribute may return NotImplemented
|
|
226
|
+
to declare that, after all, it is not able to do it. In that case,
|
|
227
|
+
the attribute lookup proceeds in the super class hierarchy::
|
|
228
|
+
|
|
229
|
+
sage: class B(A):
|
|
230
|
+
....: @lazy_attribute
|
|
231
|
+
....: def x(self):
|
|
232
|
+
....: if hasattr(self, "y"):
|
|
233
|
+
....: print("calculating x from y in B")
|
|
234
|
+
....: return self.y
|
|
235
|
+
....: else:
|
|
236
|
+
....: print("y not there; B does not define x")
|
|
237
|
+
....: return NotImplemented
|
|
238
|
+
....:
|
|
239
|
+
sage: b = B()
|
|
240
|
+
sage: b.x
|
|
241
|
+
y not there; B does not define x
|
|
242
|
+
calculating x in A
|
|
243
|
+
3
|
|
244
|
+
sage: b = B()
|
|
245
|
+
sage: b.y = 1
|
|
246
|
+
sage: b.x
|
|
247
|
+
calculating x from y in B
|
|
248
|
+
1
|
|
249
|
+
|
|
250
|
+
.. rubric:: Attribute existence testing
|
|
251
|
+
|
|
252
|
+
Testing for the existence of an attribute with hasattr currently
|
|
253
|
+
always triggers its full calculation, which may not be desirable
|
|
254
|
+
when the calculation is expensive::
|
|
255
|
+
|
|
256
|
+
sage: a = A()
|
|
257
|
+
sage: hasattr(a, "x")
|
|
258
|
+
calculating x in A
|
|
259
|
+
True
|
|
260
|
+
|
|
261
|
+
It would be great if we could take over the control somehow, if at
|
|
262
|
+
all possible without a special implementation of hasattr, so as to
|
|
263
|
+
allow for something like::
|
|
264
|
+
|
|
265
|
+
sage: class A ():
|
|
266
|
+
....: @lazy_attribute
|
|
267
|
+
....: def x(self, existence_only=False):
|
|
268
|
+
....: if existence_only:
|
|
269
|
+
....: print("testing for x existence")
|
|
270
|
+
....: return True
|
|
271
|
+
....: else:
|
|
272
|
+
....: print("calculating x in A")
|
|
273
|
+
....: return 3
|
|
274
|
+
....:
|
|
275
|
+
sage: a = A()
|
|
276
|
+
sage: hasattr(a, "x") # todo: not implemented
|
|
277
|
+
testing for x existence
|
|
278
|
+
sage: a.x
|
|
279
|
+
calculating x in A
|
|
280
|
+
3
|
|
281
|
+
sage: a.x
|
|
282
|
+
3
|
|
283
|
+
|
|
284
|
+
Here is a full featured example, with both conditional definition
|
|
285
|
+
and existence testing::
|
|
286
|
+
|
|
287
|
+
sage: class B(A):
|
|
288
|
+
....: @lazy_attribute
|
|
289
|
+
....: def x(self, existence_only=False):
|
|
290
|
+
....: if hasattr(self, "y"):
|
|
291
|
+
....: if existence_only:
|
|
292
|
+
....: print("testing for x existence in B")
|
|
293
|
+
....: return True
|
|
294
|
+
....: else:
|
|
295
|
+
....: print("calculating x from y in B")
|
|
296
|
+
....: return self.y
|
|
297
|
+
....: else:
|
|
298
|
+
....: print("y not there; B does not define x")
|
|
299
|
+
....: return NotImplemented
|
|
300
|
+
....:
|
|
301
|
+
sage: b = B()
|
|
302
|
+
sage: hasattr(b, "x") # todo: not implemented
|
|
303
|
+
y not there; B does not define x
|
|
304
|
+
testing for x existence
|
|
305
|
+
True
|
|
306
|
+
sage: b.x
|
|
307
|
+
y not there; B does not define x
|
|
308
|
+
calculating x in A
|
|
309
|
+
3
|
|
310
|
+
sage: b = B()
|
|
311
|
+
sage: b.y = 1
|
|
312
|
+
sage: hasattr(b, "x") # todo: not implemented
|
|
313
|
+
testing for x existence in B
|
|
314
|
+
True
|
|
315
|
+
sage: b.x
|
|
316
|
+
calculating x from y in B
|
|
317
|
+
1
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
.. rubric:: lazy attributes and introspection
|
|
321
|
+
|
|
322
|
+
.. TODO::
|
|
323
|
+
|
|
324
|
+
Make the following work nicely::
|
|
325
|
+
|
|
326
|
+
sage: b.x? # todo: not implemented
|
|
327
|
+
sage: b.x?? # todo: not implemented
|
|
328
|
+
|
|
329
|
+
Right now, the first one includes the doc of this class, and the
|
|
330
|
+
second one brings up the code of this class, both being not very
|
|
331
|
+
useful.
|
|
332
|
+
|
|
333
|
+
TESTS:
|
|
334
|
+
|
|
335
|
+
.. rubric:: Lazy attributes and Cython
|
|
336
|
+
|
|
337
|
+
This attempts to check that lazy attributes work with built-in
|
|
338
|
+
functions like cpdef methods::
|
|
339
|
+
|
|
340
|
+
sage: class A:
|
|
341
|
+
....: def __len__(x):
|
|
342
|
+
....: return int(5)
|
|
343
|
+
....: len = lazy_attribute(len)
|
|
344
|
+
....:
|
|
345
|
+
sage: A().len
|
|
346
|
+
5
|
|
347
|
+
|
|
348
|
+
Since :issue:`11115`, extension classes derived from
|
|
349
|
+
:class:`~sage.structure.parent.Parent` can inherit a lazy attribute,
|
|
350
|
+
such as ``element_class``::
|
|
351
|
+
|
|
352
|
+
sage: cython_code = ["from sage.structure.parent cimport Parent",
|
|
353
|
+
....: "from sage.structure.element cimport Element",
|
|
354
|
+
....: "cdef class MyElement(Element): pass",
|
|
355
|
+
....: "cdef class MyParent(Parent):",
|
|
356
|
+
....: " Element = MyElement"]
|
|
357
|
+
sage: cython('\n'.join(cython_code)) # needs sage.misc.cython
|
|
358
|
+
sage: P = MyParent(category=Rings()) # needs sage.misc.cython
|
|
359
|
+
sage: P.element_class # indirect doctest # needs sage.misc.cython
|
|
360
|
+
<class '...MyElement'>
|
|
361
|
+
|
|
362
|
+
.. rubric:: About descriptor specifications
|
|
363
|
+
|
|
364
|
+
The specifications of descriptors (see 3.4.2.3 Invoking
|
|
365
|
+
Descriptors in the Python reference manual) are incomplete
|
|
366
|
+
w.r.t. inheritance, and maybe even ill-implemented. We illustrate
|
|
367
|
+
this on a simple class hierarchy, with an instrumented descriptor::
|
|
368
|
+
|
|
369
|
+
sage: class descriptor():
|
|
370
|
+
....: def __get__(self, obj, cls):
|
|
371
|
+
....: print(cls)
|
|
372
|
+
....: return 1
|
|
373
|
+
sage: class A():
|
|
374
|
+
....: x = descriptor()
|
|
375
|
+
sage: class B(A):
|
|
376
|
+
....: pass
|
|
377
|
+
....:
|
|
378
|
+
|
|
379
|
+
This is fine::
|
|
380
|
+
|
|
381
|
+
sage: A.x
|
|
382
|
+
<class '__main__.A'>
|
|
383
|
+
1
|
|
384
|
+
|
|
385
|
+
The behaviour for the following case is not specified (see Instance Binding)
|
|
386
|
+
when ``x`` is not in the dictionary of ``B`` but in that of some super
|
|
387
|
+
category::
|
|
388
|
+
|
|
389
|
+
sage: B().x
|
|
390
|
+
<class '__main__.B'>
|
|
391
|
+
1
|
|
392
|
+
|
|
393
|
+
It would seem more natural (and practical!) to get ``A`` rather than ``B``.
|
|
394
|
+
|
|
395
|
+
From the specifications for Super Binding, it would be expected to
|
|
396
|
+
get ``A`` and not ``B`` as cls parameter::
|
|
397
|
+
|
|
398
|
+
sage: super(B, B()).x
|
|
399
|
+
<class '__main__.B'>
|
|
400
|
+
1
|
|
401
|
+
|
|
402
|
+
Due to this, the natural implementation runs into an infinite loop
|
|
403
|
+
in the following example::
|
|
404
|
+
|
|
405
|
+
sage: class A():
|
|
406
|
+
....: @lazy_attribute
|
|
407
|
+
....: def unimplemented_A(self):
|
|
408
|
+
....: return NotImplemented
|
|
409
|
+
....: @lazy_attribute
|
|
410
|
+
....: def unimplemented_AB(self):
|
|
411
|
+
....: return NotImplemented
|
|
412
|
+
....: @lazy_attribute
|
|
413
|
+
....: def unimplemented_B_implemented_A(self):
|
|
414
|
+
....: return 1
|
|
415
|
+
....:
|
|
416
|
+
sage: class B(A):
|
|
417
|
+
....: @lazy_attribute
|
|
418
|
+
....: def unimplemented_B(self):
|
|
419
|
+
....: return NotImplemented
|
|
420
|
+
....: @lazy_attribute
|
|
421
|
+
....: def unimplemented_AB(self):
|
|
422
|
+
....: return NotImplemented
|
|
423
|
+
....: @lazy_attribute
|
|
424
|
+
....: def unimplemented_B_implemented_A(self):
|
|
425
|
+
....: return NotImplemented
|
|
426
|
+
....:
|
|
427
|
+
sage: class C(B):
|
|
428
|
+
....: pass
|
|
429
|
+
....:
|
|
430
|
+
|
|
431
|
+
This is the simplest case where, without workaround, we get an
|
|
432
|
+
infinite loop::
|
|
433
|
+
|
|
434
|
+
sage: hasattr(B(), "unimplemented_A") # todo: not implemented
|
|
435
|
+
False
|
|
436
|
+
|
|
437
|
+
.. TODO::
|
|
438
|
+
|
|
439
|
+
Improve the error message::
|
|
440
|
+
|
|
441
|
+
sage: B().unimplemented_A # todo: not implemented
|
|
442
|
+
Traceback (most recent call last):
|
|
443
|
+
...
|
|
444
|
+
AttributeError: 'super' object has no attribute 'unimplemented_A'...
|
|
445
|
+
|
|
446
|
+
We now make some systematic checks::
|
|
447
|
+
|
|
448
|
+
sage: B().unimplemented_A
|
|
449
|
+
Traceback (most recent call last):
|
|
450
|
+
...
|
|
451
|
+
AttributeError: '...' object has no attribute 'unimplemented_A'...
|
|
452
|
+
sage: B().unimplemented_B
|
|
453
|
+
Traceback (most recent call last):
|
|
454
|
+
...
|
|
455
|
+
AttributeError: '...' object has no attribute 'unimplemented_B'...
|
|
456
|
+
sage: B().unimplemented_AB
|
|
457
|
+
Traceback (most recent call last):
|
|
458
|
+
...
|
|
459
|
+
AttributeError: '...' object has no attribute 'unimplemented_AB'...
|
|
460
|
+
sage: B().unimplemented_B_implemented_A
|
|
461
|
+
1
|
|
462
|
+
|
|
463
|
+
sage: C().unimplemented_A()
|
|
464
|
+
Traceback (most recent call last):
|
|
465
|
+
...
|
|
466
|
+
AttributeError: '...' object has no attribute 'unimplemented_A'...
|
|
467
|
+
sage: C().unimplemented_B()
|
|
468
|
+
Traceback (most recent call last):
|
|
469
|
+
...
|
|
470
|
+
AttributeError: '...' object has no attribute 'unimplemented_B'...
|
|
471
|
+
sage: C().unimplemented_AB()
|
|
472
|
+
Traceback (most recent call last):
|
|
473
|
+
...
|
|
474
|
+
AttributeError: '...' object has no attribute 'unimplemented_AB'...
|
|
475
|
+
sage: C().unimplemented_B_implemented_A # todo: not implemented
|
|
476
|
+
1
|
|
477
|
+
"""
|
|
478
|
+
def __init__(self, f):
|
|
479
|
+
"""
|
|
480
|
+
Initialize ``self``.
|
|
481
|
+
|
|
482
|
+
EXAMPLES::
|
|
483
|
+
|
|
484
|
+
sage: def f(x):
|
|
485
|
+
....: "doc of f"
|
|
486
|
+
....: return 1
|
|
487
|
+
....:
|
|
488
|
+
sage: x = lazy_attribute(f); x
|
|
489
|
+
<sage.misc.lazy_attribute.lazy_attribute object at ...>
|
|
490
|
+
sage: x.__doc__
|
|
491
|
+
'doc of f'
|
|
492
|
+
sage: x.__name__
|
|
493
|
+
'f'
|
|
494
|
+
sage: x.__module__
|
|
495
|
+
'__main__'
|
|
496
|
+
"""
|
|
497
|
+
self.f = f
|
|
498
|
+
if hasattr(f, "__doc__"):
|
|
499
|
+
self.__doc__ = f.__doc__
|
|
500
|
+
elif hasattr(f, "__doc__"): # Needed to handle Cython methods
|
|
501
|
+
self.__doc__ = f.__doc__
|
|
502
|
+
if hasattr(f, "__name__"):
|
|
503
|
+
self.__name__ = f.__name__
|
|
504
|
+
elif hasattr(f, "__name__"): # Needed to handle Cython methods
|
|
505
|
+
self.__name__ = f.__name__
|
|
506
|
+
if hasattr(f, "__module__"):
|
|
507
|
+
self.__module__ = f.__module__
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
class lazy_class_attribute(lazy_attribute):
|
|
511
|
+
"""
|
|
512
|
+
A lazy class attribute for a class is like a usual class attribute,
|
|
513
|
+
except that, instead of being computed when the class is constructed, it
|
|
514
|
+
is computed on the fly the first time it is accessed, either through the
|
|
515
|
+
class itself or through one of its objects.
|
|
516
|
+
|
|
517
|
+
This is very similar to :class:`lazy_attribute` except that the attribute
|
|
518
|
+
is a class attribute. More precisely, once computed, the lazy class
|
|
519
|
+
attribute is stored in the class rather than in the object. The lazy class
|
|
520
|
+
attribute is only computed once for all the objects::
|
|
521
|
+
|
|
522
|
+
sage: class Cl():
|
|
523
|
+
....: @lazy_class_attribute
|
|
524
|
+
....: def x(cls):
|
|
525
|
+
....: print("computing x")
|
|
526
|
+
....: return 1
|
|
527
|
+
sage: Cl.x
|
|
528
|
+
computing x
|
|
529
|
+
1
|
|
530
|
+
sage: Cl.x
|
|
531
|
+
1
|
|
532
|
+
|
|
533
|
+
As for a any usual class attribute it is also possible to access it from
|
|
534
|
+
an object::
|
|
535
|
+
|
|
536
|
+
sage: b = Cl()
|
|
537
|
+
sage: b.x
|
|
538
|
+
1
|
|
539
|
+
|
|
540
|
+
First access from an object also properly triggers the computation::
|
|
541
|
+
|
|
542
|
+
sage: class Cl1():
|
|
543
|
+
....: @lazy_class_attribute
|
|
544
|
+
....: def x(cls):
|
|
545
|
+
....: print("computing x")
|
|
546
|
+
....: return 1
|
|
547
|
+
sage: Cl1().x
|
|
548
|
+
computing x
|
|
549
|
+
1
|
|
550
|
+
sage: Cl1().x
|
|
551
|
+
1
|
|
552
|
+
|
|
553
|
+
.. WARNING::
|
|
554
|
+
|
|
555
|
+
The behavior of lazy class attributes with respect to inheritance is
|
|
556
|
+
not specified. It currently depends on the evaluation order::
|
|
557
|
+
|
|
558
|
+
sage: class A():
|
|
559
|
+
....: @lazy_class_attribute
|
|
560
|
+
....: def x(cls):
|
|
561
|
+
....: print("computing x")
|
|
562
|
+
....: return str(cls)
|
|
563
|
+
....: @lazy_class_attribute
|
|
564
|
+
....: def y(cls):
|
|
565
|
+
....: print("computing y")
|
|
566
|
+
....: return str(cls)
|
|
567
|
+
sage: class B(A):
|
|
568
|
+
....: pass
|
|
569
|
+
|
|
570
|
+
sage: A.x
|
|
571
|
+
computing x
|
|
572
|
+
"<class '__main__.A'>"
|
|
573
|
+
sage: B.x
|
|
574
|
+
"<class '__main__.A'>"
|
|
575
|
+
|
|
576
|
+
sage: B.y
|
|
577
|
+
computing y
|
|
578
|
+
"<class '__main__.B'>"
|
|
579
|
+
sage: A.y
|
|
580
|
+
computing y
|
|
581
|
+
"<class '__main__.A'>"
|
|
582
|
+
sage: B.y
|
|
583
|
+
"<class '__main__.B'>"
|
|
584
|
+
|
|
585
|
+
TESTS::
|
|
586
|
+
|
|
587
|
+
sage: "x" in b.__dict__
|
|
588
|
+
False
|
|
589
|
+
"""
|
|
590
|
+
def __get__(self, _, cls):
|
|
591
|
+
"""
|
|
592
|
+
Implement the attribute access protocol.
|
|
593
|
+
|
|
594
|
+
EXAMPLES::
|
|
595
|
+
|
|
596
|
+
sage: class A: pass
|
|
597
|
+
sage: def f(x): return 1
|
|
598
|
+
....:
|
|
599
|
+
sage: f = lazy_class_attribute(f)
|
|
600
|
+
sage: f.__get__(A(), A)
|
|
601
|
+
1
|
|
602
|
+
"""
|
|
603
|
+
result = self.f(cls)
|
|
604
|
+
if result is NotImplemented:
|
|
605
|
+
return getattr(super(cls, cls), self.__name__)
|
|
606
|
+
setattr(cls, self.__name__, result)
|
|
607
|
+
return result
|