1

I have a list like this

yy = ['A1', 'B1', 'C1']

with the values for A1, B1 and C1 in a dict like this

ff = {
    'A1': 10,
    'B1': 20,
    'C1': 30
}

Now I want to do sum of the list with values from ff. This is what I tried to do

p = "sum(lst)"

eval(p, {'lst': yy}, ff)

But I get TypeError: unsupported operand type(s) for +: 'int' and 'str'. During debugging I found out that if I do like this p = "sum([A1, B1, C1])" and eval It works. Not sure why this happens?

Full code:

ff = {
    'A1': 10,
    'B1': 20,
    'C1': 30
}


yy = ['A1', 'B1', 'C1']

p = "sum(lst)"

eval(p, {'lst': yy}, ff)

I know eval's consequences. I filter everything before it reaches eval

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
PyAn
  • 291
  • 4
  • 14
  • Explain why you need eval. You need a **good** reason to use eval. Simply put: don't use eval. – David Mašek Aug 29 '15 at 17:42
  • I am using eval to parse Excel formulas like this one `'=SUM(AY92:BI92)/SUM(AL92:AX92)'` where I first expand the list from Ay92 to BI92 and then do a sum using eval. – PyAn Aug 29 '15 at 17:44
  • I do kinda understand your problem here. Are you trying to meddle with `eval`'s optional params to get your output? – Bhargav Rao Aug 29 '15 at 17:46
  • Using eval on something what looks like (user generated?) input is bad. See [this](http://stackoverflow.com/questions/1832940/is-using-eval-in-python-a-bad-practice?lq=1). I could make Excel formula that would mess up my computer. And worse any computer that runs your script on my formula. – David Mašek Aug 29 '15 at 17:49
  • I cannot get those values. see my full code here http://pastebin.com/RSHZN4vq – PyAn Aug 29 '15 at 17:55
  • I know its consequences. I filter everything before it reaches eval(). so no worries. – PyAn Aug 29 '15 at 17:58
  • Yes THIS is what I want. I just need to remove those quotes. I am not sure how to do that. – PyAn Aug 29 '15 at 18:00
  • I can't comment on you code in this small comment, I suggest that after having a working code (using `eval` or whatnot) you post it on http://codereview.stackexchange.com/ and get a way to make it work without using `eval`. – f.rodrigues Aug 29 '15 at 18:01

4 Answers4

3

Do you have to use eval?

why not use sum();

yy = ['A1', 'B1', 'C1']

ff = {
    'A1': 10,
    'B1': 20,
    'C1': 30
}

print sum([ff[key] for key in yy])

if you really need to use eval, here how to do it:

print eval('+'.join(str(ff[key]) for key in yy))
# or for short
print eval("+".join(yy), globals=ff)

# or the way you are doing
print eval("sum(lst)", {'lst': [ff[key] for key in yy]})

The reason you were getting a TypeError is because "sum(lst)" is essently sum(['A1', 'B1', 'C1']), which Python doesn't know how to handle. By changing lst to [ff[key] for key in yy] we are making a new list that's of the number referenced(by key) by yy in ff

I see your comment:

I am using eval to parse Excel formulas like this one '=SUM(AY92:BI92)/SUM(AL92:AX92)' where I first expand the list from Ay92 to BI92 and then do a sum using eval

Why not get the values of AY92:BI92 into a list and the values of AL92:AX92 into a second list and them use sum(lst1)/sum(lst2)? Eval is nasty, if this excel file contains malicious code inside the cell, it will be run.

Community
  • 1
  • 1
f.rodrigues
  • 3,499
  • 6
  • 26
  • 62
1

Though this is not advised you can try

ff = {
    'A1': 10,
    'B1': 20,
    'C1': 30
}


yy = '[A1, B1, C1]'

p = "sum(eval(lst))"

eval(p, {'lst': yy}, ff)

The working of this is

  • The first eval call will convert the variable lst and do

    sum(eval('[A1, B1, C1]'))
    
  • The second eval will now convert the inner variables and make it as

    sum([10, 20, 30])
    

This will give you your output.

Also note that if you are unable to initialize the variable yy, you can use the str.replace function

yy = str(yy).replace("'", '')

This will return '[A1, B1, C1]' from your earlier variable.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
  • 2
    you can use `yy = str(yy).replace("'", '')` instead of hardcoding it to `yy = '[A1, B1, C1]'` – f.rodrigues Aug 29 '15 at 18:07
  • 1
    @f.rodrigues True. But do see the difference. When you type out `['A1', 'B1', 'C1']` you will have to add `'` everywhere except at the ends and then the additional overhead of `replace`. However if the OP is getting the list like that itself from the other end, then your advise of using `replace` is certainly the way to go. I have added it as a note below – Bhargav Rao Aug 29 '15 at 18:10
  • Exactly, If he can initialize `yy` the way he wants, your answer is fine and a lot cleaner, I was assuming he could, like using `ff.keys()` would yield `['A1', 'B1', 'C1']` anyway. – f.rodrigues Aug 29 '15 at 18:42
0

Given that data to sum up the elements in the dictionary that are referred to by the strings in the list:

yy = ['A1', 'B1', 'C1']
ff = {
    'A1': 10,
    'B1': 20,
    'C1': 30
}
print (sum(map(ff.get, yy)))

ff.get returns the value of a given key. Map that across the list giving a list of numbers which is then given to sum.

See:

>>> print (sum(map(ff.get, yy)))
60

If you must eval:

>>> eval("sum(map(tab.get, lst))", {'lst': yy, 'tab': ff})
60
Dan D.
  • 73,243
  • 15
  • 104
  • 123
0

It's pretty straighforward:

>>> yy = ['A1', 'B1', 'C1']
>>> ff = {
...     'A1': 10,
...     'B1': 20,
...     'C1': 30
... }
>>> sum(ff[i] for i in yy)
60

But, if you somehow need to use eval (I don't know why).

>>> eval("+".join(yy), ff)
60

But, really there's no reason to use eval for this. It is neither fast nor secure.

utdemir
  • 26,532
  • 10
  • 62
  • 81