Basic Usage

In this part, basic usages of TreeValue will be introduced one by one with sample code and graph to explain them.

Create a tree

You can easily create a tree value object based on FastTreeValue.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from treevalue import FastTreeValue

t = FastTreeValue({
    'a': 1,
    'b': 2.3,
    'x': {
        'c': 'str',
        'd': [1, 2, None],
        'e': b'bytes',
    }
})

if __name__ == '__main__':
    print(t)

The result should be

1
2
3
4
5
6
7
<FastTreeValue 0x7f36d15ff6d0>
├── 'a' --> 1
├── 'b' --> 2.3
└── 'x' --> <FastTreeValue 0x7f36d15ff940>
    ├── 'c' --> 'str'
    ├── 'd' --> [1, 2, None]
    └── 'e' --> b'bytes'

Edit the tree

After the tree is created, you can access and edit it with __getattr__, __setattr__ and __delattr__.

 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
import os

from treevalue import FastTreeValue

if __name__ == '__main__':
    t = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
    print("Original tree:", t, sep=os.linesep)

    # Get values
    print("Value of t.a: ", t.a)
    print("Value of t.x.c:", t.x.c)
    print("Value of t.x:", t.x, sep=os.linesep)

    # Set values
    t.a = 233
    print("Value after t.a = 233:", t, sep=os.linesep)
    t.x.d = -1
    print("Value after t.x.d = -1:", t, sep=os.linesep)
    t.x = FastTreeValue({'e': 5, 'f': 6})
    print("Value after t.x = FastTreeValue({'e': 5, 'f': 6}):", t, sep=os.linesep)
    t.x.g = {'e': 5, 'f': 6}
    print("Value after t.x.g = {'e': 5, 'f': 6}:", t, sep=os.linesep)

    # Delete values
    del t.x.g
    print("Value after del t.x.g:", t, sep=os.linesep)

The result should be

 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
Original tree:
<FastTreeValue 0x7fe646d406d0>
├── 'a' --> 1
├── 'b' --> 2
└── 'x' --> <FastTreeValue 0x7fe646d40940>
    ├── 'c' --> 3
    └── 'd' --> 4

Value of t.a:  1
Value of t.x.c: 3
Value of t.x:
<FastTreeValue 0x7fe646d40940>
├── 'c' --> 3
└── 'd' --> 4

Value after t.a = 233:
<FastTreeValue 0x7fe646d406d0>
├── 'a' --> 233
├── 'b' --> 2
└── 'x' --> <FastTreeValue 0x7fe646d40940>
    ├── 'c' --> 3
    └── 'd' --> 4

Value after t.x.d = -1:
<FastTreeValue 0x7fe646d406d0>
├── 'a' --> 233
├── 'b' --> 2
└── 'x' --> <FastTreeValue 0x7fe646d40940>
    ├── 'c' --> 3
    └── 'd' --> -1

Value after t.x = FastTreeValue({'e': 5, 'f': 6}):
<FastTreeValue 0x7fe646d406d0>
├── 'a' --> 233
├── 'b' --> 2
└── 'x' --> <FastTreeValue 0x7fe6469aee50>
    ├── 'e' --> 5
    └── 'f' --> 6

Value after t.x.g = {'e': 5, 'f': 6}:
<FastTreeValue 0x7fe646d406d0>
├── 'a' --> 233
├── 'b' --> 2
└── 'x' --> <FastTreeValue 0x7fe6469aee50>
    ├── 'e' --> 5
    ├── 'f' --> 6
    └── 'g' --> {'e': 5, 'f': 6}

Value after del t.x.g:
<FastTreeValue 0x7fe646d406d0>
├── 'a' --> 233
├── 'b' --> 2
└── 'x' --> <FastTreeValue 0x7fe6469aee50>
    ├── 'e' --> 5
    └── 'f' --> 6

The values on the tree has been changed or deleted properly. And the full life circle of the tree t is like below.

../../_images/edit_tree_1.gv.svg../../_images/edit_tree_2.gv.svg

