Append to string either string or list variable - python

My issue is, that i have dict (argparse). If flag has only one attribute, parser will return string, else list.
I am basically making parser, that will create command from input args.
Example:
kwargs = {-name: "MyName", tags: ["tag_1", "tag_2", "tag_3"] }
Needed output:
"--name Myname --tags tag_1 tag_2 tag3"
I kind managed to do that, but I do not think this is optimal way :/
for k, v in kwargs.iteritems():
if v is None:
continue
elif not isinstance(v, basestring):
print (v)
command = command+' {} '.format(k) + ' '.join(v)
else:
print (v)
command = command+' {} {}'.format(k, v)
Also I use this method of argparse, to feth values vars(argparse.parse_args()) (this is not a part of this question, but maybe someone knows). That way keeps missing leading '-' signs, but I'd like to have them... a lot :)
Example:
--tag tag_1 tag_2 tag_3
is converted to:
[tag: tag_1, tag_2, tag3]

Does this function produce what you want:
def foo(adict):
alist = []
for k,v in adict.items():
k = '--'+k
if isinstance(v,list):
v = ' '.join([str(i) for i in v])
else:
v = str(v)
alist.append('{} {}'.format(k,v))
return ' '.join(alist)
Adding the -- to the key is trivial. I suppose you could refine it so that it adds just - to a single character key. The rest is making sure that lists (and possibly nonstring values) are formatted correctly.
Doing all the refinement in the formatting is a lot easier than trying to tweak the argparse or its out put directly.
In [38]: args = argparse.Namespace(name="MyName", tags=['tag1','tag2'], other=12)
Out[39]: Namespace(name='MyName', other=12, tags=['tag1', 'tag2'])
In [41]: vars(args)
Out[41]: {'name': 'MyName', 'other': 12, 'tags': ['tag1', 'tag2']}
In [42]: foo(vars(args))
Out[42]: '--tags tag1 tag2 --name MyName --other 12'

Not sure if I understand your question 100%.
But why can't you use dict as follows,
kwargs = {"--name" : "MyName", "--tags" : ["tag_1", "tag_2", "tag_3"] }
That way your command would be
>>> command
'your_command --name MyName --tags tag_1 tag_2 tag_3'

In order to have your desired output, you can try something like this:
kwargs = {'name': "MyName", 'tags': ["tag_1", "tag_2", "tag_3"] }
final = "--{0} {1} --{2} {3}".format(list(kwargs.keys())[0], list(kwargs.values())[0], list(kwargs.keys())[1], " ".join(list(kwargs.values())[1]))
print(final)
Output:
'--name MyName --tags tag_1 tag_2 tag_3'

Related

get dictionary key by path (string)

I have this path that can change from time to time:
'#/path/to/key'
The parts of the path aren't defined, so this value is also fine
'#/this/is/a/longer/path'
I'm splitting this key at '/' so I get
['#', 'path', 'to', 'key']
and I need to get to the key in this path, let's say my dict is exp, so I need to get to here:
exp['path']['to']['key']
how could I possibly know how to get to this key?
Use the recursion, Luke ...
def deref_multi(data, keys):
return deref_multi(data[keys[0]], keys[1:]) \
if keys else data
last = deref_multi(exp, ['path','to','key'])
UPDATE: It's It's been 5+ years, time for an update, this time without using recursion (which may use slightly more resources than if Python does the looping internally). Use whichever is more understandable (and so maintainable) to you:
from functools import reduce
def deref_multi(data, keys):
return reduce(lambda d, key: d[key], keys, data)
I suggest you to use python-benedict, a python dict subclass with full keypath support and many utility methods.
You just need to cast your existing dict:
exp = benedict(exp)
# now your keys can be dotted keypaths too
exp['path.to.key']
Here the library and the documentation:
https://github.com/fabiocaccamo/python-benedict
Note: I am the author of this project
def get_key_by_path(dict_obj, path_string):
path_list = path_string.split('/')[1:]
obj_ptr = dict_obj
for elem in path_list:
obj_ptr = obj_ptr[elem]
return obj_ptr
There have been some good answers here, but none of them account for paths that aren't correct or paths that at some point result in something that is not subscriptable. The code below will potentially allow you a little more leeway in handling such cases whereas other code so far will just throw an error or have unexpected behavior.
path = '#/path/to/key'
exp = {'path' : { 'to' : { 'key' : "Hello World"}}}
def getFromPath(dictionary, path):
curr = dictionary
path = path.split("/")[1:] # Gets rid of '#' as it's uneccessary
while(len(path)):
key = path.pop(0)
curr = curr.get(key)
if (type(curr) is not dict and len(path)):
print("Path does not exist!")
return None
return curr
print(getFromPath(exp, path)) #Your value
>>> exp = {'path': {'to': {'key': 42}}}
>>> my_key = exp
>>> for i in '#/path/to/key'.split('/')[1:]:
>>> my_key = my_key[i]
>>> print(my_key)
42
But I'm a bit curious about how you retrieved such dict
Assuming what you mean by this is that your array ['#', 'path', 'to', 'key'] has indexes leading into a nested starting from index 1, you could iterate over each item in the list starting from the second and just dig deeper through every iteration.
For example, in Python 3 you could do this.
def get_key_from_path(exp, path):
"""Returns the value at the key from <path> in <exp>.
"""
cur = exp
for dir in path[1:]:
cur = exp[dir]
return cur
Using functools in place of recursion:
# Define:
from functools import partial, reduce
deref = partial(reduce, lambda d, k: d[k])
# Use:
exp = {'path': {'to': {'key': 42}}}
deref(('path', 'to', 'key'), exp)
3 year old question, I know... I just really like functools.

