from functools import lru_cache
from functools import wraps, partial
from typing import Mapping, Optional, Any, Type, TypeVar, Union, Callable, Tuple
from graphviz import Digraph
from hbutils.reflection import dynamic_call, raising
from ..func import method_treelize, MISSING_NOT_ALLOW, func_treelize
from ..tree import TreeValue, jsonify, clone, typetrans, mapping, mask, filter_, reduce_, union, graphics, walk
from ..tree import rise as rise_func
from ..tree import subside as subside_func
_BASE_GENERATION_CONFIG = {}
[docs]def general_tree_value(base: Optional[Mapping[str, Any]] = None,
methods: Optional[Mapping[str, Any]] = None):
"""
Overview:
Get general tree value class.
Arguments:
- base (:obj:`Optional[Mapping[str, Any]]`): Base configuration of `func_treelize`.
- methods (:obj:`Optional[Mapping[str, Any]]`): Method configurations of `func_treelize`.
Returns:
- clazz (:obj:`Type[TreeValue]`): General tree value class.
"""
base = base or {}
methods = methods or {}
def _dynamic_suffix_dec(func, f):
return wraps(func)(dynamic_call(f))
@lru_cache()
def _get_decorator(name, treelize: bool, ext_cfg: Optional[tuple] = None):
_item = methods.get(name, None)
if treelize and isinstance(_item or {}, dict):
_config = _BASE_GENERATION_CONFIG.copy()
_config.update(base)
_config.update(_item or {})
_config.update(dict(ext_cfg or ()))
return lambda func: method_treelize(**_config)(func)
elif isinstance(_item, BaseException) or (isinstance(_item, type) and issubclass(_item, BaseException)):
return lambda func: _dynamic_suffix_dec(func, raising(_item))
elif hasattr(_item, '__call__'):
return lambda func: _dynamic_suffix_dec(func, _item)
elif name in methods.keys():
return lambda func: _dynamic_suffix_dec(func, lambda: methods[name])
else:
return lambda func: func
def _decorate(func, treelize: bool, ext_cfg: Optional[dict] = None):
return _get_decorator(func.__name__, treelize, tuple(sorted((ext_cfg or {}).items())))(func)
_decorate_treelize = partial(_decorate, treelize=True)
_decorate_method = partial(_decorate, treelize=False)
_decorate_and_replace = partial(_decorate, treelize=True, ext_cfg=dict(self_copy=True))
_TreeValue = TypeVar("_TreeValue", bound=TreeValue)
# noinspection PyUnresolvedReferences,PyMethodFirstArgAssignment,PyMethodParameters
class _GeneralTreeValue(TreeValue):
@method_treelize()
def _attr_extern(self_, key):
"""
Overview:
External protected function for support the unfounded attributes. \
In `FastTreeValue` it is extended to support the access to values' attribute.
Arguments:
- key (:obj:`str`): Attribute name.
Returns:
- return (:obj:): TreeValue of the values' attribute.
Example:
>>> class Container:
>>> def __init__(self, value):
>>> self.__value = value
>>>
>>> @property
>>> def value(self):
>>> return self.__value
>>>
>>> def append(self, v):
>>> return self.__value + v
>>>
>>> t = FastTreeValue({'a': Container(1), 'b': Container(2)})
>>> t.value # FastTreeValue({'a': 1, 'b': 2})
>>> t.append # FastTreeValue({'a': <method 'append' of Container(1)>, 'b': <method 'append' of Container(2)>})
"""
return getattr(self_, key)
@_decorate_method
def json(self):
"""
Overview:
Dump current `TreeValue` object to json data.
Returns:
- json (:obj:`dict`): Dumped json data.
Example:
>>> t = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t.json() # {'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}}
"""
return jsonify(self)
@_decorate_method
def clone(self, copy_value: Union[None, bool, Callable, Any] = None):
"""
Overview:
Create a fully clone of the current tree.
Returns:
- tree (:obj:`_TreeValue`): Cloned tree value object.
- copy_value (:obj:`Union[None, bool, Callable, Any]`): Deep copy value or not, \
default is `None` which means do not deep copy the values. \
If deep copy is required, just set it to `True`.
Example:
>>> t = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t.x.clone() # FastTreeValue({'c': 3, 'd': 4})
"""
return clone(self, copy_value)
@_decorate_method
def type(self, clazz: Type[_TreeValue]) -> _TreeValue:
"""
Overview:
Transform tree value object to another tree value type. \
Attention that in this function, no copy will be made, \
the original tree value and the transformed tree value are using the same space area.
Arguments:
- return_type (:obj:`Type[_TreeValue]`): Target tree value type
Returns:
- tree (:obj:`_TreeValue`): Transformed tree value object.
Example:
>>> t = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t.type(TreeValue) # TreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
"""
return typetrans(self, clazz)
@_decorate_method
def map(self, mapper, delayed=False):
"""
Overview:
Do mapping on every value in this tree.
Arguments:
- func (:obj:): Function for mapping
- delayed (:obj:`bool`): Enable delayed mode for this mapping.
Returns:
- tree (:obj:`_TreeValue`): Mapped tree value object.
Example:
>>> t = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t.map(lambda x: x + 2) # FastTreeValue({'a': 3, 'b': 4, 'x': {'c': 5, 'd': 6}})
>>> t.map(lambda: 1) # FastTreeValue({'a': 1, 'b': 1, 'x': {'c': 1, 'd': 1}})
>>> t.map(lambda x, p: p) # FastTreeValue({'a': ('a',), 'b': ('b',), 'x': {'c': ('x', 'c'), 'd': ('x', 'd')}})
"""
return mapping(self, mapper, delayed)
@_decorate_method
def mask(self, mask_: TreeValue, remove_empty: bool = True):
"""
Overview:
Filter the element in the tree with a mask
Arguments:
- `mask_` (:obj:`TreeValue`): Tree value mask object
- `remove_empty` (:obj:`bool`): Remove empty tree node automatically, default is `True`.
Returns:
- tree (:obj:`_TreeValue`): Filtered tree value object.
Example:
>>> t = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t.mask(TreeValue({'a': True, 'b': False, 'x': False})) # FastTreeValue({'a': 1})
>>> t.mask(TreeValue({'a': True, 'b': False, 'x': {'c': True, 'd': False}})) # FastTreeValue({'a': 1, 'x': {'c': 3}})
"""
return mask(self, mask_, remove_empty)
@_decorate_method
def filter(self, func, remove_empty: bool = True):
"""
Overview:
Filter the element in the tree with a predict function.
Arguments:
- func (:obj:): Function for filtering
- remove_empty (:obj:`bool`): Remove empty tree node automatically, default is `True`.
Returns:
- tree (:obj:`_TreeValue`): Filtered tree value object.
Example:
>>> t = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t.filter(lambda x: x < 3) # FastTreeValue({'a': 1, 'b': 2})
>>> t.filter(lambda x: x < 3, False) # FastTreeValue({'a': 1, 'b': 2, 'x': {}})
>>> t.filter(lambda x: x % 2 == 1) # FastTreeValue({'a': 1, 'x': {'c': 3}})
>>> t.filter(lambda x, p: p[0] in {'b', 'x'}) # FastTreeValue({'b': 2, 'x': {'c': 3, 'd': 4}})
"""
return filter_(self, func, remove_empty)
@_decorate_method
def walk(self):
"""
Overview:
Walk the values and nodes in the tree.
The order of walk is not promised, if you need the ordered walking result, \
just use function ``sorted`` at the outer side of :func:`walk`.
Returns:
- iter: Iterator to walk the given tree, contains 2 items, the 1st one is the full \
path of the node, the 2nd one is the value.
Examples:
>>> from treevalue import FastTreeValue, walk
>>> tv1 = FastTreeValue({'a': 1, 'b': 2, 'c': {'x': 2, 'y': 2}})
>>> for k, v in tv1.walk():
... print(k, v)
() <FastTreeValue 0x7f672fc533c8>
├── 'a' --> 1
├── 'b' --> 2
└── 'c' --> <FastTreeValue 0x7f672fc53438>
├── 'x' --> 2
└── 'y' --> 2
('a',) 1
('b',) 2
('c',) <FastTreeValue 0x7f672fc53438>
├── 'x' --> 2
└── 'y' --> 2
('c', 'x') 2
('c', 'y') 2
"""
return walk(self)
@_decorate_method
def reduce(self, func):
"""
Overview
Reduce the tree to value.
Arguments:
- func (:obj:): Function for reducing
Returns:
- result (:obj:): Reduce result
Examples:
>>> from functools import reduce
>>>
>>> t = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t.reduce(lambda **kwargs: sum(kwargs.values())) # 10, 1 + 2 + (3 + 4)
>>> t.reduce(lambda **kwargs: reduce(lambda x, y: x * y, list(kwargs.values()))) # 24, 1 * 2 * (3 * 4)
"""
return reduce_(self, func)
@_decorate_method
def rise(self, dict_: bool = True, list_: bool = True, tuple_: bool = True, template=None):
"""
Overview:
Make the structure (dict, list, tuple) in value rise up to the top, above the tree value.
Arguments:
- `dict_` (:obj:`bool`): Enable dict rise, default is `True`.
- `list_` (:obj:`bool`): Enable list rise, default is `True`.
- `tuple_` (:obj:`bool`): Enable list rise, default is `True`.
- template (:obj:): Rising template, default is `None`, which means auto detect.
Returns:
- risen (:obj:): Risen value.
Example:
>>> t = FastTreeValue({'x': raw({'a': [1, 2], 'b': [2, 3]}), 'y': raw({'a': [5, 6, 7], 'b': [7, 8]})})
>>> dt = t.rise()
>>> # dt will be {'a': <FastTreeValue 1>, 'b': [<FastTreeValue 2>, <FastTreeValue 3>]}
>>> # FastTreeValue 1 will be FastTreeValue({'x': [1, 2], 'y': [5, 6, 7]})
>>> # FastTreeValue 2 will be FastTreeValue({'x': 2, 'y': 7})
>>> # FastTreeValue 3 will be FastTreeValue({'x': 3, 'y': 8})
>>>
>>> t2 = FastTreeValue({'x': raw({'a': [1, 2], 'b': [2, 3]}), 'y': raw({'a': [5, 6], 'b': [7, 8]})})
>>> dt2 = t2.rise()
>>> # dt2 will be {'a': [<FastTreeValue 1>, <FastTreeValue 2>], 'b': [<FastTreeValue 3>, <FastTreeValue 4>]}
>>> # FastTreeValue 1 will be FastTreeValue({'x': 1, 'y': 5})
>>> # FastTreeValue 2 will be FastTreeValue({'x': 2, 'y': 6})
>>> # FastTreeValue 3 will be FastTreeValue({'x': 2, 'y': 7})
>>> # FastTreeValue 4 will be FastTreeValue({'x': 3, 'y': 8})
>>>
>>> dt3 = t2.rise(template={'a': None, 'b': None})
>>> # dt3 will be {'a': <FastTreeValue 1>, 'b': <FastTreeValue 2>}
>>> # FastTreeValue 1 will be FastTreeValue({'x': [1, 2], 'y': [5, 6]})
>>> # FastTreeValue 2 will be FastTreeValue({'x': [2, 3], 'y': [7, 8]})
"""
return rise_func(self, dict_, list_, tuple_, template)
@_decorate_method
def graph(self, root: Optional[str] = None, title: Optional[str] = None,
cfg: Optional[dict] = None,
dup_value: Union[bool, Callable, type, Tuple[Type, ...]] = False,
repr_gen: Optional[Callable] = None,
node_cfg_gen: Optional[Callable] = None,
edge_cfg_gen: Optional[Callable] = None) -> Digraph:
"""
Overview:
Draw graph of this tree value.
Args:
- root (:obj:`Optional[str]`): Root name of the graph, default is ``None``, \
this name will be automatically generated.
- title (:obj:`Optional[str]`): Title of the graph, default is ``None``, \
this title will be automatically generated from ``root`` argument.
- cfg (:obj:`Optional[dict]`): Configuration of the graph.
- dup_value (:obj:`Union[bool, Callable, type, Tuple[Type, ...]]`): Value duplicator, \
set `True` to make value with same id use the same node in graph, \
you can also define your own node id algorithm by this argument. \
Default is `False` which means do not use value duplicator.
- repr_gen (:obj:`Optional[Callable]`): Representation format generator, \
default is `None` which means using `repr` function.
- node_cfg_gen (:obj:`Optional[Callable]`): Node configuration generator, \
default is `None` which means no configuration.
- edge_cfg_gen (:obj:`Optional[Callable]`): Edge configuration generator, \
default is `None` which means no configuration.
Returns:
- graph (:obj:`Digraph`): Generated graph of tree values.
"""
root = root or ('<%s #%x>' % (type(self).__name__, id(self._detach())))
title = title or ('Graph of tree %s.' % (root,))
return graphics(
(self, root), title=title, cfg=cfg, dup_value=dup_value,
repr_gen=repr_gen, node_cfg_gen=node_cfg_gen, edge_cfg_gen=edge_cfg_gen,
)
@classmethod
@_decorate_method
def func(cls, mode: str = 'strict', inherit: bool = True,
missing: Union[Any, Callable] = MISSING_NOT_ALLOW, delayed: bool = False,
subside: Union[Mapping, bool, None] = None, rise: Union[Mapping, bool, None] = None):
"""
Overview:
Wrap a common function to tree-supported function based on this type.
Arguments:
- mode (:obj:`str`): Mode of the wrapping, default is `strict`.
- inherit (:obj:`bool`): Allow inherit in wrapped function, default is `True`.
- missing (:obj:`Union[Any, Callable]`): Missing value or lambda generator of when missing, \
default is `MISSING_NOT_ALLOW`, which means raise `KeyError` when missing detected.
- delayed (:obj:`bool`): Enable delayed mode or not, the calculation will be delayed when enabled, \
default is ``False``, which means to all the calculation at once.
- subside (:obj:`Union[Mapping, bool, None]`): Subside enabled to function's arguments or not, \
and subside configuration, default is `None` which means do not use subside. \
When subside is `True`, it will use all the default arguments in `subside` function.
- rise (:obj:`Union[Mapping, bool, None]`): Rise enabled to function's return value or not, \
and rise configuration, default is `None` which means do not use rise. \
When rise is `True`, it will use all the default arguments in `rise` function. \
(Not recommend to use auto mode when your return structure is not so strict.)
Returns:
- decorator (:obj:`Callable`): Wrapper for tree-supported function.
Example:
>>> from treevalue import FastTreeValue
>>>
>>> @FastTreeValue.func()
>>> def ssum(a, b):
>>> return a + b # the a and b will be integers, not TreeValue
>>>
>>> t1 = TreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = TreeValue({'a': 11, 'b': 22, 'x': {'c': 33, 'd': 5}})
>>> ssum(1, 2) # 3
>>> ssum(t1, t2) # FastTreeValue({'a': 12, 'b': 24, 'x': {'c': 36, 'd': 9}})
"""
return func_treelize(mode, cls, inherit, missing, delayed, subside, rise)
@classmethod
@_decorate_method
def union(cls, *trees, return_type=None, **kwargs):
"""
Overview:
Union tree values together.
Arguments:
- trees (:obj:`_TreeValue`): Tree value objects.
- mode (:obj:): Mode of the wrapping, default is `strict`.
- return_type (:obj:`Optional[Type[_ClassType]]`): Return type of the wrapped function, default is `TreeValue`.
- inherit (:obj:`bool`): Allow inherit in wrapped function, default is `True`.
- missing (:obj:): Missing value or lambda generator of when missing, default is `MISSING_NOT_ALLOW`, which \
means raise `KeyError` when missing detected.
Returns:
- result (:obj:`TreeValue`): Unionised tree value.
Example:
>>> t = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> tx = t.map(lambda v: v % 2 == 1)
>>> FastTreeValue.union(t, tx) # FastTreeValue({'a': (1, True), 'b': (2, False), 'x': {'c': (3, True), 'd': (4, False)}})
"""
return union(*trees, return_type=return_type or cls, **kwargs)
@classmethod
@_decorate_method
def subside(cls, value, dict_: bool = True, list_: bool = True, tuple_: bool = True,
return_type: Optional[Type[_TreeValue]] = None, **kwargs):
"""
Overview:
Drift down the structures (list, tuple, dict) down to the tree's value.
Arguments:
- value (:obj:): Original value object, may be nested dict, list or tuple.
- `dict_` (:obj:`bool`): Enable dict subside, default is `True`.
- `list_` (:obj:`bool`): Enable list subside, default is `True`.
- `tuple_` (:obj:`bool`): Enable list subside, default is `True`.
- mode (:obj:): Mode of the wrapping, default is `strict`.
- return_type (:obj:`Optional[Type[_ClassType]]`): Return type of the wrapped function, \
will be auto detected when there is exactly one tree value type in this original value, \
otherwise the default will be `TreeValue`.
- inherit (:obj:`bool`): Allow inherit in wrapped function, default is `True`.
- missing (:obj:): Missing value or lambda generator of when missing, default is `MISSING_NOT_ALLOW`, which \
means raise `KeyError` when missing detected.
Returns:
- return (:obj:`_TreeValue`): Subsided tree value.
Example:
>>> data = {
>>> 'a': TreeValue({'a': 1, 'b': 2}),
>>> 'x': {
>>> 'c': TreeValue({'a': 3, 'b': 4}),
>>> 'd': [
>>> TreeValue({'a': 5, 'b': 6}),
>>> TreeValue({'a': 7, 'b': 8}),
>>> ]
>>> },
>>> 'k': '233'
>>> }
>>>
>>> tree = FastTreeValue.subside(data)
>>> # tree should be --> FastTreeValue({
>>> # 'a': raw({'a': 1, 'k': '233', 'x': {'c': 3, 'd': [5, 7]}}),
>>> # 'b': raw({'a': 2, 'k': '233', 'x': {'c': 4, 'd': [6, 8]}}),
>>> #}), all structures above the tree values are subsided to the bottom of the tree.
"""
return subside_func(value, dict_, list_, tuple_,
return_type=return_type or cls, **kwargs)
@classmethod
@_decorate_method
def graphics(cls, *trees, title: Optional[str] = None, cfg: Optional[dict] = None,
dup_value: Union[bool, Callable, type, Tuple[Type, ...]] = False,
repr_gen: Optional[Callable] = None,
node_cfg_gen: Optional[Callable] = None,
edge_cfg_gen: Optional[Callable] = None) -> Digraph:
"""
Overview:
Draw graph by tree values.
Multiple tree values is supported.
Args:
- trees: Given tree values, tuples of `Tuple[TreeValue, str]` or tree values are both accepted.
- title (:obj:`Optional[str]`): Title of the graph.
- cfg (:obj:`Optional[dict]`): Configuration of the graph.
- dup_value (:obj:`Union[bool, Callable, type, Tuple[Type, ...]]`): Value duplicator, \
set `True` to make value with same id use the same node in graph, \
you can also define your own node id algorithm by this argument. \
Default is `False` which means do not use value duplicator.
- repr_gen (:obj:`Optional[Callable]`): Representation format generator, \
default is `None` which means using `repr` function.
- node_cfg_gen (:obj:`Optional[Callable]`): Node configuration generator, \
default is `None` which means no configuration.
- edge_cfg_gen (:obj:`Optional[Callable]`): Edge configuration generator, \
default is `None` which means no configuration.
Returns:
- graph (:obj:`Digraph`): Generated graph of tree values.
"""
return graphics(
*trees, title=title, cfg=cfg, dup_value=dup_value,
repr_gen=repr_gen, node_cfg_gen=node_cfg_gen, edge_cfg_gen=edge_cfg_gen,
)
@_decorate_treelize
def __add__(self_, other):
"""
Overview:
Add tree values together.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 11, 'b': 22, 'x': {'c': 30, 'd': 40}})
>>> t1 + t2 # FastTreeValue({'a': 12, 'b': 24, 'x': {'c': 33, 'd': 44}})
"""
return self_ + other
@_decorate_treelize
def __radd__(self_, other):
"""
Overview:
Right version of `__add__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 1 + t1 # FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
"""
return other + self_
@_decorate_and_replace
def __iadd__(self_, other):
"""
Overview:
Self version of `__add__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 11, 'b': 22, 'x': {'c': 30, 'd': 40}})
>>> t1 += t2
>>> t1 # t1's id will not change, FastTreeValue({'a': 12, 'b': 24, 'x': {'c': 33, 'd': 44}})
"""
self_ += other
return self_
@_decorate_treelize
def __sub__(self_, other):
"""
Overview:
Substract tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 11, 'b': 22, 'x': {'c': 30, 'd': 40}})
>>> t1 - t2 # FastTreeValue({'a': -10, 'b': -20, 'x': {'c': -27, 'd': -36}})
"""
return self_ - other
@_decorate_treelize
def __rsub__(self_, other):
"""
Overview:
Right version of `__sub__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 1 - t1 # FastTreeValue({'a': 0, 'b': -1, 'x': {'c': -2, 'd': -3}})
"""
return other - self_
@_decorate_and_replace
def __isub__(self_, other):
"""
Overview:
Self version of `__sub__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 11, 'b': 22, 'x': {'c': 30, 'd': 40}})
>>> t1 -= t2
>>> t1 # t1's id will not change, FastTreeValue({'a': -10, 'b': -20, 'x': {'c': -27, 'd': -36}})
"""
self_ -= other
return self_
@_decorate_treelize
def __mul__(self_, other):
"""
Overview:
Multiply tree values together.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 11, 'b': 22, 'x': {'c': 30, 'd': 40}})
>>> t1 * t2 # FastTreeValue({'a': 11, 'b': 44, 'x': {'c': 90, 'd': 160}})
"""
return self_ * other
@_decorate_treelize
def __rmul__(self_, other):
"""
Overview:
Right version of `__mul__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 2 * t1 # FastTreeValue({'a': 2, 'b': 4, 'x': {'c': 6, 'd': 8}})
"""
return other * self_
@_decorate_and_replace
def __imul__(self_, other):
"""
Overview:
Self version of `__mul__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 11, 'b': 22, 'x': {'c': 30, 'd': 40}})
>>> t1 *= t2
>>> t1 # t1's id will not change, FastTreeValue({'a': 11, 'b': 44, 'x': {'c': 90, 'd': 160}})
"""
self_ *= other
return self_
@_decorate_treelize
def __matmul__(self_, other):
"""
Overview:
Matrix tree values together, can be used in numpy or torch.
"""
return self_ @ other
@_decorate_treelize
def __rmatmul__(self_, other):
"""
Overview:
Right version of `__matmul__`.
"""
return other @ self_
@_decorate_and_replace
def __imatmul__(self_, other):
"""
Overview:
Self version of `__matmul__`.
Original id of self will be kept.
"""
self_ @= other
return self_
@_decorate_treelize
def __truediv__(self_, other):
"""
Overview:
True divide tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 10, 'b': 25, 'x': {'c': 30, 'd': 40}})
>>> t1 / t2 # FastTreeValue({'a': 0.1, 'b': 0.08, 'x': {'c': 0.1, 'd': 0.1}})
"""
return self_ / other
@_decorate_treelize
def __rtruediv__(self_, other):
"""
Overview:
Right version of `__truediv__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 6 / t1 # FastTreeValue({'a': 6, 'b': 3, 'x': {'c': 2, 'd': 1.5}})
"""
return other / self_
@_decorate_and_replace
def __itruediv__(self_, other):
"""
Overview:
Self version of `__truediv__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 10, 'b': 25, 'x': {'c': 30, 'd': 40}})
>>> t1 /= t2
>>> t1 # t1's id will not change, FastTreeValue({'a': 0.1, 'b': 0.08, 'x': {'c': 0.1, 'd': 0.1}})
"""
self_ /= other
return self_
@_decorate_treelize
def __floordiv__(self_, other):
"""
Overview:
Floor divide tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 10, 'b': 25, 'x': {'c': 30, 'd': 40}})
>>> t2 // t1 # FastTreeValue({'a': 10, 'b': 12, 'x': {'c': 10, 'd': 10}})
"""
return self_ // other
@_decorate_treelize
def __rfloordiv__(self_, other):
"""
Overview:
Right version of `__floordiv__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 6 // t1 # FastTreeValue({'a': 6, 'b': 3, 'x': {'c': 2, 'd': 1}})
"""
return other // self_
@_decorate_and_replace
def __ifloordiv__(self_, other):
"""
Overview:
Self version of `__floordiv__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 10, 'b': 25, 'x': {'c': 30, 'd': 40}})
>>> t2 //= t1
>>> t2 # t2's id will not change, FastTreeValue({'a': 10, 'b': 12, 'x': {'c': 10, 'd': 10}})
"""
self_ //= other
return self_
@_decorate_treelize
def __mod__(self_, other):
"""
Overview:
Mod tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 10, 'b': 25, 'x': {'c': 30, 'd': 40}})
>>> t2 % t1 # FastTreeValue({'a': 0, 'b': 1, 'x': {'c': 0, 'd': 0}})
"""
return self_ % other
@_decorate_treelize
def __rmod__(self_, other):
"""
Overview:
Right version of `__mod__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 6 % t1 # FastTreeValue({'a': 0, 'b': 0, 'x': {'c': 0, 'd': 2}})
"""
return other % self_
@_decorate_and_replace
def __imod__(self_, other):
"""
Overview:
Self version of `__mod__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 10, 'b': 25, 'x': {'c': 30, 'd': 40}})
>>> t2 %= t1
>>> t2 # t2's id will not change, FastTreeValue({'a': 0, 'b': 1, 'x': {'c': 0, 'd': 0}})
"""
self_ %= other
return self_
@_decorate_treelize
def __pow__(self_, power):
"""
Overview:
Mod tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 ** t2 # FastTreeValue({'a': 1, 'b': 8, 'x': {'c': 81, 'd': 1024}})
"""
return self_ ** power
@_decorate_treelize
def __rpow__(self_, other):
"""
Overview:
Right version of `__pow__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 2 ** t1 # FastTreeValue({'a': 2, 'b': 4, 'x': {'c': 8, 'd': 16}})
"""
return other ** self_
@_decorate_and_replace
def __ipow__(self_, other):
"""
Overview:
Self version of `__pow__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 **= t2
>>> t1 # t1's id will not change, FastTreeValue({'a': 1, 'b': 8, 'x': {'c': 81, 'd': 1024}})
"""
self_ **= other
return self_
@_decorate_treelize
def __and__(self_, other):
"""
Overview:
Bitwise and tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 & t2 # FastTreeValue({'a': 0, 'b': 2, 'x': {'c': 0, 'd': 4}})
"""
return self_ & other
@_decorate_treelize
def __rand__(self_, other):
"""
Overview:
Right version of `__and__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 5 & t1 # FastTreeValue({'a': 1, 'b': 0, 'x': {'c': 1, 'd': 4}})
"""
return other & self_
@_decorate_and_replace
def __iand__(self_, other):
"""
Overview:
Self version of `__and__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 &= t2
>>> t1 # t1's id will not change, FastTreeValue({'a': 0, 'b': 2, 'x': {'c': 0, 'd': 4}})
"""
self_ &= other
return self_
@_decorate_treelize
def __or__(self_, other):
"""
Overview:
Bitwise or tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 | t2 # FastTreeValue({'a': 3, 'b': 3, 'x': {'c': 7, 'd': 5}})
"""
return self_ | other
@_decorate_treelize
def __ror__(self_, other):
"""
Overview:
Right version of `__or__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 5 | t1 # FastTreeValue({'a': 5, 'b': 7, 'x': {'c': 7, 'd': 5}})
"""
return other | self_
@_decorate_and_replace
def __ior__(self_, other):
"""
Overview:
Self version of `__or__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 |= t2
>>> t1 # t1's id will not change, FastTreeValue({'a': 3, 'b': 3, 'x': {'c': 7, 'd': 5}})
"""
self_ |= other
return self_
@_decorate_treelize
def __xor__(self_, other):
"""
Overview:
Bitwise or tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 ^ t2 # FastTreeValue({'a': 3, 'b': 1, 'x': {'c': 7, 'd': 1}})
"""
return self_ ^ other
@_decorate_treelize
def __rxor__(self_, other):
"""
Overview:
Right version of `__xor__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 5 ^ t1 # FastTreeValue({'a': 4, 'b': 7, 'x': {'c': 6, 'd': 1}})
"""
return other ^ self_
@_decorate_and_replace
def __ixor__(self_, other):
"""
Overview:
Self version of `__xor__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 ^= t2
>>> t1 # t1's id will not change, FastTreeValue({'a': 3, 'b': 1, 'x': {'c': 7, 'd': 1}})
"""
self_ ^= other
return self_
@_decorate_treelize
def __lshift__(self_, other):
"""
Overview:
Left shift tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 << t2 # FastTreeValue({'a': 4, 'b': 16, 'x': {'c': 48, 'd': 128}})
"""
return self_ << other
@_decorate_treelize
def __rlshift__(self_, other):
"""
Overview:
Right version of `__lshift__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 3 << t1 # FastTreeValue({'a': 6, 'b': 12, 'x': {'c': 24, 'd': 48}})
"""
return other << self_
@_decorate_and_replace
def __ilshift__(self_, other):
"""
Overview:
Self version of `__xor__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1 <<= t2
>>> t1 # t1's id will not change, FastTreeValue({'a': 4, 'b': 16, 'x': {'c': 48, 'd': 128}})
"""
self_ <<= other
return self_
@_decorate_treelize
def __rshift__(self_, other):
"""
Overview:
Left shift tree values.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 20, 'b': 30, 'x': {'c': 40, 'd': 50}})
>>> t2 >> t1 # FastTreeValue({'a': 10, 'b': 7, 'x': {'c': 5, 'd': 3}})
"""
return self_ >> other
@_decorate_treelize
def __rrshift__(self_, other):
"""
Overview:
Right version of `__rshift__`.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> 64 >> t1 # FastTreeValue({'a': 32, 'b': 16, 'x': {'c': 8, 'd': 4}})
"""
return other >> self_
@_decorate_and_replace
def __irshift__(self_, other):
"""
Overview:
Self version of `__xor__`.
Original id of self will be kept.
Example:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> t2 = FastTreeValue({'a': 20, 'b': 30, 'x': {'c': 40, 'd': 50}})
>>> t2 >>= t1
>>> t2 # t2's id will not change, FastTreeValue({'a': 10, 'b': 7, 'x': {'c': 5, 'd': 3}})
"""
self_ >>= other
return self_
@_decorate_treelize
def __pos__(self_):
"""
Overview:
Positive tree values.
Examples:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> +t1 # FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
"""
return +self_
@_decorate_treelize
def __neg__(self_):
"""
Overview:
Negative tree values.
Examples:
>>> t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
>>> -t1 # FastTreeValue({'a': -1, 'b': -2, 'x': {'c': -3, 'd': -4}})
"""
return -self_
@_decorate_treelize
def __invert__(self_):
"""
Overview:
Bitwise invert tree values.
Examples:
>>> t1 = FastTreeValue({'a': 1, 'b': -2, 'x': {'c': 3, 'd': -4}})
>>> ~t1 # FastTreeValue({'a': -2, 'b': 1, 'x': {'c': -4, 'd': 3}})
"""
return ~self_
@method_treelize()
def _getitem_extern(self_, item):
return self_[item]
def __getitem__(self, item):
"""
Overview:
Get item of tree values.
Examples:
>>> from treevalue import FastTreeValue
>>> t1 = FastTreeValue({'a': [1, 2], 'b': [2, 3], 'x': {'c': [3, 4], 'd': [4, 5]}})
>>> t1['a'] # key access
[1, 2]
>>> t1[0] # value access
<FastTreeValue 0x7fa9c8c36cf8>
├── 'a' --> 1
├── 'b' --> 2
└── 'x' --> <FastTreeValue 0x7fa9c8c369e8>
├── 'c' --> 3
└── 'd' --> 4
>>> t1[-1]
<FastTreeValue 0x7fa9c8a72dd8>
├── 'a' --> 2
├── 'b' --> 3
└── 'x' --> <FastTreeValue 0x7fa9c8c36978>
├── 'c' --> 4
└── 'd' --> 5
>>> t1[::-1]
<FastTreeValue 0x7fa9c8c36d30>
├── 'a' --> [2, 1]
├── 'b' --> [3, 2]
└── 'x' --> <FastTreeValue 0x7fa9c8c36cf8>
├── 'c' --> [4, 3]
└── 'd' --> [5, 4]
>>> t1['x'][0] # mixed access
<FastTreeValue 0x7fa9c8a7b0b8>
├── 'c' --> 3
└── 'd' --> 4
.. note::
If you need to get the string items from the node values, you can use double bracket. \
For example
>>> from treevalue import FastTreeValue, raw
>>> tv4 = FastTreeValue({
... 'a': raw({'a': 1, 'y': 2}),
... 'c': {'x': raw({'a': 3, 'y': 4})},
... })
>>> tv4['a'] # key access
{'a': 1, 'y': 2}
>>> tv4[['a']] # value access
<FastTreeValue 0x7fa9c8c36588>
├── 'a' --> 1
└── 'c' --> <FastTreeValue 0x7fa9c8a7b0b8>
└── 'x' --> 3
"""
return TreeValue.__getitem__(self, item)
@method_treelize()
def _setitem_extern(self_, key, value):
self_[key] = value
def __setitem__(self, key, value):
"""
Overview:
Set item of tree values.
Examples:
>>> from treevalue import FastTreeValue
>>> t1 = FastTreeValue({'a': [1, 2], 'b': [2, 3], 'x': {'c': [3, 4], 'd': [4, 5]}})
>>> t1[0] = -2
>>> t1
<FastTreeValue 0x7fa9c8c36518>
├── 'a' --> [-2, 2]
├── 'b' --> [-2, 3]
└── 'x' --> <FastTreeValue 0x7fa9c8a72dd8>
├── 'c' --> [-2, 4]
└── 'd' --> [-2, 5]
>>> t1[0] = FastTreeValue({'a': 2, 'b': 3, 'x': {'c': 4, 'd': 5}})
>>> t1
<FastTreeValue 0x7fa9c8c36518>
├── 'a' --> [2, 2]
├── 'b' --> [3, 3]
└── 'x' --> <FastTreeValue 0x7fa9c8a72dd8>
├── 'c' --> [4, 4]
└── 'd' --> [5, 5]
>>> t1['b'] = [22, 33]
>>> t1
<FastTreeValue 0x7fa9c8c36518>
├── 'a' --> [2, 2]
├── 'b' --> [22, 33]
└── 'x' --> <FastTreeValue 0x7fa9c8a72dd8>
├── 'c' --> [4, 4]
└── 'd' --> [5, 5]
.. note::
If you need to set the string items from the node values, you can use double bracket. \
For example
>>> from treevalue import FastTreeValue, raw
>>> tv4 = FastTreeValue({
... 'a': raw({'a': 1, 'y': 2}),
... 'c': {'x': raw({'a': 3, 'y': 4})},
... })
>>> tv4['a'] = {'a': 11, 'y': 22} # key access
>>> tv4
<FastTreeValue 0x7fa9c8c36588>
├── 'a' --> {'a': 11, 'y': 22}
└── 'c' --> <FastTreeValue 0x7fa9c8c369e8>
└── 'x' --> {'a': 3, 'y': 4}
>>> tv4[['a']] = -2 # value access
>>> tv4
<FastTreeValue 0x7fa9c8c36588>
├── 'a' --> {'a': -2, 'y': 22}
└── 'c' --> <FastTreeValue 0x7fa9c8c369e8>
└── 'x' --> {'a': -2, 'y': 4}
"""
TreeValue.__setitem__(self, key, value)
@method_treelize()
def _delitem_extern(self_, key):
del self_[key]
def __delitem__(self, key):
"""
Overview:
Delete item of tree values.
Examples:
>>> from treevalue import FastTreeValue, raw
>>> t1 = FastTreeValue({'a': [1, 2], 'b': [2, 3], 'x': {'c': [3, 4], 'd': [4, 5]}})
>>> del t1[0]
>>> t1
<FastTreeValue 0x7fa9c8c366a0>
├── 'a' --> [2]
├── 'b' --> [3]
└── 'x' --> <FastTreeValue 0x7fa9c8a7b0b8>
├── 'c' --> [4]
└── 'd' --> [5]
>>> del t1['b']
>>> t1
<FastTreeValue 0x7fa9c8c366a0>
├── 'a' --> [2]
└── 'x' --> <FastTreeValue 0x7fa9c8a7b0b8>
├── 'c' --> [4]
└── 'd' --> [5]
.. note::
If you need to delete the string items from the node values, you can use double bracket. \
For example
>>> from treevalue import FastTreeValue, raw
>>> tv4 = FastTreeValue({
... 'a': raw({'a': 1, 'y': 2}),
... 'c': {'x': raw({'a': 3, 'y': 4})},
... 'g': {'x': raw({'a': 31, 'y': 42})},
... })
>>> del tv4['g'] # key delete
>>> tv4
<FastTreeValue 0x7fa9c8c36978>
├── 'a' --> {'a': 1, 'y': 2}
└── 'c' --> <FastTreeValue 0x7fa9c8c36d30>
└── 'x' --> {'a': 3, 'y': 4}
>>> del tv4[['a']] # value delete
>>> tv4
<FastTreeValue 0x7fa9c8c36978>
├── 'a' --> {'y': 2}
└── 'c' --> <FastTreeValue 0x7fa9c8c36d30>
└── 'x' --> {'y': 4}
"""
return TreeValue.__delitem__(self, key)
@_decorate_treelize
def __call__(self_, *args, **kwargs):
"""
Overview:
Call of tree values.
Example:
>>> class Container:
>>> def __init__(self, value):
>>> self.__value = value
>>>
>>> def append(self, v):
>>> return self.__value + v
>>>
>>> t = FastTreeValue({'a': Container(1), 'b': Container(2)})
>>> t.append(2) # FastTreeValue({'a': 3, 'b': 4})
>>> t.append(FastTreeValue({'a': 10, 'b': 20})) # FastTreeValue({'a': 11, 'b': 22})
"""
return self_(*args, **kwargs)
return _GeneralTreeValue