memory-graph 0.3.86__tar.gz → 0.3.88__tar.gz
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.
- {memory_graph-0.3.86 → memory_graph-0.3.88}/PKG-INFO +59 -16
- {memory_graph-0.3.86 → memory_graph-0.3.88}/README.md +58 -15
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/__init__.py +1 -1
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/extension_numpy.py +13 -2
- memory_graph-0.3.88/memory_graph/extension_pandas.py +142 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/extension_torch.py +10 -1
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph.egg-info/PKG-INFO +59 -16
- {memory_graph-0.3.86 → memory_graph-0.3.88}/pyproject.toml +1 -1
- memory_graph-0.3.86/memory_graph/extension_pandas.py +0 -36
- {memory_graph-0.3.86 → memory_graph-0.3.88}/LICENSE.txt +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/call_stack.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/config.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/config_default.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/config_helpers.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/html_table.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/list_view.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/memory_to_nodes.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/node_base.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/node_key_value.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/node_leaf.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/node_linear.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/node_table.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/sequence.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/slicer.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/slices.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/slices_iterator.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/slices_table_iterator.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/test.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/test_max_graph_depth.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/test_memory_graph.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/test_memory_to_nodes.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/test_sequence.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/test_slicer.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/test_slices.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/test_slices_iterator.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph/utils.py +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph.egg-info/SOURCES.txt +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph.egg-info/dependency_links.txt +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph.egg-info/requires.txt +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/memory_graph.egg-info/top_level.txt +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/setup.cfg +0 -0
- {memory_graph-0.3.86 → memory_graph-0.3.88}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memory_graph
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.88
|
|
4
4
|
Summary: Teaching tool and debugging aid in context of references, mutable data types, and shallow and deep copy.
|
|
5
5
|
Author-email: Bas Terwijn <bterwijn@gmail.com>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -37,10 +37,10 @@ An example Binary Tree data structure:
|
|
|
37
37
|

