Python compiled eval? - python

my situation is as follows:
I have a large table like object which is accessed with a string key and integer index; i.e. lookup is like this: value = table.get("Key", index) .
I would like to give the user the opportunity to enter an arbitrary algebraic expression involving the string keys. The code code should then iterate over the second index and evaluate the expression repeatedly.
So for user input like this: "KeyA + 2*math.abs(KeyC)" I would like to run python code resembling:
for index in index_list:
answer = table.get("KeyA", index) + 2*math.abs(table.get("Keyc",index))
I guess can parse the expression using one of the Python Parser libraries I found on the internet, but it is not by any means clear to me how actually "run" the parsed code. Any suggestions?

If your end users can enter variables in figure brackets, {..}, you can use str.format to format your string
>>> expression = '{a}*{b}'
>>> values = {'a': 10, 'b': 20, 'c': 30}
>>> expression.format(**values)
'10*20'
Here values dictionary might be filled with table.get for all keys found in expression, for example with a regular expression:
>>> import re
>>> regexp = re.compile('{(.*?)}')
>>> keys = regexp.findall(expression)
>>> keys
['a', 'b']
>>> table_get = lambda *x: np.random.randint(5)
>>> values = {k: table_get(k) for k in keys}
>>> expression.format(**values)
'1*4'
Then you can refer to Safe way to parse user-supplied mathematical formula in Python for safe expression parsing and evaluation.

Related

Is there a way to unpack the result of split() inside of for-loop?

I know that maybe the title of the question is not the most intuitive one, but I could not think of better way to describe it in short and here is what I actually mean.
I want to write some small parser, that would build a dictionary of kwargs out of string that I specify.
Here is an example:
string_of_kwargs = 'n=6,m=10'
graph_kwargs = {pair.split('=')[0]:pair.split('=')[1]
for pair in string_of_kwargs.split(',')}
And the output is:
{'n': '6', 'm': '10'}
The problem is that in the code above I had to use pair.split('=') twice
and I wonder if there is some way to go around it in case I had to unpack more values like this in future.
Sure:
>>> dict(pair.split('=', 1) for pair in string_of_kwargs.split(','))
{'n': '6', 'm': '10'}
Why the 1 as second argument of split()? That's in case there are more than one '=' sign. There is more to do to make this bullet-proof, though, but this is beyond the scope of the question.
You can hackily use a nested for-clause for the binding to a name by iterating over a single-element list like this:
graph_kwargs = {
k:v for pair in string_of_kwargs.split(',')
for k,v in [pair.split('=')]
}
Note, I call it hackey, but it was apparently idiomatic enough to be worthy of a bespoke optimization in Python 3.9, where it basically gets compiled down to a regular assignment instead of actually creating the intermediate list. You can see this for yourself by playing with dis different versions of the interpreter.
Yet another option (though I would still recommend using dict and a generator):
>>> from operator import methodcaller
>>> kv_split = methodcaller('split', '=', 1)
>>> {k: v for k, v in map(kv_split, string_of_kwargs.split(","))}
{'n': '6', 'm': '10'}
If you know that string_of_kwargs will always be of same format(trusted input) like ',' separated assignment expression. https://realpython.com/python-eval-function/
# convenient(when dict values itself contain '=' or ',') but risky
# This will evaluate the strings also though, '6' -> 6
eval(f'dict({string_of_kwargs})')
from ast import literal_eval
# This will evaluate the strings also though, '6' -> 6
dict((k, literal_eval(v)) for k, v in (pair.split('=') for pair in s.split(',')))
You can use regular expression to split on "," and "=" characters.
Then you can get all the even indexes as keys and odd indexes as values for your dictionary
import re
string_of_kwargs = 'n=6,m=10'
splitted = re.split('=|,', string_of_kwargs) # This will split on = or ,
# Using python list slicing
keys = splitted[0::2] # get all the keys in splitted
values = splitted[1::2] # get all the values in splitted
Using regex findall to split the text, and used it to create the dict pair
import re
string_of_kwargs = 'n=6,m=10'
x=re.findall(r"[\w']+", string_of_kwargs )
your_dict = dict(zip(x[::2], x[1::2]))