How to replace text between parentheses in Python?

I have a dictionary containing the following key-value pairs: d={'Alice':'x','Bob':'y','Chloe':'z'}
I want to replace the lower case variables(values) by the constants(keys) in any given string.
For example, if my string is:
A(x)B(y)C(x,z)
how do I replace the characters in order to get a resultant string of :
A(Alice)B(Bob)C(Alice,Chloe)
Should I use regular expressions?
re.sub() solution with replacement function:
import re
d = {'Alice':'x','Bob':'y','Chloe':'z'}
flipped = dict(zip(d.values(), d.keys()))
s = 'A(x)B(y)C(x,z)'
result = re.sub(r'\([^()]+\)', lambda m: '({})'.format(','.join(flipped.get(k,'')
for k in m.group().strip('()').split(','))), s)
print(result)
The output:
A(Alice)B(Bob)C(Alice,Chloe)
Extended version:
import re
def repl(m):
val = m.group().strip('()')
d = {'Alice':'x','Bob':'y','Chloe':'z'}
flipped = dict(zip(d.values(), d.keys()))
if ',' in val:
return '({})'.format(','.join(flipped.get(k,'') for k in val.split(',')))
else:
return '({})'.format(flipped.get(val,''))
s = 'A(x)B(y)C(x,z)'
result = re.sub(r'\([^()]+\)', repl, s)
print(result)
Bonus approach for particular input case A(x)B(y)C(Alice,z):
...
s = 'A(x)B(y)C(Alice,z)'
result = re.sub(r'\([^()]+\)', lambda m: '({})'.format(','.join(flipped.get(k,'') or k
for k in m.group().strip('()').split(','))), s)
print(result)
I assume you want to replace the values in a string with the respective keys of the dictionary. If my assumption is correct you can try this without using regex.
First the swap the keys and values using dictionary comprehension.
my_dict = {'Alice':'x','Bob':'y','Chloe':'z'}
my_dict = { y:x for x,y in my_dict.iteritems()}
Then using list_comprehension, you replace the values
str_ = 'A(x)B(y)C(x,z)'
output = ''.join([i if i not in my_dict.keys() else my_dict[i] for i in str_])
Hope this is what you need ;)
Code
import re
d={'Alice':'x','Bob':'y','Chloe':'z'}
keys = d.keys()
values = d.values()
s = "A(x)B(y)C(x,z)"
for i in range(0, len(d.keys())):
rx = r"" + re.escape(values[i])
s = re.sub(rx, keys[i], s)
print s
Output
A(Alice)B(Bob)C(Alice,Chloe)
Also you could use the replace method in python like this:
d={'x':'Alice','y':'Bob','z':'Chloe'}
str = "A(x)B(y)C(x,z)"
for key in d:
str = str.replace(key,d[key])
print (str)
But yeah you should swipe your dictionary values like Kishore suggested.
This is the way that I would do it:
import re
def sub_args(text, tosub):
ops = '|'.join(tosub.keys())
for argstr, _ in re.findall(r'(\(([%s]+?,?)+\))' % ops, text):
args = argstr[1:-1].split(',')
args = [tosub[a] for a in args]
subbed = '(%s)' % ','.join(map(str, args))
text = re.sub(re.escape(argstr), subbed, text)
return text
text = 'A(x)B(y)C(x,z)'
tosub = {
'x': 'Alice',
'y': 'Bob',
'z': 'Chloe'
}
print(sub_args(text, tosub))
Basically you just use the regex pattern to find all of the argument groups and substitute in the proper values--the nice thing about this approach is that you don't have to worry about subbing where you don't want to (for example, if you had a string like 'Fn(F,n)'). You can also have multi-character keys, like 'F(arg1,arg2)'.