|
|
38
38
|
Or see it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/bin_tree.py×tep=0.2&play).
|
|
39
39
|
|
|
40
|
-
# Videos
|
|
41
|
-
| [](https://dai.ly/xb2u52e) | [](https://dai.ly/xb2u4eq) |
|
|
42
42
|
|:--:|:--:|
|
|
43
|
-
| [Quick Intro (3:49)](https://
|
|
43
|
+
| [Quick Intro (3:49)](https://dai.ly/xb2u52e) | [Mutability (17:29)](https://dai.ly/xb2u4eq) |
|
|
44
44
|
|
|
45
45
|
# Memory Graph #
|
|
46
46
|
For program understanding and debugging, the [memory_graph](https://github.com/bterwijn/memory_graph) package can visualize your data, supporting many different data types, including but not limited to:
|
|
@@ -131,7 +131,7 @@ A better way to understand what values are shared is to draw a graph using [memo
|
|
|
131
131
|
|
|
132
132
|
[Sliding Puzzle Solver](#sliding-puzzle-solver)
|
|
133
133
|
|
|
134
|
-
[Control
|
|
134
|
+
[Flow Control](#flow-control)
|
|
135
135
|
|
|
136
136
|
[Configuration](#configuration)
|
|
137
137
|
|
|
@@ -393,7 +393,7 @@ The effect of calling `add_one()` is that `b[0]` increases by 1, while `a` is un
|
|
|
393
393
|
|
|
394
394
|
# Data Model Exercises #
|
|
395
395
|
|
|
396
|
-
Now is a good time to practice with these Python Data Model concepts. Here are [some exercises](https://github.com/bterwijn/memory_graph_videos/blob/main/exercises/exercises.md) on references, mutability, copies, and function calls. Also see the programming exercises at [the end of the Mutability video](https://
|
|
396
|
+
Now is a good time to practice with these Python Data Model concepts. Here are [some exercises](https://github.com/bterwijn/memory_graph_videos/blob/main/exercises/exercises.md) on references, mutability, copies, and function calls. Also see the programming exercises at [the end of the Mutability video](https://dai.ly/xb2u4eq?start=891).
|
|
397
397
|
|
|
398
398
|
# Block #
|
|
399
399
|
It is often helpful to temporarily block program execution to inspect the graph. For this we can use the `mg.block()` function:
|
|
@@ -512,7 +512,7 @@ The ```mg.stack()``` doesn't work well in **watch** context in most debuggers be
|
|
|
512
512
|
| [Wing](https://wingware.com/) | `mg.stack_wing()` |
|
|
513
513
|
|
|
514
514
|

|
|
515
|
-
See the [Quick Intro (3:49)](https://
|
|
515
|
+
See the [Quick Intro (3:49)](https://dai.ly/xb2u52e?start=124) video for the setup.
|
|
516
516
|
|
|
517
517
|
## Other Debuggers ##
|
|
518
518
|
For other debuggers, invoke this function within the **watch** context. Then, in the "call_stack.txt" file, identify the slice of functions you wish to include as stack frames in the call stack.
|
|
@@ -765,9 +765,9 @@ A sliding puzzle solver as a challenging example showing how memory_graph deals
|
|
|
765
765
|
|
|
766
766
|

|
|
767
767
|
|
|
768
|
-
# Control
|
|
768
|
+
# Flow Control #
|
|
769
769
|
|
|
770
|
-
Some examples where we focus on the flow of
|
|
770
|
+
Some examples where we focus more on the flow of execution in Python code.
|
|
771
771
|
|
|
772
772
|
## Different Methods ##
|
|
773
773
|
|
|
@@ -869,17 +869,58 @@ sms.send('001122334455', 'Update to Privacy Policy')
|
|
|
869
869
|
```
|
|
870
870
|
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/inheritance.py&breakpoints=35&continues=1&play).
|
|
871
871
|
|
|
872
|
+
## Iterators ##
|
|
873
|
+
|
|
874
|
+
What actually happens when Python executes a for-loop?
|
|
875
|
+
|
|
876
|
+
```python
|
|
877
|
+
for value in container:
|
|
878
|
+
print(value)
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
Behind the scenes, Python uses the [iterator protocol](https://docs.python.org/3/glossary.html#term-iterator):
|
|
882
|
+
|
|
883
|
+
```python
|
|
884
|
+
iterator = iter(container)
|
|
885
|
+
while True:
|
|
886
|
+
try:
|
|
887
|
+
value = next(iterator)
|
|
888
|
+
print(value)
|
|
889
|
+
except StopIteration:
|
|
890
|
+
break
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
- `iter(container)`: creates an iterator.
|
|
894
|
+
- `next(iterator)`: retrieves one value at a time.
|
|
895
|
+
|
|
896
|
+
When there are no more values, the iterator raises StopIteration. The for-loop catches this exception automatically and ends the loop. For containers that support backward iteration, Python also provides:
|
|
897
|
+
|
|
898
|
+
- `reversed(container)`: creates a backward iterator.
|
|
899
|
+
|
|
900
|
+
We can support these operations in our own classes by implementing:
|
|
901
|
+
|
|
902
|
+
```python
|
|
903
|
+
def __iter__(self):
|
|
904
|
+
def __next__(self):
|
|
905
|
+
def __reversed__(self):
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
This provides a powerful abstraction: an algorithm can process values without needing to know how a container stores them internally. The same algorithm can therefore work with lists, sets, dictionaries, linked lists, trees, and many other containers.
|
|
909
|
+
|
|
910
|
+
Run a Linked_List iterator example in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/list_iter.py×tep=0.2&play).
|
|
911
|
+
|
|
912
|
+
|
|
872
913
|
## Decorator ##
|
|
873
914
|
|
|
874
915
|
This example shows the flow of control when using a decorator. A decorator wraps a function and is active before and after the function is called.
|
|
875
916
|
|
|
876
917
|
```python
|
|
877
|
-
def log_call(function):
|
|
918
|
+
def log_call(function): # decorator example
|
|
878
919
|
def wrapper(*args, **kwargs):
|
|
879
|
-
print(f"
|
|
880
|
-
|
|
881
|
-
print(f"
|
|
882
|
-
return
|
|
920
|
+
print(f"log: '{function.__name__}' called with: {args}, {kwargs}")
|
|
921
|
+
return_value = function(*args, **kwargs)
|
|
922
|
+
print(f"log: '{function.__name__}' returned with: {return_value}")
|
|
923
|
+
return return_value
|
|
883
924
|
return wrapper
|
|
884
925
|
|
|
885
926
|
@log_call
|
|
@@ -891,11 +932,11 @@ def calculate_total(price, quantity, rounding=False):
|
|
|
891
932
|
def send_email(receiver, message, sender="support@company.com"):
|
|
892
933
|
print(f"Sending email to:{receiver} from:{sender}, {message}")
|
|
893
934
|
|
|
894
|
-
total = calculate_total(
|
|
935
|
+
total = calculate_total(2.4, 3, rounding=True)
|
|
895
936
|
print(total)
|
|
896
937
|
send_email("alice@example.com", "Your order is ready")
|
|
897
938
|
```
|
|
898
|
-
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/decorator.py&breakpoints=
|
|
939
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/decorator.py&breakpoints=19&continues=1&play).
|
|
899
940
|
|
|
900
941
|
## Exception Handling ##
|
|
901
942
|
|
|
@@ -1030,6 +1071,8 @@ create: 2
|
|
|
1030
1071
|
use: 2
|
|
1031
1072
|
```
|
|
1032
1073
|
|
|
1074
|
+
Lazy evaluation is critical when you want to quickly start to process the first elements of a large stream of data, that may not even fit in your RAM entirely, or process just a section of an infite stream.
|
|
1075
|
+
|
|
1033
1076
|
</td></tr>
|
|
1034
1077
|
</table>
|
|
1035
1078
|
|
|
@@ -17,10 +17,10 @@ An example Binary Tree data structure:
|
|
|
17
17
|

|
|
18
18
|
Or see it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/bin_tree.py×tep=0.2&play).
|
|
19
19
|
|
|
20
|
-
# Videos
|
|
21
|
-
| [](https://dai.ly/xb2u52e) | [](https://dai.ly/xb2u4eq) |
|
|
22
22
|
|:--:|:--:|
|
|
23
|
-
| [Quick Intro (3:49)](https://
|
|
23
|
+
| [Quick Intro (3:49)](https://dai.ly/xb2u52e) | [Mutability (17:29)](https://dai.ly/xb2u4eq) |
|
|
24
24
|
|
|
25
25
|
# Memory Graph #
|
|
26
26
|
For program understanding and debugging, the [memory_graph](https://github.com/bterwijn/memory_graph) package can visualize your data, supporting many different data types, including but not limited to:
|
|
@@ -111,7 +111,7 @@ A better way to understand what values are shared is to draw a graph using [memo
|
|
|
111
111
|
|
|
112
112
|
[Sliding Puzzle Solver](#sliding-puzzle-solver)
|
|
113
113
|
|
|
114
|
-
[Control
|
|
114
|
+
[Flow Control](#flow-control)
|
|
115
115
|
|
|
116
116
|
[Configuration](#configuration)
|
|
117
117
|
|
|
@@ -373,7 +373,7 @@ The effect of calling `add_one()` is that `b[0]` increases by 1, while `a` is un
|
|
|
373
373
|
|
|
374
374
|
# Data Model Exercises #
|
|
375
375
|
|
|
376
|
-
Now is a good time to practice with these Python Data Model concepts. Here are [some exercises](https://github.com/bterwijn/memory_graph_videos/blob/main/exercises/exercises.md) on references, mutability, copies, and function calls. Also see the programming exercises at [the end of the Mutability video](https://
|
|
376
|
+
Now is a good time to practice with these Python Data Model concepts. Here are [some exercises](https://github.com/bterwijn/memory_graph_videos/blob/main/exercises/exercises.md) on references, mutability, copies, and function calls. Also see the programming exercises at [the end of the Mutability video](https://dai.ly/xb2u4eq?start=891).
|
|
377
377
|
|
|
378
378
|
# Block #
|
|
379
379
|
It is often helpful to temporarily block program execution to inspect the graph. For this we can use the `mg.block()` function:
|
|
@@ -492,7 +492,7 @@ The ```mg.stack()``` doesn't work well in **watch** context in most debuggers be
|
|
|
492
492
|
| [Wing](https://wingware.com/) | `mg.stack_wing()` |
|
|
493
493
|
|
|
494
494
|

|
|
495
|
-
See the [Quick Intro (3:49)](https://
|
|
495
|
+
See the [Quick Intro (3:49)](https://dai.ly/xb2u52e?start=124) video for the setup.
|
|
496
496
|
|
|
497
497
|
## Other Debuggers ##
|
|
498
498
|
For other debuggers, invoke this function within the **watch** context. Then, in the "call_stack.txt" file, identify the slice of functions you wish to include as stack frames in the call stack.
|
|
@@ -745,9 +745,9 @@ A sliding puzzle solver as a challenging example showing how memory_graph deals
|
|
|
745
745
|
|
|
746
746
|

|
|
747
747
|
|
|
748
|
-
# Control
|
|
748
|
+
# Flow Control #
|
|
749
749
|
|
|
750
|
-
Some examples where we focus on the flow of
|
|
750
|
+
Some examples where we focus more on the flow of execution in Python code.
|
|
751
751
|
|
|
752
752
|
## Different Methods ##
|
|
753
753
|
|
|
@@ -849,17 +849,58 @@ sms.send('001122334455', 'Update to Privacy Policy')
|
|
|
849
849
|
```
|
|
850
850
|
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/inheritance.py&breakpoints=35&continues=1&play).
|
|
851
851
|
|
|
852
|
+
## Iterators ##
|
|
853
|
+
|
|
854
|
+
What actually happens when Python executes a for-loop?
|
|
855
|
+
|
|
856
|
+
```python
|
|
857
|
+
for value in container:
|
|
858
|
+
print(value)
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
Behind the scenes, Python uses the [iterator protocol](https://docs.python.org/3/glossary.html#term-iterator):
|
|
862
|
+
|
|
863
|
+
```python
|
|
864
|
+
iterator = iter(container)
|
|
865
|
+
while True:
|
|
866
|
+
try:
|
|
867
|
+
value = next(iterator)
|
|
868
|
+
print(value)
|
|
869
|
+
except StopIteration:
|
|
870
|
+
break
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
- `iter(container)`: creates an iterator.
|
|
874
|
+
- `next(iterator)`: retrieves one value at a time.
|
|
875
|
+
|
|
876
|
+
When there are no more values, the iterator raises StopIteration. The for-loop catches this exception automatically and ends the loop. For containers that support backward iteration, Python also provides:
|
|
877
|
+
|
|
878
|
+
- `reversed(container)`: creates a backward iterator.
|
|
879
|
+
|
|
880
|
+
We can support these operations in our own classes by implementing:
|
|
881
|
+
|
|
882
|
+
```python
|
|
883
|
+
def __iter__(self):
|
|
884
|
+
def __next__(self):
|
|
885
|
+
def __reversed__(self):
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
This provides a powerful abstraction: an algorithm can process values without needing to know how a container stores them internally. The same algorithm can therefore work with lists, sets, dictionaries, linked lists, trees, and many other containers.
|
|
889
|
+
|
|
890
|
+
Run a Linked_List iterator example in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/list_iter.py×tep=0.2&play).
|
|
891
|
+
|
|
892
|
+
|
|
852
893
|
## Decorator ##
|
|
853
894
|
|
|
854
895
|
This example shows the flow of control when using a decorator. A decorator wraps a function and is active before and after the function is called.
|
|
855
896
|
|
|
856
897
|
```python
|
|
857
|
-
def log_call(function):
|
|
898
|
+
def log_call(function): # decorator example
|
|
858
899
|
def wrapper(*args, **kwargs):
|
|
859
|
-
print(f"
|
|
860
|
-
|
|
861
|
-
print(f"
|
|
862
|
-
return
|
|
900
|
+
print(f"log: '{function.__name__}' called with: {args}, {kwargs}")
|
|
901
|
+
return_value = function(*args, **kwargs)
|
|
902
|
+
print(f"log: '{function.__name__}' returned with: {return_value}")
|
|
903
|
+
return return_value
|
|
863
904
|
return wrapper
|
|
864
905
|
|
|
865
906
|
@log_call
|
|
@@ -871,11 +912,11 @@ def calculate_total(price, quantity, rounding=False):
|
|
|
871
912
|
def send_email(receiver, message, sender="support@company.com"):
|
|
872
913
|
print(f"Sending email to:{receiver} from:{sender}, {message}")
|
|
873
914
|
|
|
874
|
-
total = calculate_total(
|
|
915
|
+
total = calculate_total(2.4, 3, rounding=True)
|
|
875
916
|
print(total)
|
|
876
917
|
send_email("alice@example.com", "Your order is ready")
|
|
877
918
|
```
|
|
878
|
-
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/decorator.py&breakpoints=
|
|
919
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/decorator.py&breakpoints=19&continues=1&play).
|
|
879
920
|
|
|
880
921
|
## Exception Handling ##
|
|
881
922
|
|
|
@@ -1010,6 +1051,8 @@ create: 2
|
|
|
1010
1051
|
use: 2
|
|
1011
1052
|
```
|
|
1012
1053
|
|
|
1054
|
+
Lazy evaluation is critical when you want to quickly start to process the first elements of a large stream of data, that may not even fit in your RAM entirely, or process just a section of an infite stream.
|
|
1055
|
+
|
|
1013
1056
|
</td></tr>
|
|
1014
1057
|
</table>
|
|
1015
1058
|
|
|
@@ -7,6 +7,7 @@ from memory_graph.node_linear import Node_Linear
|
|
|
7
7
|
from memory_graph.node_table import Node_Table
|
|
8
8
|
|
|
9
9
|
import memory_graph.config as config
|
|
10
|
+
import memory_graph.config_default as config_default
|
|
10
11
|
|
|
11
12
|
def ndarray_to_node(data, ndarray_data):
|
|
12
13
|
dim = len(ndarray_data.shape)
|
|
@@ -34,8 +35,18 @@ def extend_numpy():
|
|
|
34
35
|
config.type_to_node[np.matrix] = lambda data : Node_Table(data, np.asarray(data)) # convert to ndarray to avoid infinite recursion due to index issue
|
|
35
36
|
config.type_to_node[np.ndarray] = lambda data : ndarray_to_node(data, data)
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
config_default.type_to_color_light[np.ndarray] = "hotpink1"
|
|
39
|
+
config_default.type_to_color_light[np.matrix] = "hotpink2"
|
|
40
|
+
|
|
41
|
+
config_default.type_to_color_dark[np.ndarray] = "#652c47"
|
|
42
|
+
config_default.type_to_color_dark[np.matrix] = "#652c47"
|
|
43
|
+
|
|
44
|
+
if not config.color_mode_dark:
|
|
45
|
+
config.type_to_color[np.ndarray] = config_default.type_to_color_light[np.ndarray]
|
|
46
|
+
config.type_to_color[np.matrix] = config_default.type_to_color_light[np.matrix]
|
|
47
|
+
else:
|
|
48
|
+
config.type_to_color[np.ndarray] = config_default.type_to_color_dark[np.ndarray]
|
|
49
|
+
config.type_to_color[np.matrix] = config_default.type_to_color_dark[np.matrix]
|
|
39
50
|
|
|
40
51
|
def unextend_numpy():
|
|
41
52
|
import numpy as np
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
"""Extension to add memory graph configuration for public Pandas values."""
|
|
6
|
+
import reprlib
|
|
7
|
+
|
|
8
|
+
from memory_graph.node_leaf import Node_Leaf
|
|
9
|
+
from memory_graph.node_table import Node_Table
|
|
10
|
+
|
|
11
|
+
import memory_graph.config as config
|
|
12
|
+
import memory_graph.config_default as config_default
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _display_value(value):
|
|
16
|
+
"""Keep object references, but display Pandas scalar values without internals."""
|
|
17
|
+
import pandas as pd
|
|
18
|
+
|
|
19
|
+
if value is pd.NA or value is pd.NaT:
|
|
20
|
+
return str(value)
|
|
21
|
+
if isinstance(value, (pd.Timestamp, pd.Timedelta, pd.Period, pd.Interval)):
|
|
22
|
+
return str(value)
|
|
23
|
+
return value
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _column_name(name, default="value"):
|
|
27
|
+
return default if name is None else str(name)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _dataframe_to_node(data):
|
|
31
|
+
# Unlike .values, itertuples does not coerce all numeric columns to one dtype.
|
|
32
|
+
return Node_Table(
|
|
33
|
+
data,
|
|
34
|
+
[[_display_value(value) for value in row]
|
|
35
|
+
for row in data.itertuples(index=False, name=None)],
|
|
36
|
+
col_names=[str(name) for name in data.columns],
|
|
37
|
+
row_names=[str(name) for name in data.index],
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _series_to_node(data):
|
|
42
|
+
return Node_Table(
|
|
43
|
+
data,
|
|
44
|
+
[[_display_value(value)] for value in data.tolist()],
|
|
45
|
+
col_names=[_column_name(data.name)],
|
|
46
|
+
row_names=[str(name) for name in data.index],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _index_to_node(data):
|
|
51
|
+
# Also handles RangeIndex, DatetimeIndex, TimedeltaIndex and PeriodIndex
|
|
52
|
+
# through config's base-class lookup.
|
|
53
|
+
return Node_Table(
|
|
54
|
+
data,
|
|
55
|
+
[[_display_value(value)] for value in data.tolist()],
|
|
56
|
+
col_names=[_column_name(data.name)],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _multiindex_to_node(data):
|
|
61
|
+
# Use the actual row labels, not .levels (which includes unused labels).
|
|
62
|
+
return Node_Table(
|
|
63
|
+
data,
|
|
64
|
+
[[_display_value(value) for value in row] for row in data.tolist()],
|
|
65
|
+
col_names=[_column_name(name, f"level_{i}")
|
|
66
|
+
for i, name in enumerate(data.names)],
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class _Categorical_Table(Node_Table):
|
|
71
|
+
"""Show values as a table, with category order as compact metadata."""
|
|
72
|
+
|
|
73
|
+
def get_label(self, slices):
|
|
74
|
+
data = self.get_data()
|
|
75
|
+
return (super().get_label(slices)
|
|
76
|
+
+ f"\ncategories={reprlib.repr(data.categories.tolist())}"
|
|
77
|
+
+ f"\nordered={data.ordered}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _categorical_to_node(data):
|
|
81
|
+
return _Categorical_Table(
|
|
82
|
+
data,
|
|
83
|
+
[[_display_value(value)] for value in data.tolist()],
|
|
84
|
+
col_names=[_column_name(getattr(data, "name", None))],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _intervalindex_to_node(data):
|
|
89
|
+
return Node_Table(
|
|
90
|
+
data,
|
|
91
|
+
[[_display_value(left), _display_value(right), data.closed]
|
|
92
|
+
for left, right in zip(data.left.tolist(), data.right.tolist())],
|
|
93
|
+
col_names=["left", "right", "closed"],
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _scalar_to_node(data):
|
|
98
|
+
return Node_Leaf(data, str(data))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _pandas_types():
|
|
102
|
+
"""Keep optional Pandas imports inside the extension, as before."""
|
|
103
|
+
import pandas as pd
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
pd.DataFrame: (_dataframe_to_node, "olivedrab1", "#3a4d13"),
|
|
107
|
+
pd.Series: (_series_to_node, "olivedrab2", "#3a4d13"),
|
|
108
|
+
pd.Index: (_index_to_node, "olivedrab3", "#384d24"),
|
|
109
|
+
pd.MultiIndex: (_multiindex_to_node, "palegreen2", "#244d32"),
|
|
110
|
+
pd.CategoricalIndex: (_categorical_to_node, "darkseagreen2", "#324d39"),
|
|
111
|
+
pd.Categorical: (_categorical_to_node, "darkseagreen2", "#324d39"),
|
|
112
|
+
pd.IntervalIndex: (_intervalindex_to_node, "lightgoldenrod1", "#5c5125"),
|
|
113
|
+
# IndexSlice is an instance, not a public class or a table. Obtain its
|
|
114
|
+
# type from the public singleton instead of importing a private type.
|
|
115
|
+
type(pd.IndexSlice): (
|
|
116
|
+
lambda data: Node_Leaf(data, "pd.IndexSlice (slice helper)"),
|
|
117
|
+
"lightsteelblue1", "#30465c",
|
|
118
|
+
),
|
|
119
|
+
pd.Timestamp: (_scalar_to_node, "olivedrab2", "#3a4d13"),
|
|
120
|
+
pd.Timedelta: (_scalar_to_node, "olivedrab2", "#3a4d13"),
|
|
121
|
+
pd.Period: (_scalar_to_node, "olivedrab2", "#3a4d13"),
|
|
122
|
+
pd.Interval: (_scalar_to_node, "lightgoldenrod1", "#5c5125"),
|
|
123
|
+
type(pd.NA): (_scalar_to_node, "gray85", "#454545"),
|
|
124
|
+
type(pd.NaT): (_scalar_to_node, "gray85", "#454545"),
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def extend_pandas():
|
|
129
|
+
for data_type, (to_node, light, dark) in _pandas_types().items():
|
|
130
|
+
config.type_to_node[data_type] = to_node
|
|
131
|
+
config_default.type_to_color_light[data_type] = light
|
|
132
|
+
config_default.type_to_color_dark[data_type] = dark
|
|
133
|
+
config.type_to_color[data_type] = dark if config.color_mode_dark else light
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def unextend_pandas():
|
|
137
|
+
for data_type in _pandas_types():
|
|
138
|
+
config.type_to_node.pop(data_type, None)
|
|
139
|
+
config.type_to_color.pop(data_type, None)
|
|
140
|
+
# Remove theme defaults too, so changing theme cannot re-enable colors.
|
|
141
|
+
config_default.type_to_color_light.pop(data_type, None)
|
|
142
|
+
config_default.type_to_color_dark.pop(data_type, None)
|
|
@@ -5,12 +5,21 @@
|
|
|
5
5
|
""" Extension to add the memory graph configuration for Pandas types. """
|
|
6
6
|
import memory_graph.extension_numpy as ext_np
|
|
7
7
|
import memory_graph.config as config
|
|
8
|
+
import memory_graph.config_default as config_default
|
|
8
9
|
|
|
9
10
|
def extend_torch():
|
|
10
11
|
import torch
|
|
11
12
|
|
|
12
13
|
config.type_to_node[torch.Tensor] = lambda data : ext_np.ndarray_to_node(data, data.numpy())
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
config_default.type_to_color_light[torch.Tensor] = "darkolivegreen1"
|
|
16
|
+
|
|
17
|
+
config_default.type_to_color_dark[torch.Tensor] = "darkolivegreen"
|
|
18
|
+
|
|
19
|
+
if not config.color_mode_dark:
|
|
20
|
+
config.type_to_color[torch.Tensor] = config_default.type_to_color_light[torch.Tensor]
|
|
21
|
+
else:
|
|
22
|
+
config.type_to_color[torch.Tensor] = config_default.type_to_color_dark[torch.Tensor]
|
|
14
23
|
|
|
15
24
|
def unextend_torch():
|
|
16
25
|
import torch
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memory_graph
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.88
|
|
4
4
|
Summary: Teaching tool and debugging aid in context of references, mutable data types, and shallow and deep copy.
|
|
5
5
|
Author-email: Bas Terwijn <bterwijn@gmail.com>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -37,10 +37,10 @@ An example Binary Tree data structure:
|
|
|
37
37
|

|
|
38
38
|
Or see it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/bin_tree.py×tep=0.2&play).
|
|
39
39
|
|
|
40
|
-
# Videos
|
|
41
|
-
| [](https://dai.ly/xb2u52e) | [](https://dai.ly/xb2u4eq) |
|
|
42
42
|
|:--:|:--:|
|
|
43
|
-
| [Quick Intro (3:49)](https://
|
|
43
|
+
| [Quick Intro (3:49)](https://dai.ly/xb2u52e) | [Mutability (17:29)](https://dai.ly/xb2u4eq) |
|
|
44
44
|
|
|
45
45
|
# Memory Graph #
|
|
46
46
|
For program understanding and debugging, the [memory_graph](https://github.com/bterwijn/memory_graph) package can visualize your data, supporting many different data types, including but not limited to:
|
|
@@ -131,7 +131,7 @@ A better way to understand what values are shared is to draw a graph using [memo
|
|
|
131
131
|
|
|
132
132
|
[Sliding Puzzle Solver](#sliding-puzzle-solver)
|
|
133
133
|
|
|
134
|
-
[Control
|
|
134
|
+
[Flow Control](#flow-control)
|
|
135
135
|
|
|
136
136
|
[Configuration](#configuration)
|
|
137
137
|
|
|
@@ -393,7 +393,7 @@ The effect of calling `add_one()` is that `b[0]` increases by 1, while `a` is un
|
|
|
393
393
|
|
|
394
394
|
# Data Model Exercises #
|
|
395
395
|
|
|
396
|
-
Now is a good time to practice with these Python Data Model concepts. Here are [some exercises](https://github.com/bterwijn/memory_graph_videos/blob/main/exercises/exercises.md) on references, mutability, copies, and function calls. Also see the programming exercises at [the end of the Mutability video](https://
|
|
396
|
+
Now is a good time to practice with these Python Data Model concepts. Here are [some exercises](https://github.com/bterwijn/memory_graph_videos/blob/main/exercises/exercises.md) on references, mutability, copies, and function calls. Also see the programming exercises at [the end of the Mutability video](https://dai.ly/xb2u4eq?start=891).
|
|
397
397
|
|
|
398
398
|
# Block #
|
|
399
399
|
It is often helpful to temporarily block program execution to inspect the graph. For this we can use the `mg.block()` function:
|
|
@@ -512,7 +512,7 @@ The ```mg.stack()``` doesn't work well in **watch** context in most debuggers be
|
|
|
512
512
|
| [Wing](https://wingware.com/) | `mg.stack_wing()` |
|
|
513
513
|
|
|
514
514
|

|
|
515
|
-
See the [Quick Intro (3:49)](https://
|
|
515
|
+
See the [Quick Intro (3:49)](https://dai.ly/xb2u52e?start=124) video for the setup.
|
|
516
516
|
|
|
517
517
|
## Other Debuggers ##
|
|
518
518
|
For other debuggers, invoke this function within the **watch** context. Then, in the "call_stack.txt" file, identify the slice of functions you wish to include as stack frames in the call stack.
|
|
@@ -765,9 +765,9 @@ A sliding puzzle solver as a challenging example showing how memory_graph deals
|
|
|
765
765
|
|
|
766
766
|

|
|
767
767
|
|
|
768
|
-
# Control
|
|
768
|
+
# Flow Control #
|
|
769
769
|
|
|
770
|
-
Some examples where we focus on the flow of
|
|
770
|
+
Some examples where we focus more on the flow of execution in Python code.
|
|
771
771
|
|
|
772
772
|
## Different Methods ##
|
|
773
773
|
|
|
@@ -869,17 +869,58 @@ sms.send('001122334455', 'Update to Privacy Policy')
|
|
|
869
869
|
```
|
|
870
870
|
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/inheritance.py&breakpoints=35&continues=1&play).
|
|
871
871
|
|
|
872
|
+
## Iterators ##
|
|
873
|
+
|
|
874
|
+
What actually happens when Python executes a for-loop?
|
|
875
|
+
|
|
876
|
+
```python
|
|
877
|
+
for value in container:
|
|
878
|
+
print(value)
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
Behind the scenes, Python uses the [iterator protocol](https://docs.python.org/3/glossary.html#term-iterator):
|
|
882
|
+
|
|
883
|
+
```python
|
|
884
|
+
iterator = iter(container)
|
|
885
|
+
while True:
|
|
886
|
+
try:
|
|
887
|
+
value = next(iterator)
|
|
888
|
+
print(value)
|
|
889
|
+
except StopIteration:
|
|
890
|
+
break
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
- `iter(container)`: creates an iterator.
|
|
894
|
+
- `next(iterator)`: retrieves one value at a time.
|
|
895
|
+
|
|
896
|
+
When there are no more values, the iterator raises StopIteration. The for-loop catches this exception automatically and ends the loop. For containers that support backward iteration, Python also provides:
|
|
897
|
+
|
|
898
|
+
- `reversed(container)`: creates a backward iterator.
|
|
899
|
+
|
|
900
|
+
We can support these operations in our own classes by implementing:
|
|
901
|
+
|
|
902
|
+
```python
|
|
903
|
+
def __iter__(self):
|
|
904
|
+
def __next__(self):
|
|
905
|
+
def __reversed__(self):
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
This provides a powerful abstraction: an algorithm can process values without needing to know how a container stores them internally. The same algorithm can therefore work with lists, sets, dictionaries, linked lists, trees, and many other containers.
|
|
909
|
+
|
|
910
|
+
Run a Linked_List iterator example in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/list_iter.py×tep=0.2&play).
|
|
911
|
+
|
|
912
|
+
|
|
872
913
|
## Decorator ##
|
|
873
914
|
|
|
874
915
|
This example shows the flow of control when using a decorator. A decorator wraps a function and is active before and after the function is called.
|
|
875
916
|
|
|
876
917
|
```python
|
|
877
|
-
def log_call(function):
|
|
918
|
+
def log_call(function): # decorator example
|
|
878
919
|
def wrapper(*args, **kwargs):
|
|
879
|
-
print(f"
|
|
880
|
-
|
|
881
|
-
print(f"
|
|
882
|
-
return
|
|
920
|
+
print(f"log: '{function.__name__}' called with: {args}, {kwargs}")
|
|
921
|
+
return_value = function(*args, **kwargs)
|
|
922
|
+
print(f"log: '{function.__name__}' returned with: {return_value}")
|
|
923
|
+
return return_value
|
|
883
924
|
return wrapper
|
|
884
925
|
|
|
885
926
|
@log_call
|
|
@@ -891,11 +932,11 @@ def calculate_total(price, quantity, rounding=False):
|
|
|
891
932
|
def send_email(receiver, message, sender="support@company.com"):
|
|
892
933
|
print(f"Sending email to:{receiver} from:{sender}, {message}")
|
|
893
934
|
|
|
894
|
-
total = calculate_total(
|
|
935
|
+
total = calculate_total(2.4, 3, rounding=True)
|
|
895
936
|
print(total)
|
|
896
937
|
send_email("alice@example.com", "Your order is ready")
|
|
897
938
|
```
|
|
898
|
-
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/decorator.py&breakpoints=
|
|
939
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/decorator.py&breakpoints=19&continues=1&play).
|
|
899
940
|
|
|
900
941
|
## Exception Handling ##
|
|
901
942
|
|
|
@@ -1030,6 +1071,8 @@ create: 2
|
|
|
1030
1071
|
use: 2
|
|
1031
1072
|
```
|
|
1032
1073
|
|
|
1074
|
+
Lazy evaluation is critical when you want to quickly start to process the first elements of a large stream of data, that may not even fit in your RAM entirely, or process just a section of an infite stream.
|
|
1075
|
+
|
|
1033
1076
|
</td></tr>
|
|
1034
1077
|
</table>
|
|
1035
1078
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "memory_graph"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.88"
|
|
8
8
|
description = "Teaching tool and debugging aid in context of references, mutable data types, and shallow and deep copy."
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Bas Terwijn", email = "bterwijn@gmail.com"}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# This file is part of memory_graph.
|
|
2
|
-
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
-
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
-
|
|
5
|
-
""" Extension to add the memory graph configuration for Pandas types. """
|
|
6
|
-
from memory_graph.node_linear import Node_Linear
|
|
7
|
-
from memory_graph.node_table import Node_Table
|
|
8
|
-
|
|
9
|
-
import memory_graph.config as config
|
|
10
|
-
|
|
11
|
-
def extend_pandas():
|
|
12
|
-
import pandas as pd
|
|
13
|
-
|
|
14
|
-
config.type_to_node[pd.DataFrame] = lambda data : (
|
|
15
|
-
Node_Table(data,
|
|
16
|
-
data.values.tolist(),
|
|
17
|
-
col_names = data.columns.tolist(),
|
|
18
|
-
row_names = [ str(i) for i in data.index.tolist()]
|
|
19
|
-
)
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
config.type_to_node[pd.Series] = lambda data : (
|
|
23
|
-
Node_Linear(data, data.tolist())
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
config.type_to_color[pd.DataFrame] = "olivedrab1"
|
|
27
|
-
config.type_to_color[pd.Series] = "olivedrab2"
|
|
28
|
-
|
|
29
|
-
def unextend_pandas():
|
|
30
|
-
import pandas as pd
|
|
31
|
-
|
|
32
|
-
del config.type_to_node[pd.DataFrame]
|
|
33
|
-
del config.type_to_node[pd.Series]
|
|
34
|
-
|
|
35
|
-
del config.type_to_color[pd.DataFrame]
|
|
36
|
-
del config.type_to_color[pd.Series]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|