Python: split this length-structured string the most elegant way

Given this string:
fsw="M525x617M525x617S16d48492x577S10000505x544S22a00506x524S21300487x601S37601511x574S34500482x483
I'd like to convert
fsw[8:] (thus "M525x617S16d48492x577S10000505x544S22a00506x524S21300487x601S37601511x574S34500482x483")
in a dictionary containing:
{'S16d48':'492x577', 'S10000':'505x544', 'S22a00':'506x524', 'S21300':'487x601', 'S37601':'511x574', 'S34500':'482x483'}
I managed to get the following with regexp:
>>> import re
>>> re.findall("S[123][0-9a-f]{2}[0-5][0-9a-f]",fsw[8:])
['S16d48', 'S10000', 'S22a00', 'S21300', 'S37601', 'S34500']
>>> re.findall("S[123][0-9a-f]{2}[0-5][0-9a-f].......",fsw[8:])
['S16d48492x577', 'S10000505x544', 'S22a00506x524', 'S21300487x601', 'S37601511x574', 'S34500482x483']
but as far as a dictionary is concerned... I failed to get any further.
Another question: in a Python dictionary it is well the whole
key-value pair (say "S16d48":"492x577") that must be unique right ?
In advance - thanks a lot.
Regards.
It seems you can alter your expression to
(?P<key>S[123][0-9a-f]{2}[0-5][0-9a-f])
(?P<value>\d+x\d+)
And then do a dict comprehension as in
import re
rx = re.compile(r'(?P<key>S[123][0-9a-f]{2}[0-5][0-9a-f])(?P<value>\d+x\d+)')
data = "M525x617M525x617S16d48492x577S10000505x544S22a00506x524S21300487x601S37601511x574S34500482x483"
result = {m["key"]: m["value"] for m in rx.finditer(data)}
This yields
{'S16d48': '492x577', 'S10000': '505x544', 'S22a00': '506x524', 'S21300': '487x601', 'S37601': '511x574', 'S34500': '482x483'}
See a demo for the expression on regex101.com and for the code on ideone.com.
You can convert the lists you already created to a dictionary in the following way:
import re
fsw="M525x617M525x617S16d48492x577S10000505x544S22a00506x524S21300487x601S37601511x574S34500482x483"
str_lst = re.findall("S[123][0-9a-f]{2}[0-5][0-9a-f]",fsw[8:])
full_lst = re.findall("S[123][0-9a-f]{2}[0-5][0-9a-f].......",fsw[8:])
str_dict = {x: y[len(x):] for x in str_lst for y in full_lst if y.startswith(x)}
This gives:
{'S16d48': '492x577',
'S10000': '505x544',
'S22a00': '506x524',
'S21300': '487x601',
'S37601': '511x574',
'S34500': '482x483'}
Not sure if I have understood what you are trying to do, but one way to obtain your dictionary from that string is
d = {}
for piece in fsw[8:].split('S')[1:]:
d["S"+piece[:5]] = piece[5:]
print(d)

how to create a dictionary from a set of properly formatted tuples in python

