Module mpython.core.delayed_types
Classes
class AnyDelayedArray (parent, *index)
-
Expand source code
class AnyDelayedArray(AnyMatlabArray): """ This is an object that we return when we don't know how an indexed element will be used yet. It decides whether it is a Struct, Cell or Array based on the type of indexing that is used. In Matlab: * `a(x,y) = num` indicates that `a` is a numeric array; * `a(x,y) = cell` indicates that `a` is a cell array; * `a{x,y} = any` indicates that `a` is a cell array; * `a(x,y).f = any` indicates that `a` is a struct array; * `a.f = any` indicates that `a` is a struct. These indexing operations can be chained, so in `a(x).b.c{y}.d(z) = 2`: * `a` is a struct array; * `b` is a struct; * `c` is a cell; * `c{y}` is a struct * `d` is a numeric array. In Python, there is only one type of indexing (`[]`). This is a problem as we cannot differentiate `a{x}.b = y` — where `a` is a cell that contains a struct — from `a(x).b = y` — where `a` is a struct array. One solution may be to abuse the "call" operator `()`, so that it returns a cell. This would work in some situations (`a[x].b = y` is a struct array, whereas `a(x).b = y` is a cell of struct). However, the statement `a(x) = y` (which would correspond to matlab's `a{x} = y`) is not valid python syntax. Furthermore, it would induce a new problem, as cells could not be differentiated from function handles, in some cases. Instead, the use of brackets automatically transforms the object into either: * a `Struct` (in all "get" cases, and in the "set" context `a[x] = y`, when `y` is either a `dict` or a `Struct`); or * an `Array` (in the "set" context `a[x] = y`, when `y` is neither a `dict` nor a `Struct`). Alternatively, if the user wishes to specify which type the object should take, we implement the properties `as_cell`, `as_struct` and `as_num`. Therefore: * `a[x,y] = num` : `a` is a numeric array; * `a[x,y] = struct` : `a` is a numeric array; * `a[x,y].f = any` : `a` is a struct array; * `a(x,y).f = any` : `a` is a cell array containing a struct; * `a.f = any` : `a` is a struct. And explictly: * `a.as_cell[x,y] = any` : `a` is a cell array; * `a.as_struct[x,y].f = any` : `a` is a struct array; * `a.as_cell[x,y].f = any` : `a` is a cell array containing a struct; * `a.as_num[x,y] = num` : `a` is a numeric array. """ _ATTRIBUTES = ("_parent", "_index", "_future", "_finalized") def __init__(self, parent, *index): """ Parameters ---------- parent : ndarray | dict Reference to the object that will eventually contain this element. * If the containing array is a `Cell`, `parent` should be a `ndarray` view of that cell, and `index` should be a [tuple of] int. * If the containing array is a `Struct`, `parent` should be a `dict`, and `index` should be a string. index : str | [tuple of] int Index into the parent where this element will be inserted. """ super().__init__() self._parent = parent # reference to parent container self._index = index # index into parent container self._future = None # future array self._finalized = False # whether this array has been finalized @property def _final(self): self._finalize() return self._future def _finalize(self): if self._finalized: return if self._future is None: # FIXME: I am not entirely sure this should ever happen self._future = _empty_array() # if future array is wrapped, unwrap it if isinstance(self._future, WrappedDelayedArray): self._future = self._future._future if hasattr(self._future, "_delayed_wrapper"): del self._future._delayed_wrapper # set value in parent parent = self._parent for index in self._index[:-1]: parent = parent[index] parent[self._index[-1]] = self._future # finalize parent if needed if hasattr(self._parent, "_final"): self._parent = self._parent._final self._finalized = True def _error_is_not_finalized(self, *args, **kwargs): raise IndexOrKeyOrAttributeError( "This DelayedArray has not been finalized, and you are " "attempting to use it in a way that may break its finalization " "cycle. It most likely means that you are indexing out-of-bounds " "without *setting* the out-of-bound value. " "Correct usage: `a.b(i).c = x` | Incorrect usage: `x = a.b(i).c`." ) # Kill all operators __str__ = __repr__ = _error_is_not_finalized __bool__ = __float__ = __int__ = _error_is_not_finalized __ceil__ = __floor__ = __round__ = __trunc__ = _error_is_not_finalized __add__ = __iadd__ = __radd__ = _error_is_not_finalized __sub__ = __isub__ = __rsub__ = _error_is_not_finalized __mul__ = __imul__ = __rmul__ = _error_is_not_finalized __truediv___ = __itruediv___ = __rtruediv___ = _error_is_not_finalized __floordiv___ = __ifloordiv___ = __rfloordiv___ = _error_is_not_finalized __eq__ = __ne__ = _error_is_not_finalized __gt__ = __ge__ = __lt__ = __le__ = _error_is_not_finalized __abs__ = __neg__ = __pos__ = _error_is_not_finalized __pow__ = __ipow__ = __rpow__ = _error_is_not_finalized __mod__ = __imod__ = __rmod__ = _error_is_not_finalized __divmod__ = __idivmod__ = __rdivmod__ = _error_is_not_finalized __contains__ = _error_is_not_finalized def __getattribute__(self, name): # Do not allow any attribute to be accessed except for those # explicitly allowed by the AnyDelayedArray class. # This is so no "computation" is peformed on DelayedCell, # DelayedStruct, etc. if name.startswith("_"): return super().__getattribute__(name) if name not in self.__dict__ and name not in AnyDelayedArray.__dict__: return self._error_is_not_finalized() return super().__getattribute__(name) # --- Promise type ------------------------------------------------- @property def as_cell(self) -> "DelayedCell": if self._future is None: self._future = DelayedCell((), self._parent, *self._index) if not isinstance(self._future, DelayedCell): raise TypeError( f"{type(self._future)} cannot be interpreted as a Cell" ) return self._future @property def as_struct(self) -> "DelayedStruct": if self._future is None: self._future = DelayedStruct((), self._parent, *self._index) if not isinstance(self._future, DelayedStruct): raise TypeError( f"{type(self._future)} cannot be interpreted as a Struct" ) return self._future @property def as_num(self) -> "DelayedArray": if self._future is None: self._future = DelayedArray([0], self._parent, *self._index) if not isinstance(self._future, DelayedArray): raise TypeError( f"{type(self._future)} cannot be interpreted as a Array" ) return self._future def as_obj(self, obj): MatlabClass = _imports.MatlabClass if ( self._future is not None and not isinstance(self._future, MatlabClass) ): raise TypeError( f"{type(self._future)} cannot be interpreted as a {type(obj)}" ) self._future = obj return self._future # --- Guess promised type ------------------------------------------ def __call__(self, *index): return self.as_cell(*index) def __getitem__(self, index): return self.as_struct[index] def __getattr__(self, key): return self.as_struct[key] def __setitem__(self, index, value): Array = _imports.Array Cell = _imports.Cell MatlabClass = _imports.MatlabClass Struct = _imports.Struct if isinstance(index, str): arr = self.as_struct elif isinstance(value, MatlabClass): if index not in (0, -1): raise NotImplementedError( "Implicit advanced indexing not implemented for", type(value) ) self.as_obj(value) return self._finalize() elif isinstance(value, (dict, Struct)): arr = self.as_struct elif isinstance(value, (tuple, list, set, Cell)): arr = self.as_cell elif isinstance(value, (int, float, np.number, Array)): arr = self.as_num elif isinstance(value, np.ndarray): if issubclass(value.dtype.type, np.number): arr = self.as_num else: arr = self.as_cell else: arr = self.as_cell arr[index] = value return self._finalize() # Setter -> we can trigger finalize def __setattr__(self, key, value): if key in type(self)._ATTRIBUTES: return super().__setattr__(key, value) self.as_struct[key] = value return self._finalize() # Setter -> we can trigger finalize
This is an object that we return when we don't know how an indexed element will be used yet.
It decides whether it is a Struct, Cell or Array based on the type of indexing that is used.
In Matlab:
a(x,y) = num
indicates thata
is a numeric array;a(x,y) = cell
indicates thata
is a cell array;a{x,y} = any
indicates thata
is a cell array;a(x,y).f = any
indicates thata
is a struct array;a.f = any
indicates thata
is a struct.
These indexing operations can be chained, so in
a(x).b.c{y}.d(z) = 2
:a
is a struct array;b
is a struct;c
is a cell;c{y}
is a structd
is a numeric array.
In Python, there is only one type of indexing (
[]
). This is a problem as we cannot differentiatea{x}.b = y
— wherea
is a cell that contains a struct — froma(x).b = y
— wherea
is a struct array.One solution may be to abuse the "call" operator
()
, so that it returns a cell. This would work in some situations (a[x].b = y
is a struct array, whereasa(x).b = y
is a cell of struct). However, the statementa(x) = y
(which would correspond to matlab'sa{x} = y
) is not valid python syntax. Furthermore, it would induce a new problem, as cells could not be differentiated from function handles, in some cases.Instead, the use of brackets automatically transforms the object into either:
- a
Struct
(in all "get" cases, and in the "set" contexta[x] = y
, wheny
is either adict
or aStruct
); or - an
Array
(in the "set" contexta[x] = y
, wheny
is neither adict
nor aStruct
).
Alternatively, if the user wishes to specify which type the object should take, we implement the properties
as_cell
,as_struct
andas_num
.Therefore:
a[x,y] = num
:a
is a numeric array;a[x,y] = struct
:a
is a numeric array;a[x,y].f = any
:a
is a struct array;a(x,y).f = any
:a
is a cell array containing a struct;a.f = any
:a
is a struct.
And explictly:
a.as_cell[x,y] = any
:a
is a cell array;a.as_struct[x,y].f = any
:a
is a struct array;a.as_cell[x,y].f = any
:a
is a cell array containing a struct;a.as_num[x,y] = num
:a
is a numeric array.
Parameters
parent
:ndarray | dict
-
Reference to the object that will eventually contain this element.
- If the containing array is a
Cell
,parent
should be andarray
view of that cell, andindex
should be a [tuple of] int. - If the containing array is a
Struct
,parent
should be adict
, andindex
should be a string.
- If the containing array is a
index
:str | [tuple of] int
- Index into the parent where this element will be inserted.
Ancestors
Subclasses
Instance variables
prop as_cell : DelayedCell
-
Expand source code
@property def as_cell(self) -> "DelayedCell": if self._future is None: self._future = DelayedCell((), self._parent, *self._index) if not isinstance(self._future, DelayedCell): raise TypeError( f"{type(self._future)} cannot be interpreted as a Cell" ) return self._future
prop as_num : DelayedArray
-
Expand source code
@property def as_num(self) -> "DelayedArray": if self._future is None: self._future = DelayedArray([0], self._parent, *self._index) if not isinstance(self._future, DelayedArray): raise TypeError( f"{type(self._future)} cannot be interpreted as a Array" ) return self._future
prop as_struct : DelayedStruct
-
Expand source code
@property def as_struct(self) -> "DelayedStruct": if self._future is None: self._future = DelayedStruct((), self._parent, *self._index) if not isinstance(self._future, DelayedStruct): raise TypeError( f"{type(self._future)} cannot be interpreted as a Struct" ) return self._future
Methods
def as_obj(self, obj)
-
Expand source code
def as_obj(self, obj): MatlabClass = _imports.MatlabClass if ( self._future is not None and not isinstance(self._future, MatlabClass) ): raise TypeError( f"{type(self._future)} cannot be interpreted as a {type(obj)}" ) self._future = obj return self._future
Inherited members
class DelayedArray (shape, parent, *index)
-
Expand source code
class DelayedArray(WrappedDelayedArray): """ An `Array` that will insert itself in its parent later. See `AnyDelayedArray`. """ def __init__(self, shape, parent, *index): """ Parameters ---------- shape : list[int] Shape of the future numeric array. parent : Struct | Cell | AnyDelayedArray Parent object that contains the future object. *index : int | str Index of the future object in its parent. """ Array = _imports.Array future = Array.from_shape(shape) future._delayed_wrapper = self super().__init__(future, parent, *index)
An
Array
that will insert itself in its parent later.See
AnyDelayedArray
.Parameters
shape
:list[int]
- Shape of the future numeric array.
parent
:Struct | Cell | AnyDelayedArray
- Parent object that contains the future object.
*index
:int | str
- Index of the future object in its parent.
Ancestors
Inherited members
class DelayedCell (shape, parent, *index)
-
Expand source code
class DelayedCell(WrappedDelayedArray): """ A `Cell` that will insert itself in its parent later. See `AnyDelayedArray`. """ def __init__(self, shape, parent, *index): """ Parameters ---------- shape : list[int] Shape of the future cell array. parent : Struct | Cell | AnyDelayedArray Parent object that contains the future object. *index : int | str Index of the future object in its parent. """ Cell = _imports.Cell future = Cell.from_shape(shape) future._delayed_wrapper = self super().__init__(future, parent, *index) # Insert delayed arrays instead of the usual defaults opt = dict( flags=["refs_ok", "zerosize_ok", "multi_index"], op_flags=["writeonly", "no_broadcast"], ) arr = np.ndarray.view(self._future, np.ndarray) with np.nditer(arr, **opt) as iter: for elem in iter: elem[()] = AnyDelayedArray(self, iter.multi_index)
A
Cell
that will insert itself in its parent later.See
AnyDelayedArray
.Parameters
shape
:list[int]
- Shape of the future cell array.
parent
:Struct | Cell | AnyDelayedArray
- Parent object that contains the future object.
*index
:int | str
- Index of the future object in its parent.
Ancestors
Inherited members
class DelayedStruct (shape, parent, *index)
-
Expand source code
class DelayedStruct(WrappedDelayedArray): """ A `Struct` that will insert itself in its parent later. See `AnyDelayedArray`. """ def __init__(self, shape, parent, *index): """ Parameters ---------- shape : list[int] Shape of the future struct array. parent : Struct | Cell | AnyDelayedArray Parent object that contains the future object. *index : int | str Index of the future object in its parent. """ Struct = _imports.Struct future = Struct.from_shape(shape) future._delayed_wrapper = self super().__init__(future, parent, *index)
A
Struct
that will insert itself in its parent later.See
AnyDelayedArray
.Parameters
shape
:list[int]
- Shape of the future struct array.
parent
:Struct | Cell | AnyDelayedArray
- Parent object that contains the future object.
*index
:int | str
- Index of the future object in its parent.
Ancestors
Inherited members
class WrappedDelayedArray (future, parent, *index)
-
Expand source code
class WrappedDelayedArray(AnyDelayedArray): """ Base class for future objects with known type. See `DelayedStruct`, `DelayedCell`, `DelayedArray`. """ def __init__(self, future, parent, *index): """ Parameters ---------- future : Struct | Cell | Array Concrete object that will be inserted in the parent later. parent : Struct | Cell | AnyDelayedArray Parent object that contains the future object. *index : int | str Index of the future obect in its parent. """ super().__init__(parent, *index) self._future = future def __call__(self, *index): return self._future.__call__(*index) def __getitem__(self, index): return self._future.__getitem__(index) def __getattr__(self, key): return self._future.__getattr__(key) def __setitem__(self, index, value): self._future.__setitem__(index, value) self._finalize() def __setattr__(self, key, value): if key in type(self)._ATTRIBUTES: return super().__setattr__(key, value) self._future.__setattr__(key, value) self._finalize()
Base class for future objects with known type.
See
DelayedStruct
,DelayedCell
,DelayedArray
.Parameters
future
:Struct | Cell | Array
- Concrete object that will be inserted in the parent later.
parent
:Struct | Cell | AnyDelayedArray
- Parent object that contains the future object.
*index
:int | str
- Index of the future obect in its parent.
Ancestors
Subclasses
Inherited members