Do index or slice calculation on the tree

Index and slice index operation can be applied all once, like the example below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import os

from treevalue import FastTreeValue

if __name__ == '__main__':
    t = FastTreeValue({
        'a': [1, 2, 3],
        'b': [4, 9, 16],
        'x': {
            'c': [11, 13, 17],
            'd': [-2, -4, -8]
        }
    })
    print("Result of t[0]:", t[0], sep=os.linesep)  # __getitem__ operator
    print("Result of t[::-1]:", t[::-1], sep=os.linesep)
    print("Result of t.x[1:]:", t.x[1:], sep=os.linesep)

The result should be

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
Result of t[0]:
<FastTreeValue 0x7f4e20abddf0>
├── 'a' --> 1
├── 'b' --> 4
└── 'x' --> <FastTreeValue 0x7f4e20a60a30>
    ├── 'c' --> 11
    └── 'd' --> -2

Result of t[::-1]:
<FastTreeValue 0x7f4e20725e50>
├── 'a' --> [3, 2, 1]
├── 'b' --> [16, 9, 4]
└── 'x' --> <FastTreeValue 0x7f4e20a60a30>
    ├── 'c' --> [17, 13, 11]
    └── 'd' --> [-8, -4, -2]

Result of t.x[1:]:
<FastTreeValue 0x7f4e20ad7370>
├── 'c' --> [13, 17]
└── 'd' --> [-4, -8]

The structures oof the trees is like the graph below.

../../_images/index_operation.gv.svg../../_images/slice_index_operation.gv.svg

Do calculation on the tree

Common calculation is supported in treevalue.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import os

from treevalue import FastTreeValue

if __name__ == '__main__':
    t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
    t2 = FastTreeValue({'a': 3, 'b': 7, 'x': {'c': 14, 'd': -5}})

    print("Result of t1 + t2:", t1 + t2, sep=os.linesep)  # __add__ operator
    print("Result of t1 - t2:", t1 - t2, sep=os.linesep)  # __sub__ operator
    print("Result of t1 ^ t2:", t1 ^ t2, sep=os.linesep)  # __xor__ operator
    print("Result of t1 + t2 * (-4 + t1 ** t2)", t1 + t2 * (-4 + t1 ** -t2))  # mathematics calculation

The result should be

 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
Result of t1 + t2:
<FastTreeValue 0x7f5cdeb14df0>
├── 'a' --> 4
├── 'b' --> 9
└── 'x' --> <FastTreeValue 0x7f5cdea92a30>
    ├── 'c' --> 17
    └── 'd' --> -1

Result of t1 - t2:
<FastTreeValue 0x7f5cdeb2e370>
├── 'a' --> -2
├── 'b' --> -5
└── 'x' --> <FastTreeValue 0x7f5cdea92a30>
    ├── 'c' --> -11
    └── 'd' --> 9

Result of t1 ^ t2:
<FastTreeValue 0x7f5cdeb2e0d0>
├── 'a' --> 2
├── 'b' --> 5
└── 'x' --> <FastTreeValue 0x7f5cdeb2e370>
    ├── 'c' --> 13
    └── 'd' --> -1

Result of t1 + t2 * (-4 + t1 ** t2) <FastTreeValue 0x7f5cdeb2e0d0>
├── 'a' --> -8.0
├── 'b' --> -25.9453125
└── 'x' --> <FastTreeValue 0x7f5cdeb2e370>
    ├── 'c' --> -52.999997072947785
    └── 'd' --> -5096

The values is processed one to one between the tree. The structures of the trees involved in __add__ calculation is like below.

../../_images/calculation_add.gv.svg../../_images/calculation_sub_and_xor.gv.svg

Actually, More common operators are supported in treevalue.

Note

In newer versions of treevalue, self operations are supported like the code below.

import os

from treevalue import FastTreeValue

