snair.stack
snair.stack

Reputation: 415

How to retain the type of list elements after stored in a dictionary?

I have a nested dictionary, which is of following form.

import pickle

db = {'obj 1':
        {
        'a' : 1, 'b' : 2, 'c' : 3,
        'foo': [0, a, a+b, 2*a, 2*(b+c)],
        'bar': [c, c+a, c-b, 2*c, 2*a]
        },
    'obj 2':
        {
        'a' : 4, 'b' : 5, 'c' : 6,
        'foo': [0, a, a+b, 2*a, 2*(b+c), 0, 0],
        'bar': [c, c+a, c-b, 2*c, 2*a, 0, 0]
        },
}

with open("db.pkl", "wb") as pkl:
    pickle.dump(db, pkl)

print(db)

It's throwing this error:

Traceback (most recent call last):
  File "dmy.py", line 8, in <module>
    'foo': [0, a, a+b, 2*a, 2*(b+c)],
NameError: name 'a' is not defined

Is there anything I can do such that I'll get the output for dmy = db['obj 1']['foo'] to be [0, a, a+b, 2*a, 2*(b+c)] which is a list. I understand what I am doing may not be the right approach, but I need the contents together like this.

Note: I already tried converting the list elements individually into strings, but while parsing, I couldn't get the content in this specified form i.e [0, a, a+b, 2*a, 2*(b+c)].

Closest I could find was this link Python - is there a way to store an operation(+ - * /) in a list or as a variable?

Upvotes: 0

Views: 70

Answers (3)

Tzn
Tzn

Reputation: 632

You could replace your list comprehensions with lambda functions and pickle them using dill (https://pypi.org/project/dill/) instead of pickle:

import dill

db = {'obj 1':
        {
        'a' : 1, 'b' : 2, 'c' : 3,
        'foo': lambda a, b, c : [0, a, a+b, 2*a, 2*(b+c)],
        'bar': lambda a, b, c : [c, c+a, c-b, 2*c, 2*a]
        },
    'obj 2':
        {
        'a' : 4, 'b' : 5, 'c' : 6,
        'foo': lambda a, b, c : [0, a, a+b, 2*a, 2*(b+c), 0, 0],
        'bar': lambda a, b, c : [c, c+a, c-b, 2*c, 2*a, 0, 0]
        },
}

print(db)

pickled_db = dill.dumps(db)
unpickled_db = dill.loads(pickled_db)

print(unpickled_db)

Upvotes: 1

snair.stack
snair.stack

Reputation: 415

For now I managed to solve it usingeval. Code I used is given below.

import pickle

db = {'obj 1':
        {
        'a' : 1,
        'b' : 2,
        'c' : 3,
        'foo': ['0', 'a', 'a+b', '2*a', '2*(b+c)'],
        'bar': ['c', 'c+a', 'c-b', '2*c', '2*a']
        },
    'obj 2':
        {
        'a' : 4,
        'b' : 5,
        'c' : 6,
        'foo': ['0', 'a', 'a+b', '2*a', '2*(b+c)', '0', '0'],
        'bar': ['c', 'c+a', 'c-b', '2*c', '2*a', '0', '0']
        },
}

with open("db.pkl", "wb") as pkl:
    pickle.dump(db, pkl)
print(db)
with open("db.pkl", "rb") as pkl:
    db_pkl = pickle.loads(pkl.read())

a = db_pkl['obj 1']['a']
b = db_pkl['obj 1']['b']
c = db_pkl['obj 1']['c']
arr = db_pkl['obj 1']['foo']

arr_foo = []
for i in range(0, len(arr)):
    foo = eval(arr[i])
    arr_foo.append(foo)
print("\n", arr_foo)

Output:

{'obj 1': {'a': 1, 'b': 2, 'c': 3, 'foo': ['0', 'a', 'a+b', '2*a', '2*(b+c)'], 'bar': ['c', 'c+a', 'c-b', '2*c', '2*a']}, 'obj 2': {'a': 4, 'b': 5, 'c': 6, 'foo': ['0', 'a', 'a+b', '2*a', '2*(b+c)', '0', '0'], 'bar': ['c', 'c+a', 'c-b', '2*c', '2*a', '0', '0']}}

 [0, 1, 3, 2, 10]

It doesn't retain the variable names but rather computes it. If anybody have a cleaner, better or alternate way to do this please feel free to edit or post answer.

Upvotes: 0

glglgl
glglgl

Reputation: 91119

The problem is that a, b and c are members of your (not-yet existing) dict. They are not variables, so

    {
    'a' : 1, 'b' : 2, 'c' : 3,
    'foo': [0, a, a+b, 2*a, 2*(b+c)],
    'bar': [c, c+a, c-b, 2*c, 2*a]
    },

doesn't work.

You can solve this problem in the following way:

import pickle

def build_dict(a, b, c, add=None):
    if add is None: add = []
    return {
        'a': a, 'b': b, 'c' : c,
        'foo': [0, a, a+b, 2*a, 2*(b+c)] + add,
        'bar': [c, c+a, c-b, 2*c, 2*a] + add
    }

db = {'obj 1': build_dict(1, 2, 3),
      'obj 2': build_dict(4, 5, 6, [0, 0])}

with open("db.pkl", "wb") as pkl:
    pickle.dump(db, pkl)

print(db)

This makes the variables a, b and c exist and usable.

Another way to do it could be

    transform({'a' : 1, 'b' : 2, 'c' : 3, lambda a, b, c: {'foo': [0, a, a+b, 2*a, 2*(b+c)], 'bar': [c, c+a, c-b, 2*c, 2*a]})
    transform({'a' : 4, 'b' : 5, 'c' : 6, lambda a, b, c: {'foo': [0, a, a+b, 2*a, 2*(b+c), 0, 0], 'bar': [c, c+a, c-b, 2*c, 2*a, 0, 0]})

with

def transform(base, transform):
    base.update(transform(**base))
    return base

or

    transform({'a' : 1, 'b' : 2, 'c' : 3, lambda base: {'foo': [0, base['a'], base['a']+base['b'], 2*base['a'], 2*(base['b']+base['c'])], 'bar': [base['c'], base['c']+base['a'], base['c']-base['b']b, 2*base['c'], 2*base['a']]})
    transform({'a' : 4, 'b' : 5, 'c' : 6, lambda base: {'foo': 0, base['a'], base['a']+base['b'], 2*base['a'], 2*(base['b']+base['c']), 0, 0], 'bar': [base['c'], base['c']+base['a'], base['c']-base['b']b, 2*base['c'], 2*base['a'], 0, 0]})

with

def transform(base, transform):
    base.update(transform(**base))
    return base

Upvotes: 1

Related Questions