Is there a simple way to create a dictionary from a list of formatted tuples. e.g. if I do something like:
d={"responseStatus":"SUCCESS","sessionId":"01234","userId":2000004904}
This creates a dictionary called d. However, if I want to create a dictionary from a string which contains the same string, I can't do that
res=<some command that returns {"responseStatus":"SUCCESS","sessionId":"01234","userId":2000004904}>
print res
# returns {"responseStatus":"SUCCESS","sessionId":"01234","userId":2000004904}
d=dict(res)
This throws an error that says:
ValueError: dictionary update sequence element #0 has length 1; 2 is required
I strongly strongly suspect that you have json on your hands.
import json
d = json.loads('{"responseStatus":"SUCCESS","sessionId":"01234","userId":2000004904}')
would give you what you want.
Use dict(zip(tuples))
>>> u = ("foo", "bar")
>>> v = ("blah", "zoop")
>>> d = dict(zip(u, v))
>>> d
{'foo': 'blah', 'bar': 'zoop'}
Note, if you have an odd number of tuples this will not work.
Based on what you gave is, res is
# returns {"responseStatus":"SUCCESS","sessionId":"01234","userId":2000004904}
So the plan is to grab the string starting at the curly brace to the end and use json to decode it:
import json
# Discard the text before the curly brace
res = res[res.index('{'):]
# Turn that text into a dictionary
d = json.loads(res)
All you need to do in your particular case is
d = eval(res)
And please keep security in mind when using eval, especially if you're mixing it with ajax/json.
UPDATE
Since others pointed out you might be getting this data over the web and it isn't just a "how to make this work" question, use this:
import json
json.loads(res)

All possible combinations of dictionary values given input string. Python

I'm trying to get all possible strings from the values in a dictionary given a particular key. For example, 'A' could mean either 'aaa','aba', or 'aac' and 'B' could mean either 'bbb','bab', or 'bbc', etc.
I've given an example of the output where it shows most combinations of the possible strings
import itertools
in_ = 'ABC'
D = {'A':['aaa','aba','aac'],'B':['bbb','bab','bbc'],'C':['ccc','cac','ccb']}
#out_ = ['aaabbbccc','ababbbccc','aacbbbccc','aaababccc','aaabbcccc','aaabbbcac','aaabbbccb'...]
I started writing the code but it started to get REALLY messy and I feel that there is a way to use itertools or something more pythonic to achieve this goal
output = []
for char in in_:
out_string = ''
while char:
for v in D[char]:
while v:
for char2 in in_:
out_string
#not pythonic . . .
Well, you've got itertools imported there. Let's use it! We want to take the Cartesian product D['A'] × D['B'] × D['C'], so we'll do precisely that using itertools.product.
import itertools
in_ = 'ABC'
D = {'A':['aaa','aba','aac'],'B':['bbb','bab','bbc'],'C':['ccc','cac','ccb']}
iterables = [D[character] for character in in_]
out_ = [''.join(tup) for tup in itertools.product(*iterables)]
Now, out_ is:
['aaabbbccc', 'aaabbbcac', 'aaabbbccb', 'aaababccc', 'aaababcac', 'aaababccb',
'aaabbcccc', 'aaabbccac', 'aaabbcccb', 'ababbbccc', 'ababbbcac', 'ababbbccb',
'abababccc', 'abababcac', 'abababccb', 'ababbcccc', 'ababbccac', 'ababbcccb',
'aacbbbccc', 'aacbbbcac', 'aacbbbccb', 'aacbabccc', 'aacbabcac', 'aacbabccb',
'aacbbcccc', 'aacbbccac', 'aacbbcccb']
Is that the result you were going for?

How to get a value within a dictionary within a dictionary with just a string?

I'm trying to create a function that takes in a dictionary and a string and outputs a value within the Dictionary depending on the string.
For example, inputs would be:
D = {'assigned':{'id':4,'name':'myname'},'anotherkey':{'id':4,'name':'myname'}}
s = "['assigned']['id']"
Result:
4
This can also be achieved with the following, but the issue is that the function will only take a dictionary and a string
print D['assigned']['id']
>> 4
If you don't want to use eval(), you can of course parse out the fields of the string yourself, using regular expressions for instance:
import re
def lookup(d, s):
mo = re.match(r"\['([a-z]+)'\]\['([a-z]+)'\]", s)
if mo and len(mo.groups()) == 2:
return d[mo.group(1)][mo.group(2)]
return None
You can do simpler parsing too since the input is pretty fixed.
You can use eval, but you have to be sure that the string does contain things you want:
>>> eval("D"+s)
4
eval can do this
>>> D = {'assigned':{'id':4,'name':'myname'},'anotherkey':{'id':4,'name':'myname'}}
>>> s = "['assigned']['id']"
>>> eval("D" +s)
4

Categories