In Python, is there a compact way to print the names of those variables in a list that meet a condition?

I'm writing some printouts for the debug mode of a script. Is there a compact way to print the names of those variables in a list that meet a condition?
specification_aw3 = 43534
specification_hg7 = 75445
specification_rt5 = 0
specification_nj8 = 5778
specification_lo4 = 34
specification_ee2 = 8785
specification_ma2 = 67
specification_pw1 = 1234
specification_mu6 = 0
specification_xu8 = 12465
specifications = [
specification_aw3,
specification_hg7,
specification_rt5,
specification_nj8,
specification_lo4,
specification_ee2,
specification_ma2,
specification_pw1,
specification_mu6,
specification_xu8
]
if any(specification == 0 for specification in specifications):
# magic code to print variables' names
# e.g. "variables equal to 0: \"specification_rt5\", \"specification_mu6\"
Just as 9000 suggests, it goes without saying that defining a dictionary is a rational approach for the minimal working example I have defined here. Please assume that this is not a feasible option for the existing code project and that I am looking for a quick, compact (plausibly ugly) bit of code to be used solely for debugging.
EDIT: illustration of something similar to what I want
So here's the beginnings of what I'm looking for:
print("specifications equal to zero:")
callers_local_objects = inspect.currentframe().f_back.f_locals.items()
for specification in [specification for specification in specifications if specification == 0]:
print([object_name for object_name, object_instance in callers_local_objects if object_instance is specification][0])
Basically, is there a compact way to do something like this?
I suggest that instead of a bunch of variables you use a dict:
specification = {
'aw3': 0,
'foo': 1,
'bar': 1.23,
# etc
}
You can access things by name like specification['aw3'].
Then you can find out names for which the value is 0:
zeroed = [name for (name, value) in specification.items() if value == 0]
In addition, since you mentioned printing the line would be:
for element in specification_dictionary:
print(element)
where you can combine it with a list comprehension as above for printing only the elements that meet your case. Element in this case only prints the variable name (key) if you want both the key and value just set it to use specification_dictionary.items(). Cheers.
>>> specification = { 'aw3': 0, 'foo': 1}
>>> for element in specification:
... print(element)
...
foo
aw3
>>> for (key, value) in specification.items():
... print(str(key) + " " + str(value))
...
foo 1
aw3 0
>>> for element in specification.items():
... print(element)
...
('foo', 1)
('aw3', 0)

Python convert string to array assignment

In my application I am receiving a string 'abc[0]=123'
I want to convert this string to an array of items. I have tried eval() it didnt work for me. I know the array name abc but the number of items will be different in each time.
I can split the string, get array index and do. But I would like to know if there is any direct way to convert this string as an array insert.
I would greately appreciate any suggestion.
are you looking for something like
In [36]: s = "abc[0]=123"
In [37]: vars()[s[:3]] = []
In [38]: vars()[s[:3]].append(eval(s[s.find('=') + 1:]))
In [39]: abc
Out[39]: [123]
But this is not a good way to create a variable
Here's a function for parsing urls according to php rules (i.e. using square brackets to create arrays or nested structures):
import urlparse, re
def parse_qs_as_php(qs):
def sint(x):
try:
return int(x)
except ValueError:
return x
def nested(rest, base, val):
curr, rest = base, re.findall(r'\[(.*?)\]', rest)
while rest:
curr = curr.setdefault(
sint(rest.pop(0) or len(curr)),
{} if rest else val)
return base
def dtol(d):
if not hasattr(d, 'items'):
return d
if sorted(d) == range(len(d)):
return [d[x] for x in range(len(d))]
return {k:dtol(v) for k, v in d.items()}
r = {}
for key, val in urlparse.parse_qsl(qs):
id, rest = re.match(r'^(\w+)(.*)$', key).groups()
r[id] = nested(rest, r.get(id, {}), val) if rest else val
return dtol(r)
Example:
qs = 'one=1&abc[0]=123&abc[1]=345&foo[bar][baz]=555'
print parse_qs_as_php(qs)
# {'abc': ['123', '345'], 'foo': {'bar': {'baz': '555'}}, 'one': '1'}
Your other application is doing it wrong. It should not be specifying index values in the parameter keys. The correct way to specify multiple values for a single key in a GET is to simply repeat the key:
http://my_url?abc=123&abc=456
The Python server side should correctly resolve this into a dictionary-like object: you don't say what framework you're running, but for instance Django uses a QueryDict which you can then access using request.GET.getlist('abc') which will return ['123', '456']. Other frameworks will be similar.

