1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from cpython.dict cimport (
    PyDict_Contains,
    PyDict_GetItem,
    PyDict_SetItem,
)
from cython cimport Py_ssize_t
 
 
cdef class CachedProperty:
 
    cdef readonly:
        object fget, name, __doc__
 
    def __init__(self, fget):
        self.fget = fget
        self.name = fget.__name__
        self.__doc__ = getattr(fget, "__doc__", None)
 
    def __get__(self, obj, typ):
        if obj is None:
            # accessed on the class, not the instance
            return self
 
        # Get the cache or set a default one if needed
        cache = getattr(obj, "_cache", None)
        if cache is None:
            try:
                cache = obj._cache = {}
            except (AttributeError):
                return self
 
        if PyDict_Contains(cache, self.name):
            # not necessary to Py_INCREF
            val = <object>PyDict_GetItem(cache, self.name)
        else:
            val = self.fget(obj)
            PyDict_SetItem(cache, self.name, val)
        return val
 
    def __set__(self, obj, value):
        raise AttributeError("Can't set attribute")
 
 
cache_readonly = CachedProperty
 
 
cdef class AxisProperty:
 
    cdef readonly:
        Py_ssize_t axis
        object __doc__
 
    def __init__(self, axis=0, doc=""):
        self.axis = axis
        self.__doc__ = doc
 
    def __get__(self, obj, type):
        cdef:
            list axes
 
        if obj is None:
            # Only instances have _mgr, not classes
            return self
        else:
            axes = obj._mgr.axes
        return axes[self.axis]
 
    def __set__(self, obj, value):
        obj._set_axis(self.axis, value)