if __name__ == '__main__':
    t1 = FastTreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}})
    t2 = FastTreeValue({'a': 3, 'b': 7, 'x': {'c': 14, 'd': -5}})

    print('t1:', t1, sep=os.linesep)
    print('t2:', t2, sep=os.linesep)
    print('t1 + t2:', t1 + t2, sep=os.linesep)
    _original_ids = (id(t1), id(t2))
    print()

    t1 += t2
    print('After t1 += t2')
    print('t1:', t1, sep=os.linesep)
    print('t2:', t2, sep=os.linesep)
    assert (id(t1), id(t2)) == _original_ids

The output should be

t1:
<FastTreeValue 0x7f44c35006d0>
├── 'a' --> 1
├── 'b' --> 2
└── 'x' --> <FastTreeValue 0x7f44c3500940>
    ├── 'c' --> 3
    └── 'd' --> 4

t2:
<FastTreeValue 0x7f44c316ee50>
├── 'a' --> 3
├── 'b' --> 7
└── 'x' --> <FastTreeValue 0x7f44c316ee20>
    ├── 'c' --> 14
    └── 'd' --> -5

t1 + t2:
<FastTreeValue 0x7f44c35ee370>
├── 'a' --> 4
├── 'b' --> 9
└── 'x' --> <FastTreeValue 0x7f44c35ee0d0>
    ├── 'c' --> 17
    └── 'd' --> -1


After t1 += t2
t1:
<FastTreeValue 0x7f44c35006d0>
├── 'a' --> 4
├── 'b' --> 9
└── 'x' --> <FastTreeValue 0x7f44c3500940>
    ├── 'c' --> 17
    └── 'd' --> -1

t2:
<FastTreeValue 0x7f44c316ee50>
├── 'a' --> 3
├── 'b' --> 7
└── 'x' --> <FastTreeValue 0x7f44c316ee20>
    ├── 'c' --> 14
    └── 'd' --> -5

We can see that when t1 + t2 is called, a new tree with the sums will be created without t1’s change, but when t1 += t2 is called, the values in t1 will be replaced to the sums.

Make function tree supported

Sometimes we need to do some complex calculation which are not able to be represented by raw operators.

In this situation, we can wrap the common function to tree supported function like the code below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os

from treevalue import FastTreeValue, func_treelize


@func_treelize()
def gcd(a, b):  # GCD calculation
    while True:
        r = a % b
        a, b = b, r
        if r == 0:
            break

    return a


if __name__ == '__main__':
    t1 = FastTreeValue({'a': 2, 'b': 30, 'x': {'c': 4, 'd': 9}})
    t2 = FastTreeValue({'a': 4, 'b': 48, 'x': {'c': 6, 'd': 54}})

    print("Result of gcd(t1, t2):", gcd(t1, t2), sep=os.linesep)
    print("Result of gcd(12, 9):", gcd(12, 9))

The result should be

1
2
3
4
5
6
7
8
9
Result of gcd(t1, t2):
<TreeValue 0x7f60cced7370>
├── 'a' --> 2
├── 'b' --> 6
└── 'x' --> <TreeValue 0x7f60cce60a30>
    ├── 'c' --> 2
    └── 'd' --> 9

Result of gcd(12, 9): 3

Luckily, the wrapped function can still used as the original function as well.

The structure of the trees in this part is like below.

../../_images/tree_support_1.gv.svg../../_images/tree_support_2.gv.svg

Besides, the func_treelize function will never change the original logical properties of the original function. In the example below, the calculation with original values instead of usage of the trees can be processed properly with the result of the primitive value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from treevalue import func_treelize


@func_treelize()
def gcd(a, b):  # GCD calculation
    while True:
        r = a % b
        a, b = b, r
        if r == 0:
            break

    return a


if __name__ == '__main__':
    print("gcd(6, 8):", gcd(6, 8))
    print("gcd(900, 768):", gcd(900, 768))

The output should be like below, the gcd function can still support the greatest common divisor of the primitive integers.

1
2
gcd(6, 8): 2
gcd(900, 768): 12

For further information of how the tree-supported function works, take a look at How the treelized function works , this note may give you more information.