How do you convert command line args in python to a dictionary?

I'm writing an application that takes arbitrary command line arguments, and then passes them onto a python function:
$ myscript.py --arg1=1 --arg2=foobar --arg1=4
and then inside myscript.py:
import sys
argsdict = some_function(sys.argv)
where argsdict looks like this:
{'arg1': ['1', '4'], 'arg2': 'foobar'}
I'm sure there is a library somewhere that does this, but I can't find anything.
EDIT: argparse/getopt/optparse is not what I'm looking for. These libraries are for defining an interface that is the same for each invocation. I need to be able to handle arbitrary arguments.
Unless, argparse/optparse/getopt has functionality that does this...
You can use something like this:
myscript.py
import sys
from collections import defaultdict
d=defaultdict(list)
for k, v in ((k.lstrip('-'), v) for k,v in (a.split('=') for a in sys.argv[1:])):
d[k].append(v)
print dict(d)
Result:
C:\>python myscript.py --arg1=1 --arg2=foobar --arg1=4
{'arg1': ['1', '4'], 'arg2': ['foobar']}
Note: the value will always be a list, but I think this is more consistent. If you really want the final dictionary to be
{'arg1': ['1', '4'], 'arg2': 'foobar'}
then you could just run
for k in (k for k in d if len(d[k])==1):
d[k] = d[k][0]
afterwards.
Here's an example using argparse, although it's a stretch. I wouldn't call this complete solution, but rather a good start.
class StoreInDict(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
d = getattr(namespace, self.dest)
for opt in values:
k,v = opt.split("=", 1)
k = k.lstrip("-")
if k in d:
d[k].append(v)
else:
d[k] = [v]
setattr(namespace, self.dest, d)
# Prevent argparse from trying to distinguish between positional arguments
# and optional arguments. Yes, it's a hack.
p = argparse.ArgumentParser( prefix_chars=' ' )
# Put all arguments in a single list, and process them with the custom action above,
# which convertes each "--key=value" argument to a "(key,value)" tuple and then
# merges it into the given dictionary.
p.add_argument("options", nargs="*", action=StoreInDict, default=dict())
args = p.parse_args("--arg1=1 --arg2=foo --arg1=4".split())
print args.options
Something like this?
import sys
argsdict = {}
for farg in sys.argv:
if farg.startswith('--'):
(arg,val) = farg.split("=")
arg = arg[2:]
if arg in argsdict:
argsdict[arg].append(val)
else:
argsdict[arg] = [val]
Slightly different from specified, the value is always a list.
This is what I used today, it accounts for:
--key=val, --key, -key, -key val
def clean_arguments(args):
ret_args = defaultdict(list)
for index, k in enumerate(args):
if index < len(args) - 1:
a, b = k, args[index+1]
else:
a, b = k, None
new_key = None
# double hyphen, equals
if a.startswith('--') and '=' in a:
new_key, val = a.split('=')
# double hyphen, no equals
# single hyphen, no arg
elif (a.startswith('--') and '=' not in a) or \
(a.startswith('-') and (not b or b.startswith('-'))):
val = True
# single hypen, arg
elif a.startswith('-') and b and not b.startswith('-'):
val = b
else:
if (b is None) or (a == val):
continue
else:
raise ValueError('Unexpected argument pair: %s, %s' % (a, b))
# santize the key
key = (new_key or a).strip(' -')
ret_args[key].append(val)
return ret_args
..may I ask why are you trying to rewrite (a bunch of) wheels, when you have:
http://docs.python.org/library/getopt.html
http://docs.python.org/library/optparse.html
http://docs.python.org/library/argparse.html
...etc, etc, etc...
?
EDIT:
In reply to your edit, optparse/argparse (the later one only available in >=2.7) are flexible enough to extend to suit your needs, while maintaining a consistent interface (eg. a user expects to be able to use both --arg=value and --arg value, -a value and -avalue, etc.. using a pre-existent library, you don't have to worry about supporting all those syntaxes, etc.).
Or something like this) Sorry, if this is stupid, I am a newbie:)
$ python3 Test.py a 1 b 2 c 3
import sys
def somefunc():
keys = []
values = []
input_d = sys.argv[1:]
for i in range(0, len(input_d)-1, 2):
keys.append(input_d[i])
values.append(input_d[i+1])
d_sys = dict(zip(keys, values))
somefunc()
If you really want to write something of your own instead of a proper command-line parsing library, for your input this should work:
dict(map(lambda x: x.lstrip('-').split('='),sys.argv[1:]))
You'll want to add something to catch arguments without an '=' in them.

Categories