1

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.

martineau
  • 119,623
  • 25
  • 170
  • 301
Fly_37
  • 312
  • 1
  • 12
  • 2
    You can also [split a string with multiple delimiters](https://stackoverflow.com/questions/1059559/split-strings-into-words-with-multiple-word-boundary-delimiters). – Anderson Green Aug 30 '21 at 11:57
  • ```x=re.findall(r"[\w']",string_of_kwargs);graph_kwargs={i:j for i,j in zip(x[::2],x[1::2])}``` –  Aug 30 '21 at 12:00

6 Answers6

8

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.

Pierre D
  • 24,012
  • 7
  • 60
  • 96
4

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.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
3

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'}
chepner
  • 497,756
  • 71
  • 530
  • 681
1

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(',')))
eroot163pi
  • 1,791
  • 1
  • 11
  • 23
  • 1
    Given that the string is actually called `string_of_kwargs` this might indeed be safe and not as bad as one might think. I don't really see the connection to the second part of the answer, though. (The nested list-comp is exactly how I'd have done it, and evaluating the numeric value might also be a good idea, but it's an entirely different application of eval as in the first part.) – tobias_k Aug 30 '21 at 13:34
  • Yes, you are right. I didn't pay attention to that. Just when I see question, I thought of eval – eroot163pi Aug 30 '21 at 13:41
0

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


glory9211
  • 741
  • 7
  • 18
0

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]))
mpx
  • 3,081
  • 2
  • 26
  • 56