memory-graph 0.3.15__tar.gz → 0.3.17__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.15/memory_graph.egg-info → memory_graph-0.3.17}/PKG-INFO +41 -50
- {memory_graph-0.3.15 → memory_graph-0.3.17}/README.md +40 -49
- memory_graph-0.3.17/TODO.txt +9 -0
- memory_graph-0.3.17/images/.ipynb_checkpoints/jupyter_example-checkpoint.ipynb +85 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/add_one.py +1 -1
- memory_graph-0.3.17/images/avltree.py~ +41 -0
- memory_graph-0.3.17/images/avltree_fail.gv +26 -0
- memory_graph-0.3.17/images/bin_tree.png +0 -0
- memory_graph-0.3.17/images/bin_tree.py +38 -0
- memory_graph-0.3.17/images/bin_tree2.py +31 -0
- memory_graph-0.3.17/images/bin_tree2.py~ +31 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/debugging01.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/debugging02.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/debugging03.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/debugging04.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/debugging05.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/debugging06.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/factorial.py +5 -5
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/factorial01.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/factorial02.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/factorial03.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/factorial04.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/factorial05.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/factorial06.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/factorial07.png +0 -0
- memory_graph-0.3.17/images/memory_graph.gv.pdf +0 -0
- memory_graph-0.3.17/images/memory_graph.pdf +0 -0
- memory_graph-0.3.17/images/my_graph.gv +35 -0
- memory_graph-0.3.17/images/my_graph.pdf +0 -0
- memory_graph-0.3.17/images/not_node_types.py~ +9 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set.py +2 -2
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set1.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set10.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set11.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set12.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set13.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set14.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set15.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set16.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set17.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set18.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set19.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set2.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set20.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set21.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set22.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set3.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set4.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set5.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set6.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set7.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set8.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set9.png +0 -0
- memory_graph-0.3.17/install.txt +31 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/__init__.py +24 -10
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/test.py +2 -2
- {memory_graph-0.3.15 → memory_graph-0.3.17/memory_graph.egg-info}/PKG-INFO +41 -50
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph.egg-info/SOURCES.txt +17 -5
- {memory_graph-0.3.15 → memory_graph-0.3.17}/setup.py +1 -1
- memory_graph-0.3.17/src/auto_memory_graph.py +21 -0
- memory_graph-0.3.17/src/jupyter_example.ipynb +85 -0
- memory_graph-0.3.17/src/pyodide.html +182 -0
- memory_graph-0.3.17/uml/memory_graph.uxf +322 -0
- memory_graph-0.3.15/images/bin_tree.png +0 -0
- memory_graph-0.3.15/images/bin_tree.py +0 -47
- memory_graph-0.3.15/images/cmp.sh +0 -3
- memory_graph-0.3.15/images/cmp.sh~ +0 -1
- memory_graph-0.3.15/images/name_rebinding.py~ +0 -7
- memory_graph-0.3.15/memory_graph/t.py +0 -6
- {memory_graph-0.3.15 → memory_graph-0.3.17}/LICENSE.txt +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/MANIFEST.in +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/add_one.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/avltree.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/avltree_base.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/avltree_dir.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/avltree_fail.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/avltree_key_value.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/avltree_linear.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/avltree_table.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/copies.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/copies.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/copy_method.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/copy_method.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/create_gif.sh +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/create_images.sh +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/debug_vscode.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/debugging.gif +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/debugging.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/extension_numpy.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/extension_numpy.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/extension_pandas.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/extension_pandas.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/factorial.gif +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/hash_set.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/hash_set.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/highlight.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/highlight.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/immutable.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/immutable1.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/immutable2.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/ipython.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/jupyter_example.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/linked_list.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/linked_list.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/many_types.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/many_types.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/mutable.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/mutable1.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/mutable2.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/name_rebinding.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/not_node_types.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/not_node_types1.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/not_node_types2.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/power_set.gif +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/pyodide.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/rebinding1.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/rebinding2.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/images/uva.png +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/config.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/config_default.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/config_helpers.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/extension_numpy.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/extension_pandas.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/html_table.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/list_view.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/memory_to_nodes.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/node_base.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/node_key_value.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/node_linear.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/node_table.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/sequence.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/slicer.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/slices.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/slices_iterator.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/slices_table_iterator.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/test_max_graph_depth.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/test_memory_graph.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/test_memory_to_nodes.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/test_sequence.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/test_slicer.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/test_slices.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/test_slices_iterator.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph/utils.py +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph.egg-info/dependency_links.txt +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph.egg-info/requires.txt +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/memory_graph.egg-info/top_level.txt +0 -0
- {memory_graph-0.3.15 → memory_graph-0.3.17}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: memory_graph
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17
|
|
4
4
|
Summary: Generate intuitive graphs of your Python data, great for debugging and understanding complex relationships.
|
|
5
5
|
Home-page: https://github.com/bterwijn/memory_graph
|
|
6
6
|
Author: Bas Terwijn
|
|
@@ -199,7 +199,7 @@ mg.show(locals())
|
|
|
199
199
|
|
|
200
200
|
|
|
201
201
|
### Custom Copy ###
|
|
202
|
-
We can write our own custom copy function or method in case the three "copy" options don't do what we want. For example, in the code below the copy() method of My_Class copies the `digits` but shares the `letters` between two objects.
|
|
202
|
+
We can write our own custom copy function or method in case the three standard "copy" options don't do what we want. For example, in the code below the copy() method of My_Class copies the `digits` but shares the `letters` between two objects.
|
|
203
203
|
|
|
204
204
|
```python
|
|
205
205
|
import memory_graph as mg
|
|
@@ -242,7 +242,7 @@ mg.render(locals(), 'rebinding2.png')
|
|
|
242
242
|
| rebinding1.png | rebinding2.png |
|
|
243
243
|
|
|
244
244
|
## Call Stack ##
|
|
245
|
-
The `mg.
|
|
245
|
+
The `mg.stack()` function retrieves the entire call stack, including the local variables for each function on the stack. This enables us to visualize the local variables across all active functions simultaneously. By examining the graph, we can determine whether any local variables from different functions share data. For instance, consider the function `add_one()` which adds the value `1` to each of its parameters `a`, `b`, and `c`.
|
|
246
246
|
|
|
247
247
|
```python
|
|
248
248
|
import memory_graph as mg
|
|
@@ -251,7 +251,7 @@ def add_one(a, b, c):
|
|
|
251
251
|
a += [1]
|
|
252
252
|
b += (1,)
|
|
253
253
|
c += [1]
|
|
254
|
-
mg.show(mg.
|
|
254
|
+
mg.show(mg.stack())
|
|
255
255
|
|
|
256
256
|
a = [4, 3, 2]
|
|
257
257
|
b = (4, 3, 2)
|
|
@@ -295,9 +295,9 @@ import memory_graph as mg
|
|
|
295
295
|
def factorial(n):
|
|
296
296
|
if n==0:
|
|
297
297
|
return 1
|
|
298
|
-
mg.block(mg.show, mg.
|
|
298
|
+
mg.block(mg.show, mg.stack())
|
|
299
299
|
result = n * factorial(n-1)
|
|
300
|
-
mg.block(mg.show, mg.
|
|
300
|
+
mg.block(mg.show, mg.stack())
|
|
301
301
|
return result
|
|
302
302
|
|
|
303
303
|
print(factorial(3))
|
|
@@ -314,7 +314,7 @@ A more interesting recursive example that shows sharing of data is power_set().
|
|
|
314
314
|
import memory_graph as mg
|
|
315
315
|
|
|
316
316
|
def get_subsets(subsets, data, i, subset):
|
|
317
|
-
mg.block(mg.show, mg.
|
|
317
|
+
mg.block(mg.show, mg.stack())
|
|
318
318
|
if i == len(data):
|
|
319
319
|
subsets.append(subset.copy())
|
|
320
320
|
return
|
|
@@ -322,7 +322,7 @@ def get_subsets(subsets, data, i, subset):
|
|
|
322
322
|
get_subsets(subsets, data, i+1, subset) # do include data[i]
|
|
323
323
|
subset.pop()
|
|
324
324
|
get_subsets(subsets, data, i+1, subset) # don't include data[i]
|
|
325
|
-
mg.block(mg.show, mg.
|
|
325
|
+
mg.block(mg.show, mg.stack())
|
|
326
326
|
|
|
327
327
|
def power_set(data):
|
|
328
328
|
subsets = []
|
|
@@ -346,13 +346,13 @@ mg.render(locals(), "my_graph.pdf")
|
|
|
346
346
|
as a *watch* in a debugger tool such as the integrated debugger in Visual Studio Code. Then open the "my_graph.pdf" output file to continuously see all the local variables while debugging. This avoids having to add any memory_graph `show()` or `render()` calls to your code.
|
|
347
347
|
|
|
348
348
|
### Call Stack in Watch Context ###
|
|
349
|
-
The ```mg.
|
|
349
|
+
The ```mg.stack()``` doesn't work well in *watch* context in most debuggers because debuggers introduce additional stack frames that cause problems. Use these alternative functions for various debuggers to filter out these problematic stack frames:
|
|
350
350
|
|
|
351
351
|
| debugger | function to get the call stack |
|
|
352
352
|
|:---|:---|
|
|
353
|
-
| **pdb, pudb** | `mg.
|
|
354
|
-
| **Visual Studio Code** | `mg.
|
|
355
|
-
| **Pycharm** | `mg.
|
|
353
|
+
| **pdb, pudb** | `mg.stack_pdb()` |
|
|
354
|
+
| **Visual Studio Code** | `mg.stack_vscode()` |
|
|
355
|
+
| **Pycharm** | `mg.stack_pycharm()` |
|
|
356
356
|
|
|
357
357
|

|
|
358
358
|
|
|
@@ -363,7 +363,7 @@ mg.save_call_stack("call_stack.txt")
|
|
|
363
363
|
```
|
|
364
364
|
Choose 'after' and 'up_to' what function you want to slice and then call this function to get the desired call stack:
|
|
365
365
|
```
|
|
366
|
-
mg.
|
|
366
|
+
mg.stack_after_up_to(after_function, up_to_function="<module>")
|
|
367
367
|
```
|
|
368
368
|
|
|
369
369
|
### Debugging without Debugger Tool ###
|
|
@@ -373,13 +373,13 @@ To simplify debugging without a debugger tool, we offer these alias functions th
|
|
|
373
373
|
| alias | purpose | function call |
|
|
374
374
|
|:---|:---|:---|
|
|
375
375
|
| `mg.sl()` | **s**how **l**ocal variables | `mg.show(locals())` |
|
|
376
|
-
| `mg.ss()` | **s**how the call **s**tack | `mg.show(mg.
|
|
376
|
+
| `mg.ss()` | **s**how the call **s**tack | `mg.show(mg.stack())` |
|
|
377
377
|
| `mg.bsl()` | **b**lock after **s**howing **l**ocal variables | `mg.block(mg.show, locals())` |
|
|
378
|
-
| `mg.bss()` | **b**lock after **s**howing the call **s**tack | `mg.block(mg.show, mg.
|
|
378
|
+
| `mg.bss()` | **b**lock after **s**howing the call **s**tack | `mg.block(mg.show, mg.stack())` |
|
|
379
379
|
| `mg.rl()` | **r**ender **l**ocal variables | `mg.render(locals())` |
|
|
380
|
-
| `mg.rs()` | **r**ender the call **s**tack | `mg.render(mg.
|
|
380
|
+
| `mg.rs()` | **r**ender the call **s**tack | `mg.render(mg.stack())` |
|
|
381
381
|
| `mg.brl()` | **b**lock after **r**endering **l**ocal variables | `mg.block(mg.render, locals())` |
|
|
382
|
-
| `mg.brs()` | **b**lock after **r**endering the call **s**tack | `mg.block(mg.render, mg.
|
|
382
|
+
| `mg.brs()` | **b**lock after **r**endering the call **s**tack | `mg.block(mg.render, mg.stack())` |
|
|
383
383
|
| `mg.l()` | same as `mg.bsl()` | |
|
|
384
384
|
| `mg.s()` | same as `mg.bss()` | |
|
|
385
385
|
|
|
@@ -430,7 +430,7 @@ class LinkedList:
|
|
|
430
430
|
new_node.next = self.head
|
|
431
431
|
self.head.prev = new_node
|
|
432
432
|
self.head = new_node
|
|
433
|
-
mg.block(mg.show, locals()) # <--- draw
|
|
433
|
+
mg.block(mg.show, locals()) # <--- draw locals
|
|
434
434
|
|
|
435
435
|
linked_list = LinkedList()
|
|
436
436
|
n = 100
|
|
@@ -446,42 +446,33 @@ import memory_graph as mg
|
|
|
446
446
|
import random
|
|
447
447
|
random.seed(0) # use same random numbers each run
|
|
448
448
|
|
|
449
|
-
class Node:
|
|
450
|
-
|
|
451
|
-
def __init__(self, value):
|
|
452
|
-
self.smaller = None
|
|
453
|
-
self.value = value
|
|
454
|
-
self.larger = None
|
|
455
|
-
|
|
456
449
|
class BinTree:
|
|
457
450
|
|
|
458
|
-
def __init__(self):
|
|
459
|
-
self.
|
|
451
|
+
def __init__(self, value=None, smaller=None, larger=None):
|
|
452
|
+
self.smaller = smaller
|
|
453
|
+
self.value = value
|
|
454
|
+
self.larger = larger
|
|
460
455
|
|
|
461
|
-
def
|
|
462
|
-
if
|
|
463
|
-
|
|
464
|
-
|
|
456
|
+
def add(self, value):
|
|
457
|
+
if self.value is None:
|
|
458
|
+
self.value = value
|
|
459
|
+
elif value < self.value:
|
|
460
|
+
if self.smaller is None:
|
|
461
|
+
self.smaller = BinTree(value)
|
|
465
462
|
else:
|
|
466
|
-
self.
|
|
463
|
+
self.smaller.add(value)
|
|
467
464
|
else:
|
|
468
|
-
if
|
|
469
|
-
|
|
465
|
+
if self.larger is None:
|
|
466
|
+
self.larger = BinTree(value)
|
|
470
467
|
else:
|
|
471
|
-
self.
|
|
472
|
-
mg.block(mg.show,
|
|
473
|
-
|
|
474
|
-
def add(self, value):
|
|
475
|
-
if self.root is None:
|
|
476
|
-
self.root = Node(value)
|
|
477
|
-
else:
|
|
478
|
-
self.add_recursive(value, self.root)
|
|
468
|
+
self.larger.add(value)
|
|
469
|
+
mg.block(mg.show, mg.stack()) # <--- draw stack
|
|
479
470
|
|
|
480
471
|
tree = BinTree()
|
|
481
472
|
n = 100
|
|
482
473
|
for i in range(n):
|
|
483
|
-
|
|
484
|
-
tree.add(
|
|
474
|
+
value = random.randrange(n)
|
|
475
|
+
tree.add(value)
|
|
485
476
|
```
|
|
486
477
|

|
|
487
478
|
|
|
@@ -502,7 +493,7 @@ class HashSet:
|
|
|
502
493
|
self.buckets[index] = []
|
|
503
494
|
bucket = self.buckets[index]
|
|
504
495
|
bucket.append(value)
|
|
505
|
-
mg.block(mg.show, locals()) # <--- draw
|
|
496
|
+
mg.block(mg.show, locals()) # <--- draw locals
|
|
506
497
|
|
|
507
498
|
def contains(self, value):
|
|
508
499
|
index = hash(value) % len(self.buckets)
|
|
@@ -552,7 +543,7 @@ Different aspects of memory_graph can be configured. The default configuration i
|
|
|
552
543
|
- Maps each type to a Slicer. A slicer determines how many elements of a data type are shown in the graph to prevent the graph from getting too big. 'Slicer()' does no slicing, 'Slicer(1,2,3)' shows just 1 element at the beginning, 2 in the middle, and 3 at the end.
|
|
553
544
|
|
|
554
545
|
### Simplified Graph ###
|
|
555
|
-
Memory_graph simplifies the visualization (and the viewer's mental model) by **not** showing separate nodes for immutable types like `bool`, `int`, `float`, `complex`, and `str` by default. This simplification can sometimes be slightly misleading. As in the example below, after a shallow copy, lists `a` and `b` technically share their `int` values, but the graph makes it appear as though `a` and `b` each have their own copies. However, since `int` is immutable, this simplification will never lead to unexpected changes
|
|
546
|
+
Memory_graph simplifies the visualization (and the viewer's mental model) by **not** showing separate nodes for immutable types like `bool`, `int`, `float`, `complex`, and `str` by default. This simplification can sometimes be slightly misleading. As in the example below, after a shallow copy, lists `a` and `b` technically share their `int` values, but the graph makes it appear as though `a` and `b` each have their own copies. However, since `int` is immutable, this simplification will never lead to unexpected changes (changing `a` won’t affect `b`) so will never result in bugs.
|
|
556
547
|
|
|
557
548
|
The simplification strikes a balance: it is slightly misleading but keeps the graph clean and easy to understand and focuses on the mutable types where unexpected changes can occur. This is why it is the default behavior. If you do want to show separate nodes for `int` values, such as for educational purposes, you can simply remove `int` from the `mg.config.not_node_types` set:
|
|
558
549
|
```python
|
|
@@ -765,7 +756,7 @@ mg.show(locals())
|
|
|
765
756
|
|
|
766
757
|
|
|
767
758
|
## Jupyter Notebook ##
|
|
768
|
-
In Jupyter Notebook `locals()` has additional variables that cause problems in the graph, use `mg.locals_jupyter()` to get the local variables with these problematic variables filtered out. Use `mg.
|
|
759
|
+
In Jupyter Notebook `locals()` has additional variables that cause problems in the graph, use `mg.locals_jupyter()` to get the local variables with these problematic variables filtered out. Use `mg.stack_jupyter()` to get the whole call stack with these variables filtered out.
|
|
769
760
|
|
|
770
761
|
We can use `mg.show()` and `mg.render()` in a Jupyter Notebook, but alternatively we can also use `mg.create_graph()` to create a graph and the `display()` function to render it inline with for example:
|
|
771
762
|
|
|
@@ -778,11 +769,11 @@ See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwi
|
|
|
778
769
|

|
|
779
770
|
|
|
780
771
|
## ipython ##
|
|
781
|
-
In ipython `locals()` has additional variables that cause problems in the graph, use `mg.locals_ipython()` to get the local variables with these problematic variables filtered out. Use `mg.
|
|
772
|
+
In ipython `locals()` has additional variables that cause problems in the graph, use `mg.locals_ipython()` to get the local variables with these problematic variables filtered out. Use `mg.stack_ipython()` to get the whole call stack with these variables filtered out.
|
|
782
773
|
|
|
783
774
|
Additionally install file [auto_memory_graph.py](https://raw.githubusercontent.com/bterwijn/memory_graph/main/src/auto_memory_graph.py) in the ipython startup directory:
|
|
784
|
-
* Linux/Mac:
|
|
785
|
-
* Windows:
|
|
775
|
+
* Linux/Mac: `~/.ipython/profile_default/startup/`
|
|
776
|
+
* Windows: `%USERPROFILE%\.ipython\profile_default\startup\`
|
|
786
777
|
|
|
787
778
|
Then after starting 'ipython' call function `mg_switch()` to turn on/off the automatic visualization of local variables after each command.
|
|
788
779
|

|
|
@@ -180,7 +180,7 @@ mg.show(locals())
|
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
### Custom Copy ###
|
|
183
|
-
We can write our own custom copy function or method in case the three "copy" options don't do what we want. For example, in the code below the copy() method of My_Class copies the `digits` but shares the `letters` between two objects.
|
|
183
|
+
We can write our own custom copy function or method in case the three standard "copy" options don't do what we want. For example, in the code below the copy() method of My_Class copies the `digits` but shares the `letters` between two objects.
|
|
184
184
|
|
|
185
185
|
```python
|
|
186
186
|
import memory_graph as mg
|
|
@@ -223,7 +223,7 @@ mg.render(locals(), 'rebinding2.png')
|
|
|
223
223
|
| rebinding1.png | rebinding2.png |
|
|
224
224
|
|
|
225
225
|
## Call Stack ##
|
|
226
|
-
The `mg.
|
|
226
|
+
The `mg.stack()` function retrieves the entire call stack, including the local variables for each function on the stack. This enables us to visualize the local variables across all active functions simultaneously. By examining the graph, we can determine whether any local variables from different functions share data. For instance, consider the function `add_one()` which adds the value `1` to each of its parameters `a`, `b`, and `c`.
|
|
227
227
|
|
|
228
228
|
```python
|
|
229
229
|
import memory_graph as mg
|
|
@@ -232,7 +232,7 @@ def add_one(a, b, c):
|
|
|
232
232
|
a += [1]
|
|
233
233
|
b += (1,)
|
|
234
234
|
c += [1]
|
|
235
|
-
mg.show(mg.
|
|
235
|
+
mg.show(mg.stack())
|
|
236
236
|
|
|
237
237
|
a = [4, 3, 2]
|
|
238
238
|
b = (4, 3, 2)
|
|
@@ -276,9 +276,9 @@ import memory_graph as mg
|
|
|
276
276
|
def factorial(n):
|
|
277
277
|
if n==0:
|
|
278
278
|
return 1
|
|
279
|
-
mg.block(mg.show, mg.
|
|
279
|
+
mg.block(mg.show, mg.stack())
|
|
280
280
|
result = n * factorial(n-1)
|
|
281
|
-
mg.block(mg.show, mg.
|
|
281
|
+
mg.block(mg.show, mg.stack())
|
|
282
282
|
return result
|
|
283
283
|
|
|
284
284
|
print(factorial(3))
|
|
@@ -295,7 +295,7 @@ A more interesting recursive example that shows sharing of data is power_set().
|
|
|
295
295
|
import memory_graph as mg
|
|
296
296
|
|
|
297
297
|
def get_subsets(subsets, data, i, subset):
|
|
298
|
-
mg.block(mg.show, mg.
|
|
298
|
+
mg.block(mg.show, mg.stack())
|
|
299
299
|
if i == len(data):
|
|
300
300
|
subsets.append(subset.copy())
|
|
301
301
|
return
|
|
@@ -303,7 +303,7 @@ def get_subsets(subsets, data, i, subset):
|
|
|
303
303
|
get_subsets(subsets, data, i+1, subset) # do include data[i]
|
|
304
304
|
subset.pop()
|
|
305
305
|
get_subsets(subsets, data, i+1, subset) # don't include data[i]
|
|
306
|
-
mg.block(mg.show, mg.
|
|
306
|
+
mg.block(mg.show, mg.stack())
|
|
307
307
|
|
|
308
308
|
def power_set(data):
|
|
309
309
|
subsets = []
|
|
@@ -327,13 +327,13 @@ mg.render(locals(), "my_graph.pdf")
|
|
|
327
327
|
as a *watch* in a debugger tool such as the integrated debugger in Visual Studio Code. Then open the "my_graph.pdf" output file to continuously see all the local variables while debugging. This avoids having to add any memory_graph `show()` or `render()` calls to your code.
|
|
328
328
|
|
|
329
329
|
### Call Stack in Watch Context ###
|
|
330
|
-
The ```mg.
|
|
330
|
+
The ```mg.stack()``` doesn't work well in *watch* context in most debuggers because debuggers introduce additional stack frames that cause problems. Use these alternative functions for various debuggers to filter out these problematic stack frames:
|
|
331
331
|
|
|
332
332
|
| debugger | function to get the call stack |
|
|
333
333
|
|:---|:---|
|
|
334
|
-
| **pdb, pudb** | `mg.
|
|
335
|
-
| **Visual Studio Code** | `mg.
|
|
336
|
-
| **Pycharm** | `mg.
|
|
334
|
+
| **pdb, pudb** | `mg.stack_pdb()` |
|
|
335
|
+
| **Visual Studio Code** | `mg.stack_vscode()` |
|
|
336
|
+
| **Pycharm** | `mg.stack_pycharm()` |
|
|
337
337
|
|
|
338
338
|

|
|
339
339
|
|
|
@@ -344,7 +344,7 @@ mg.save_call_stack("call_stack.txt")
|
|
|
344
344
|
```
|
|
345
345
|
Choose 'after' and 'up_to' what function you want to slice and then call this function to get the desired call stack:
|
|
346
346
|
```
|
|
347
|
-
mg.
|
|
347
|
+
mg.stack_after_up_to(after_function, up_to_function="<module>")
|
|
348
348
|
```
|
|
349
349
|
|
|
350
350
|
### Debugging without Debugger Tool ###
|
|
@@ -354,13 +354,13 @@ To simplify debugging without a debugger tool, we offer these alias functions th
|
|
|
354
354
|
| alias | purpose | function call |
|
|
355
355
|
|:---|:---|:---|
|
|
356
356
|
| `mg.sl()` | **s**how **l**ocal variables | `mg.show(locals())` |
|
|
357
|
-
| `mg.ss()` | **s**how the call **s**tack | `mg.show(mg.
|
|
357
|
+
| `mg.ss()` | **s**how the call **s**tack | `mg.show(mg.stack())` |
|
|
358
358
|
| `mg.bsl()` | **b**lock after **s**howing **l**ocal variables | `mg.block(mg.show, locals())` |
|
|
359
|
-
| `mg.bss()` | **b**lock after **s**howing the call **s**tack | `mg.block(mg.show, mg.
|
|
359
|
+
| `mg.bss()` | **b**lock after **s**howing the call **s**tack | `mg.block(mg.show, mg.stack())` |
|
|
360
360
|
| `mg.rl()` | **r**ender **l**ocal variables | `mg.render(locals())` |
|
|
361
|
-
| `mg.rs()` | **r**ender the call **s**tack | `mg.render(mg.
|
|
361
|
+
| `mg.rs()` | **r**ender the call **s**tack | `mg.render(mg.stack())` |
|
|
362
362
|
| `mg.brl()` | **b**lock after **r**endering **l**ocal variables | `mg.block(mg.render, locals())` |
|
|
363
|
-
| `mg.brs()` | **b**lock after **r**endering the call **s**tack | `mg.block(mg.render, mg.
|
|
363
|
+
| `mg.brs()` | **b**lock after **r**endering the call **s**tack | `mg.block(mg.render, mg.stack())` |
|
|
364
364
|
| `mg.l()` | same as `mg.bsl()` | |
|
|
365
365
|
| `mg.s()` | same as `mg.bss()` | |
|
|
366
366
|
|
|
@@ -411,7 +411,7 @@ class LinkedList:
|
|
|
411
411
|
new_node.next = self.head
|
|
412
412
|
self.head.prev = new_node
|
|
413
413
|
self.head = new_node
|
|
414
|
-
mg.block(mg.show, locals()) # <--- draw
|
|
414
|
+
mg.block(mg.show, locals()) # <--- draw locals
|
|
415
415
|
|
|
416
416
|
linked_list = LinkedList()
|
|
417
417
|
n = 100
|
|
@@ -427,42 +427,33 @@ import memory_graph as mg
|
|
|
427
427
|
import random
|
|
428
428
|
random.seed(0) # use same random numbers each run
|
|
429
429
|
|
|
430
|
-
class Node:
|
|
431
|
-
|
|
432
|
-
def __init__(self, value):
|
|
433
|
-
self.smaller = None
|
|
434
|
-
self.value = value
|
|
435
|
-
self.larger = None
|
|
436
|
-
|
|
437
430
|
class BinTree:
|
|
438
431
|
|
|
439
|
-
def __init__(self):
|
|
440
|
-
self.
|
|
432
|
+
def __init__(self, value=None, smaller=None, larger=None):
|
|
433
|
+
self.smaller = smaller
|
|
434
|
+
self.value = value
|
|
435
|
+
self.larger = larger
|
|
441
436
|
|
|
442
|
-
def
|
|
443
|
-
if
|
|
444
|
-
|
|
445
|
-
|
|
437
|
+
def add(self, value):
|
|
438
|
+
if self.value is None:
|
|
439
|
+
self.value = value
|
|
440
|
+
elif value < self.value:
|
|
441
|
+
if self.smaller is None:
|
|
442
|
+
self.smaller = BinTree(value)
|
|
446
443
|
else:
|
|
447
|
-
self.
|
|
444
|
+
self.smaller.add(value)
|
|
448
445
|
else:
|
|
449
|
-
if
|
|
450
|
-
|
|
446
|
+
if self.larger is None:
|
|
447
|
+
self.larger = BinTree(value)
|
|
451
448
|
else:
|
|
452
|
-
self.
|
|
453
|
-
mg.block(mg.show,
|
|
454
|
-
|
|
455
|
-
def add(self, value):
|
|
456
|
-
if self.root is None:
|
|
457
|
-
self.root = Node(value)
|
|
458
|
-
else:
|
|
459
|
-
self.add_recursive(value, self.root)
|
|
449
|
+
self.larger.add(value)
|
|
450
|
+
mg.block(mg.show, mg.stack()) # <--- draw stack
|
|
460
451
|
|
|
461
452
|
tree = BinTree()
|
|
462
453
|
n = 100
|
|
463
454
|
for i in range(n):
|
|
464
|
-
|
|
465
|
-
tree.add(
|
|
455
|
+
value = random.randrange(n)
|
|
456
|
+
tree.add(value)
|
|
466
457
|
```
|
|
467
458
|

|
|
468
459
|
|
|
@@ -483,7 +474,7 @@ class HashSet:
|
|
|
483
474
|
self.buckets[index] = []
|
|
484
475
|
bucket = self.buckets[index]
|
|
485
476
|
bucket.append(value)
|
|
486
|
-
mg.block(mg.show, locals()) # <--- draw
|
|
477
|
+
mg.block(mg.show, locals()) # <--- draw locals
|
|
487
478
|
|
|
488
479
|
def contains(self, value):
|
|
489
480
|
index = hash(value) % len(self.buckets)
|
|
@@ -533,7 +524,7 @@ Different aspects of memory_graph can be configured. The default configuration i
|
|
|
533
524
|
- Maps each type to a Slicer. A slicer determines how many elements of a data type are shown in the graph to prevent the graph from getting too big. 'Slicer()' does no slicing, 'Slicer(1,2,3)' shows just 1 element at the beginning, 2 in the middle, and 3 at the end.
|
|
534
525
|
|
|
535
526
|
### Simplified Graph ###
|
|
536
|
-
Memory_graph simplifies the visualization (and the viewer's mental model) by **not** showing separate nodes for immutable types like `bool`, `int`, `float`, `complex`, and `str` by default. This simplification can sometimes be slightly misleading. As in the example below, after a shallow copy, lists `a` and `b` technically share their `int` values, but the graph makes it appear as though `a` and `b` each have their own copies. However, since `int` is immutable, this simplification will never lead to unexpected changes
|
|
527
|
+
Memory_graph simplifies the visualization (and the viewer's mental model) by **not** showing separate nodes for immutable types like `bool`, `int`, `float`, `complex`, and `str` by default. This simplification can sometimes be slightly misleading. As in the example below, after a shallow copy, lists `a` and `b` technically share their `int` values, but the graph makes it appear as though `a` and `b` each have their own copies. However, since `int` is immutable, this simplification will never lead to unexpected changes (changing `a` won’t affect `b`) so will never result in bugs.
|
|
537
528
|
|
|
538
529
|
The simplification strikes a balance: it is slightly misleading but keeps the graph clean and easy to understand and focuses on the mutable types where unexpected changes can occur. This is why it is the default behavior. If you do want to show separate nodes for `int` values, such as for educational purposes, you can simply remove `int` from the `mg.config.not_node_types` set:
|
|
539
530
|
```python
|
|
@@ -746,7 +737,7 @@ mg.show(locals())
|
|
|
746
737
|
|
|
747
738
|
|
|
748
739
|
## Jupyter Notebook ##
|
|
749
|
-
In Jupyter Notebook `locals()` has additional variables that cause problems in the graph, use `mg.locals_jupyter()` to get the local variables with these problematic variables filtered out. Use `mg.
|
|
740
|
+
In Jupyter Notebook `locals()` has additional variables that cause problems in the graph, use `mg.locals_jupyter()` to get the local variables with these problematic variables filtered out. Use `mg.stack_jupyter()` to get the whole call stack with these variables filtered out.
|
|
750
741
|
|
|
751
742
|
We can use `mg.show()` and `mg.render()` in a Jupyter Notebook, but alternatively we can also use `mg.create_graph()` to create a graph and the `display()` function to render it inline with for example:
|
|
752
743
|
|
|
@@ -759,11 +750,11 @@ See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwi
|
|
|
759
750
|

|
|
760
751
|
|
|
761
752
|
## ipython ##
|
|
762
|
-
In ipython `locals()` has additional variables that cause problems in the graph, use `mg.locals_ipython()` to get the local variables with these problematic variables filtered out. Use `mg.
|
|
753
|
+
In ipython `locals()` has additional variables that cause problems in the graph, use `mg.locals_ipython()` to get the local variables with these problematic variables filtered out. Use `mg.stack_ipython()` to get the whole call stack with these variables filtered out.
|
|
763
754
|
|
|
764
755
|
Additionally install file [auto_memory_graph.py](https://raw.githubusercontent.com/bterwijn/memory_graph/main/src/auto_memory_graph.py) in the ipython startup directory:
|
|
765
|
-
* Linux/Mac:
|
|
766
|
-
* Windows:
|
|
756
|
+
* Linux/Mac: `~/.ipython/profile_default/startup/`
|
|
757
|
+
* Windows: `%USERPROFILE%\.ipython\profile_default\startup\`
|
|
767
758
|
|
|
768
759
|
Then after starting 'ipython' call function `mg_switch()` to turn on/off the automatic visualization of local variables after each command.
|
|
769
760
|

|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
mg.block(fun, print_loc=True), update docs, stack-overflow posts
|
|
3
|
+
|
|
4
|
+
Jupyter Notebook inline render using display()
|
|
5
|
+
|
|
6
|
+
webassembly inline render
|
|
7
|
+
|
|
8
|
+
optional max introspect depth for each type/id
|
|
9
|
+
https://discuss.python.org/t/request-for-feedback-memory-graph-a-python-visualization-tool-for-education/78347
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "23f6d43f-dd17-4020-971e-5bb8a5b1e30b",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# test: locals_jupyter()\n",
|
|
9
|
+
"Show a graph build with the filtered Jupyter locals using function `mg.locals_jupyter()`. Just adding integers to a list:"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"cell_type": "code",
|
|
14
|
+
"execution_count": null,
|
|
15
|
+
"id": "e8913787-bbef-4adb-b027-ac0f28500233",
|
|
16
|
+
"metadata": {},
|
|
17
|
+
"outputs": [],
|
|
18
|
+
"source": [
|
|
19
|
+
"import memory_graph as mg\n",
|
|
20
|
+
"\n",
|
|
21
|
+
"data = []\n",
|
|
22
|
+
"for i in range(5):\n",
|
|
23
|
+
" data.append(i)\n",
|
|
24
|
+
" display(mg.create_graph(mg.locals_jupyter())) # display in jupyter notebook\n",
|
|
25
|
+
" mg.block(mg.show, mg.locals_jupyter()) # display in PDF reader\n",
|
|
26
|
+
" "
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"cell_type": "markdown",
|
|
31
|
+
"id": "f66d9b8d-0937-4ad0-97b4-a7459e84c4f2",
|
|
32
|
+
"metadata": {},
|
|
33
|
+
"source": [
|
|
34
|
+
"# test: get_call_stack_jupyter()\n",
|
|
35
|
+
"Show a graph build the filterd Jupyter call stack from function `mg.get_call_stack_jupyter()`. Recursively filling a list with all permutation of elements with resampling:"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"cell_type": "code",
|
|
40
|
+
"execution_count": null,
|
|
41
|
+
"id": "15d0c443-7cc6-4b4f-a9db-598aaf261364",
|
|
42
|
+
"metadata": {},
|
|
43
|
+
"outputs": [],
|
|
44
|
+
"source": [
|
|
45
|
+
"import memory_graph as mg\n",
|
|
46
|
+
"\n",
|
|
47
|
+
"def get_all_permutations(permutations, elements, data, max_length):\n",
|
|
48
|
+
" if len(data) == max_length: # recursive stop condition\n",
|
|
49
|
+
" permutations.append(data.copy())\n",
|
|
50
|
+
" else:\n",
|
|
51
|
+
" for i in elements:\n",
|
|
52
|
+
" data.append(i)\n",
|
|
53
|
+
" mg.block(mg.show, mg.get_call_stack_jupyter())\n",
|
|
54
|
+
" get_all_permutations(permutations, elements, data, max_length)\n",
|
|
55
|
+
" data.pop()\n",
|
|
56
|
+
" mg.block(mg.show, mg.get_call_stack_jupyter())\n",
|
|
57
|
+
"\n",
|
|
58
|
+
"permutations = []\n",
|
|
59
|
+
"get_all_permutations(permutations, ['L','R'], [], 3)\n",
|
|
60
|
+
"print(permutations)"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"metadata": {
|
|
65
|
+
"kernelspec": {
|
|
66
|
+
"display_name": "Python 3 (ipykernel)",
|
|
67
|
+
"language": "python",
|
|
68
|
+
"name": "python3"
|
|
69
|
+
},
|
|
70
|
+
"language_info": {
|
|
71
|
+
"codemirror_mode": {
|
|
72
|
+
"name": "ipython",
|
|
73
|
+
"version": 3
|
|
74
|
+
},
|
|
75
|
+
"file_extension": ".py",
|
|
76
|
+
"mimetype": "text/x-python",
|
|
77
|
+
"name": "python",
|
|
78
|
+
"nbconvert_exporter": "python",
|
|
79
|
+
"pygments_lexer": "ipython3",
|
|
80
|
+
"version": "3.12.3"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"nbformat": 4,
|
|
84
|
+
"nbformat_minor": 5
|
|
85
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import memory_graph as mg
|
|
2
|
+
import bintrees
|
|
3
|
+
|
|
4
|
+
# Create an AVL tree
|
|
5
|
+
tree = bintrees.AVLTree()
|
|
6
|
+
tree.insert(10, "ten")
|
|
7
|
+
tree.insert(5, "five")
|
|
8
|
+
tree.insert(20, "twenty")
|
|
9
|
+
tree.insert(15, "fifteen")
|
|
10
|
+
|
|
11
|
+
mg.render(locals(), 'avltree_fail.png')
|
|
12
|
+
|
|
13
|
+
mg.config.type_to_color[bintrees.avltree.Node] = "sandybrown"
|
|
14
|
+
mg.render(locals(), 'avltree_color.png')
|
|
15
|
+
|
|
16
|
+
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_linear.Node_Linear(data, dir(data))
|
|
17
|
+
mg.config.type_to_slicer[bintrees.avltree.Node] = mg.slicer.Slicer()
|
|
18
|
+
mg.render(locals(), 'avltree_dir.png')
|
|
19
|
+
|
|
20
|
+
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_base.Node_Base(f"key:{data.key} value:{data.value}")
|
|
21
|
+
mg.render(locals(), 'avltree_base.png')
|
|
22
|
+
|
|
23
|
+
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_linear.Node_Linear(data,
|
|
24
|
+
['left', data.left,
|
|
25
|
+
'key', data.key,
|
|
26
|
+
'value', data.value,
|
|
27
|
+
'right', data.right])
|
|
28
|
+
mg.render(locals(), 'avltree_linear.png')
|
|
29
|
+
|
|
30
|
+
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_key_value.Node_Key_Value(data,
|
|
31
|
+
{'left': data.left,
|
|
32
|
+
'key': data.key,
|
|
33
|
+
'value': data.value,
|
|
34
|
+
'right': data.right}.items())
|
|
35
|
+
mg.render(locals(), 'avltree_key_value.png')
|
|
36
|
+
|
|
37
|
+
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_table.Node_Table(data,
|
|
38
|
+
[[data.key, data.value],
|
|
39
|
+
[data.left, data.right]]
|
|
40
|
+
)
|
|
41
|
+
mg.render(locals(), 'avltree_table.png')
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
digraph memory_graph {
|
|
2
|
+
node [shape=plaintext]
|
|
3
|
+
node130513740275104 [label=<
|
|
4
|
+
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="0" BGCOLOR="white"><TR><TD PORT="table">
|
|
5
|
+
<bintrees.avltree.Node object at 0x76b3992...
|
|
6
|
+
</TD></TR></TABLE>
|
|
7
|
+
> xlabel="Node"]
|
|
8
|
+
node130513740453920 [label=<
|
|
9
|
+
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="0" BGCOLOR="seagreen1"><TR><TD PORT="table">
|
|
10
|
+
<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="5" CELLPADDING="0">
|
|
11
|
+
<TR><TD BORDER="1" STYLE="ROUNDED"> _root </TD><TD BORDER="1" STYLE="ROUNDED"> _count </TD></TR>
|
|
12
|
+
<TR><TD BORDER="1" PORT="ref0"> </TD><TD BORDER="1"> 4 </TD></TR>
|
|
13
|
+
</TABLE>
|
|
14
|
+
</TD></TR></TABLE>
|
|
15
|
+
> xlabel=AVLTree]
|
|
16
|
+
node130513740453920:ref0 -> node130513740275104:table [style=solid]
|
|
17
|
+
node130513742307200 [label=<
|
|
18
|
+
<TABLE BORDER="0" CELLBORDER="3" CELLSPACING="0" CELLPADDING="0" BGCOLOR="dodgerblue1"><TR><TD PORT="table">
|
|
19
|
+
<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="5" CELLPADDING="0">
|
|
20
|
+
<TR><TD BORDER="1" STYLE="ROUNDED"> tree </TD></TR>
|
|
21
|
+
<TR><TD BORDER="1" PORT="ref0"> </TD></TR>
|
|
22
|
+
</TABLE>
|
|
23
|
+
</TD></TR></TABLE>
|
|
24
|
+
> xlabel=dict]
|
|
25
|
+
node130513742307200:ref0 -> node130513740453920:table [style=solid]
|
|
26
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
import memory_graph as mg
|
|
6
|
+
import random
|
|
7
|
+
random.seed(0) # use same random numbers each run
|
|
8
|
+
|
|
9
|
+
class BinTree:
|
|
10
|
+
|
|
11
|
+
def __init__(self, value=None, smaller=None, larger=None):
|
|
12
|
+
self.smaller = smaller
|
|
13
|
+
self.value = value
|
|
14
|
+
self.larger = larger
|
|
15
|
+
|
|
16
|
+
def add(self, value):
|
|
17
|
+
if self.value is None:
|
|
18
|
+
self.value = value
|
|
19
|
+
elif value < self.value:
|
|
20
|
+
if self.smaller is None:
|
|
21
|
+
self.smaller = BinTree(value)
|
|
22
|
+
else:
|
|
23
|
+
self.smaller.add(value)
|
|
24
|
+
else:
|
|
25
|
+
if self.larger is None:
|
|
26
|
+
self.larger = BinTree(value)
|
|
27
|
+
else:
|
|
28
|
+
self.larger.add(value)
|
|
29
|
+
if value == 51:
|
|
30
|
+
mg.render(mg.stack(), f"bin_tree.png")
|
|
31
|
+
exit(0)
|
|
32
|
+
|
|
33
|
+
tree = BinTree()
|
|
34
|
+
n = 100
|
|
35
|
+
for i in range(n):
|
|
36
|
+
value = random.randrange(n)
|
|
37
|
+
tree.add(value)
|
|
38
|
+
|