pyg.base.Dict

There are a few existing dict-extensions similar to Dict (a nice example is https://github.com/mewwts/addict) but Dict has a little more up its sleeve.

initialization

[1]:
from pyg import *
Dict(a = 1, b = 2, c = 3)
[1]:
{'a': 1, 'b': 2, 'c': 3}
[2]:
Dict(a = 1)(b = 2, c = 3)
[2]:
{'a': 1, 'b': 2, 'c': 3}
[3]:
Dict(a = 1)(b = 2)(c = lambda a, b: a+b)
[3]:
{'a': 1, 'b': 2, 'c': 3}
[4]:
Dict(a = 1) + dict(b = 2, c = 3)
[4]:
{'a': 1, 'b': 2, 'c': 3}

members access

[5]:
d = Dict(a = 1, b = 2, c = 3)
[6]:
d.a
[6]:
1
[7]:
d['a', 'b']
[7]:
[1, 2]
[8]:
d[['a', 'b']]
[8]:
{'a': 1, 'b': 2}

But the fun starts when Dict allows you to access functions of its keys:

[9]:
d[lambda a, b: a + b]
[9]:
3

It is important to note that be making d[‘a’, ‘b’] access both ‘a’ and ‘b’ keys, we abandon the right to have tuples as keys.

[10]:
d = Dict({('a','b') : 1})
import pytest
with pytest.raises(KeyError): # Dict will be trying to grab 'a' and 'b' separately
    d[('a','b')]

adding

[11]:
Dict(a = 1, b = 2) + dict(b = 3, c = 4) # like .update() but not in-place
[11]:
{'a': 1, 'b': 3, 'c': 4}

But addition is subtly different from update in the case of tree structure:

[12]:
tree = Dict(a = 1, b = Dict(c = 2, d = 3))
update = dict(x = 1, b = dict(c = 'new value for b.c but keep b.d', e = 4))
tree+update
[12]:
{'a': 1, 'b': {'c': 'new value for b.c but keep b.d', 'd': 3, 'e': 4}, 'x': 1}

Tree updating is actually important enough to have its own function that can operate on dict-trees

[13]:
tree = dict(a = 1, b = dict(c = 2, d = 3)) # I only use dicts
tree_update(tree, update) # but I can still update it like a tree
[13]:
{'a': 1, 'b': {'c': 'new value for b.c but keep b.d', 'd': 3, 'e': 4}, 'x': 1}

subtracting

You can subtract keys or list of keys

[23]:
Dict(a = 'remove me', b = 2, c = 3) - 'a'  # subtracting a key
[23]:
{'b': 2, 'c': 3}
[24]:
Dict(a = 'I am gone', b = 'and so am I', c = 3) - ['a', 'b'] # subtracting a collection of keys
[24]:
{'c': 3}
[28]:
tree = Dict(a = 1, b = Dict(c = 'delete me', d = 'but keep me'), c = 3)
tree - ('b', 'c')  ## subtracting a branch in a tree using a tuple, possible because we know ('b', 'c') is never a node
[28]:
{'a': 1, 'b': {'d': 'but keep me'}, 'c': 3}

modifying the keys: rename

[29]:
Dict(a = 1, b = 2).rename('prefix_') # need to be done sufficient
[29]:
{'prefix_a': 1, 'prefix_b': 2}
[30]:
Dict(a = 1, b = 2).rename('_suffix')
[30]:
{'a_suffix': 1, 'b_suffix': 2}
[31]:
Dict(a = 1, b = 2).rename(upper)
[31]:
{'A': 1, 'B': 2}
[33]:
Dict(a = 1, b = 2, c = 3).rename(a = 'Abraham', b = 'Barbara')
[33]:
{'Abraham': 1, 'Barbara': 2, 'c': 3}

modifying the values: do

[34]:
Dict(a = 1, b = 2, c = 3).do(lambda x: x**2) # modify all values using function
[34]:
{'a': 1, 'b': 4, 'c': 9}
[37]:
Dict(a = 1, b = 2, c = 3).do([lambda x: x**2, lambda x: x-1]) # modify all values using list of function
[37]:
{'a': 0, 'b': 3, 'c': 8}
[40]:
Dict(a = 1, b = 2, c = 3).do([lambda x: x**2, lambda x: x-1], 'a', 'b') # modify selected keys using list of function
[40]:
{'a': 0, 'b': 3, 'c': 3}

Dict can store a calculation flow

Being able to access function of members means we can think of a Dict as a container of variables. Consider this code:

[17]:
def func(a, b):
    c = a + b
    d = b + c
    e = a/b + d/c
    f = (d+e)/c
    return f
func(1,2)
[17]:
2.388888888888889

How can we keep track of our calculations and debug it easily? Consider rewriting this:

[18]:
x = Dict(a = 1, b = 2)
x = x(c = lambda a, b: a + b)
x = x(d = lambda b, c: b + c)
x = x(e = lambda a,b,c,d : a/b + d/c)
x = x(f = lambda c,d,e: (d+e)/c)
x
[18]:
{'a': 1,
 'b': 2,
 'c': 3,
 'd': 5,
 'e': 2.166666666666667,
 'f': 2.388888888888889}

We have all the internals of the function exposed and we are able to separate calculation flow and data easily:

[19]:
calculation_pipeline = dict(c = lambda a, b: a + b,
                            d = lambda b, c: b + c,
                            e = lambda a,b,c,d : a/b + d/c,
                            f = lambda c,d,e: (d+e)/c)

initial_values = Dict(a = 1, b = 2)

[20]:
initial_values(**calculation_pipeline)
[20]:
{'a': 1,
 'b': 2,
 'c': 3,
 'd': 5,
 'e': 2.166666666666667,
 'f': 2.388888888888889}

You can see in pyg.base.dictable tutorial how this is extended