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

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.

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.

Append to string either string or list variable

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'

how to get the order of named arguments after a call the function

I have a function with named arguments
def sample(a=None, b=None, c=None)
pass
how to get the order of these arguments after a call the function?
sample(b=1, a=1, c=1)
out: ['b', 'a', 'c']
sample(c=1, a=1)
out: ['c', 'a']
sample(a=1, b=1)
out: ['a', 'b']
sample(b=1, a=1)
out: ['b', 'a']
sample(a=1, b=1, c=1)
out: ['a', 'b', 'c']
maybe this can be done using a decorator or some other way?
updated:
I want to make the wrapper for elasticsearch filters like sqlalchemy filter way but through named arguments
class ESQuery(object):
def __init__(self, url, index):
pass
def filter(self, _bool=None, _range=None, _not=None, _and=None, _or=None, _type=None, exists=None, query=None):
return self.query_body
after calling the function, I need to get the correct order of query, like this http://www.elastic.co/guide/en/elasticsearch/reference/1.5/query-dsl-and-filter.html
I just can't build the query so that to keep a strict order
es = Someclass(url, index)
es.filter()
I want to do it using named arguments that people find it convenient to use as the tooltip
update 2:
I want to find another way, one line is not enough, as there can be long calls
from api.search_api import ESQuery
es = ESQuery(index='lot', place='etsy_spider', match_all=True)
print es.\
filter(query='{ "tweet": "full text search" }').\
filter(_range='{ "created": { "gte": "now - 1d / d" }}').\
filter(should='{ "term": { "featured": true }},')
maybe some ideas on how to simplify long queries in elasticsearch?
I cannot think of a reason why it would be useful. That being said, you can use the inspect module (https://docs.python.org/2/library/inspect.html):
import inspect
def f(a=2, b=3):
call_string = inspect.stack()[1][4] ## returns a list with the string that represents how the function was called.
print call_string
args, kwargs = get_function_args(call_string[0]) ## to return what you want
...
f(b=3, a=1) # prints [u'f(b=3, a=1)\n']
Then, you would parse the call_string with regular expressions.
Note that this method only works for single-line calls.
And here is a simple regex parser that will return a list of arguments and keyword arguments by order of appearance. Note that this is very basic and will not work with strings that contain ",".
import re
VALID_VAR = "([_A-Za-z][_a-zA-Z0-9]*)"
LEFT_PAR = '\('
RIGHT_PAR = '\)'
def get_function_args(line):
args = []
keywords = []
res = re.search(VALID_VAR+LEFT_PAR+'(.*?)'+RIGHT_PAR+'$', line)
if res:
allargs = res.group(2)
allargs = allargs.split(',') ## does not work if you have strings with ","
for arg in allargs:
## Arguments
res2 = re.search('^{0}$'.format(VALID_VAR), arg.strip())
if res2:
args.append(res2.group(1))
## Optional arguments
res2 = re.search('^{0} *= *(.*)$'.format(VALID_VAR), arg.strip())
if res2:
keywords.append(res2.group(1))
return args, keywords
Yes, it can be done - read on.
how to get the order of these arguments after a call the function?
Named arguments are passed as a dictionary, whose .items() by definition are in arbitrary order:
# this...
def foo(a=None, b=None, c=None):
...
# .... is the equivalent of:
def foo(**kwargs):
a = kwargs.get('a')
b = kwargs.get('b')
c = kwargs.get('c')
...
how to get the order of these arguments after a call the function?
Regardless of the above, you can achieve it by using an OrderedDict:
from collections import OrderedDict
def bar(sorted_kwargs):
for k in sorted_kwargs.keys():
print 'index of %s => %s' % (k, sorted_kwargs.keys().index(k))
bar(OrderedDict((('c', 1), ('a', 2), ('b', 3))))
# results
index of c => 0
index of a => 1
index of b => 2
maybe this can be done using a decorator or some other way?
The order of the arguments is something for the caller to decide -- a decorator cannot change that.
Btw. there is a draft PEP about this

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.

Simpler way to create dictionary of separate variables?

I would like to be able to get the name of a variable as a string but I don't know if Python has that much introspection capabilities. Something like:
>>> print(my_var.__name__)
'my_var'
I want to do that because I have a bunch of variables I'd like to turn into a dictionary like :
bar = True
foo = False
>>> my_dict = dict(bar=bar, foo=foo)
>>> print my_dict
{'foo': False, 'bar': True}
But I'd like something more automatic than that.
Python have locals() and vars(), so I guess there is a way.
As unwind said, this isn't really something you do in Python - variables are actually name mappings to objects.
However, here's one way to try and do it:
>>> a = 1
>>> for k, v in list(locals().iteritems()):
if v is a:
a_as_str = k
>>> a_as_str
a
>>> type(a_as_str)
'str'
I've wanted to do this quite a lot. This hack is very similar to rlotun's suggestion, but it's a one-liner, which is important to me:
blah = 1
blah_name = [ k for k,v in locals().iteritems() if v is blah][0]
Python 3+
blah = 1
blah_name = [ k for k,v in locals().items() if v is blah][0]
Are you trying to do this?
dict( (name,eval(name)) for name in ['some','list','of','vars'] )
Example
>>> some= 1
>>> list= 2
>>> of= 3
>>> vars= 4
>>> dict( (name,eval(name)) for name in ['some','list','of','vars'] )
{'list': 2, 'some': 1, 'vars': 4, 'of': 3}
This is a hack. It will not work on all Python implementations distributions (in particular, those that do not have traceback.extract_stack.)
import traceback
def make_dict(*expr):
(filename,line_number,function_name,text)=traceback.extract_stack()[-2]
begin=text.find('make_dict(')+len('make_dict(')
end=text.find(')',begin)
text=[name.strip() for name in text[begin:end].split(',')]
return dict(zip(text,expr))
bar=True
foo=False
print(make_dict(bar,foo))
# {'foo': False, 'bar': True}
Note that this hack is fragile:
make_dict(bar,
foo)
(calling make_dict on 2 lines) will not work.
Instead of trying to generate the dict out of the values foo and bar,
it would be much more Pythonic to generate the dict out of the string variable names 'foo' and 'bar':
dict([(name,locals()[name]) for name in ('foo','bar')])
This is not possible in Python, which really doesn't have "variables". Python has names, and there can be more than one name for the same object.
I think my problem will help illustrate why this question is useful, and it may give a bit more insight into how to answer it. I wrote a small function to do a quick inline head check on various variables in my code. Basically, it lists the variable name, data type, size, and other attributes, so I can quickly catch any mistakes I've made. The code is simple:
def details(val):
vn = val.__name__ # If such a thing existed
vs = str(val)
print("The Value of "+ str(vn) + " is " + vs)
print("The data type of " + vn + " is " + str(type(val)))
So if you have some complicated dictionary / list / tuple situation, it would be quite helpful to have the interpreter return the variable name you assigned. For instance, here is a weird dictionary:
m = 'abracadabra'
mm=[]
for n in m:
mm.append(n)
mydic = {'first':(0,1,2,3,4,5,6),'second':mm,'third':np.arange(0.,10)}
details(mydic)
The Value of mydic is {'second': ['a', 'b', 'r', 'a', 'c', 'a', 'd', 'a', 'b', 'r', 'a'], 'third': array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]), 'first': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
The data type of mydic is <type 'dict'>
details(mydic['first'])
The Value of mydic['first'] is (0, 1, 2, 3, 4, 5, 6)]
The data type of mydic['first'] is <type 'list'>
details(mydic.keys())
The Value of mydic.keys() is ['second', 'third', 'first']
The data type of mydic.keys() is <type 'tuple'>
details(mydic['second'][0])
The Value of mydic['second'][0] is a
The data type of mydic['second'][0] is <type 'str'>
I'm not sure if I put this in the right place, but I thought it might help. I hope it does.
I wrote a neat little useful function based on the answer to this question. I'm putting it here in case it's useful.
def what(obj, callingLocals=locals()):
"""
quick function to print name of input and value.
If not for the default-Valued callingLocals, the function would always
get the name as "obj", which is not what I want.
"""
for k, v in list(callingLocals.items()):
if v is obj:
name = k
print(name, "=", obj)
usage:
>> a = 4
>> what(a)
a = 4
>>|
I find that if you already have a specific list of values, that the way described by #S. Lotts is the best; however, the way described below works well to get all variables and Classes added throughout the code WITHOUT the need to provide variable name though you can specify them if you want. Code can be extend to exclude Classes.
import types
import math # mainly showing that you could import what you will before d
# Everything after this counts
d = dict(globals())
def kv_test(k,v):
return (k not in d and
k not in ['d','args'] and
type(v) is not types.FunctionType)
def magic_print(*args):
if len(args) == 0:
return {k:v for k,v in globals().iteritems() if kv_test(k,v)}
else:
return {k:v for k,v in magic_print().iteritems() if k in args}
if __name__ == '__main__':
foo = 1
bar = 2
baz = 3
print magic_print()
print magic_print('foo')
print magic_print('foo','bar')
Output:
{'baz': 3, 'foo': 1, 'bar': 2}
{'foo': 1}
{'foo': 1, 'bar': 2}
In python 3 this is easy
myVariable = 5
for v in locals():
if id(v) == id("myVariable"):
print(v, locals()[v])
this will print:
myVariable 5
Python3. Use inspect to capture the calling local namespace then use ideas presented here. Can return more than one answer as has been pointed out.
def varname(var):
import inspect
frame = inspect.currentframe()
var_id = id(var)
for name in frame.f_back.f_locals.keys():
try:
if id(eval(name)) == var_id:
return(name)
except:
pass
Here's the function I created to read the variable names. It's more general and can be used in different applications:
def get_variable_name(*variable):
'''gets string of variable name
inputs
variable (str)
returns
string
'''
if len(variable) != 1:
raise Exception('len of variables inputed must be 1')
try:
return [k for k, v in locals().items() if v is variable[0]][0]
except:
return [k for k, v in globals().items() if v is variable[0]][0]
To use it in the specified question:
>>> foo = False
>>> bar = True
>>> my_dict = {get_variable_name(foo):foo,
get_variable_name(bar):bar}
>>> my_dict
{'bar': True, 'foo': False}
In reading the thread, I saw an awful lot of friction. It's easy enough to give
a bad answer, then let someone give the correct answer. Anyway, here is what I found.
From: [effbot.org] (http://effbot.org/zone/python-objects.htm#names)
The names are a bit different — they’re not really properties of the object, and the object itself doesn't know what it’s called.
An object can have any number of names, or no name at all.
Names live in namespaces (such as a module namespace, an instance namespace, a function’s local namespace).
Note: that it says the object itself doesn’t know what it’s called, so that was the clue. Python objects are not self-referential. Then it says, Names live in namespaces. We have this in TCL/TK. So maybe my answer will help (but it did help me)
jj = 123
print eval("'" + str(id(jj)) + "'")
print dir()
166707048
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'jj']
So there is 'jj' at the end of the list.
Rewrite the code as:
jj = 123
print eval("'" + str(id(jj)) + "'")
for x in dir():
print id(eval(x))
161922920
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'jj']
3077447796
136515736
3077408320
3077656800
136515736
161922920
This nasty bit of code id's the name of variable/object/whatever-you-pedantics-call-it.
So, there it is. The memory address of 'jj' is the same when we look for it directly, as when we do the dictionary look up in global name space. I'm sure you can make a function to do this. Just remember which namespace your variable/object/wypci is in.
QED.
I wrote the package sorcery to do this kind of magic robustly. You can write:
from sorcery import dict_of
my_dict = dict_of(foo, bar)
Maybe I'm overthinking this but..
str_l = next((k for k,v in locals().items() if id(l) == id(v)))
>>> bar = True
>>> foo = False
>>> my_dict=dict(bar=bar, foo=foo)
>>> next((k for k,v in locals().items() if id(bar) == id(v)))
'bar'
>>> next((k for k,v in locals().items() if id(foo) == id(v)))
'foo'
>>> next((k for k,v in locals().items() if id(my_dict) == id(v)))
'my_dict'
import re
import traceback
pattren = re.compile(r'[\W+\w+]*get_variable_name\((\w+)\)')
def get_variable_name(x):
return pattren.match( traceback.extract_stack(limit=2)[0][3]) .group(1)
a = 1
b = a
c = b
print get_variable_name(a)
print get_variable_name(b)
print get_variable_name(c)
I uploaded a solution to pypi. It's a module defining an equivalent of C#'s nameof function.
It iterates through bytecode instructions for the frame its called in, getting the names of variables/attributes passed to it. The names are found in the .argrepr of LOAD instructions following the function's name.
Most objects don't have a __name__ attribute. (Classes, functions, and modules do; any more builtin types that have one?)
What else would you expect for print(my_var.__name__) other than print("my_var")? Can you simply use the string directly?
You could "slice" a dict:
def dict_slice(D, keys, default=None):
return dict((k, D.get(k, default)) for k in keys)
print dict_slice(locals(), ["foo", "bar"])
# or use set literal syntax if you have a recent enough version:
print dict_slice(locals(), {"foo", "bar"})
Alternatively:
throw = object() # sentinel
def dict_slice(D, keys, default=throw):
def get(k):
v = D.get(k, throw)
if v is not throw:
return v
if default is throw:
raise KeyError(k)
return default
return dict((k, get(k)) for k in keys)
Well, I encountered the very same need a few days ago and had to get a variable's name which was pointing to the object itself.
And why was it so necessary?
In short I was building a plug-in for Maya. The core plug-in was built using C++ but the GUI is drawn through Python(as its not processor intensive). Since I, as yet, don't know how to return multiple values from the plug-in except the default MStatus, therefore to update a dictionary in Python I had to pass the the name of the variable, pointing to the object implementing the GUI and which contained the dictionary itself, to the plug-in and then use the MGlobal::executePythonCommand() to update the dictionary from the global scope of Maya.
To do that what I did was something like:
import time
class foo(bar):
def __init__(self):
super(foo, self).__init__()
self.time = time.time() #almost guaranteed to be unique on a single computer
def name(self):
g = globals()
for x in g:
if isinstance(g[x], type(self)):
if g[x].time == self.time:
return x
#or you could:
#return filter(None,[x if g[x].time == self.time else None for x in g if isinstance(g[x], type(self))])
#and return all keys pointing to object itself
I know that it is not the perfect solution in in the globals many keys could be pointing to the same object e.g.:
a = foo()
b = a
b.name()
>>>b
or
>>>a
and that the approach isn't thread-safe. Correct me if I am wrong.
At least this approach solved my problem by getting the name of any variable in the global scope which pointed to the object itself and pass it over to the plug-in, as argument, for it use internally.
I tried this on int (the primitive integer class) but the problem is that these primitive classes don't get bypassed (please correct the technical terminology used if its wrong). You could re-implement int and then do int = foo but a = 3 will never be an object of foo but of the primitive. To overcome that you have to a = foo(3) to get a.name() to work.
With python 2.7 and newer there is also dictionary comprehension which makes it a bit shorter. If possible I would use getattr instead eval (eval is evil) like in the top answer. Self can be any object which has the context your a looking at. It can be an object or locals=locals() etc.
{name: getattr(self, name) for name in ['some', 'vars', 'here]}
I was working on a similar problem. #S.Lott said "If you have the list of variables, what's the point of "discovering" their names?" And my answer is just to see if it could be done and if for some reason you want to sort your variables by type into lists. So anyways, in my research I came came across this thread and my solution is a bit expanded and is based on #rlotun solution. One other thing, #unutbu said, "This idea has merit, but note that if two variable names reference the same value (e.g. True), then an unintended variable name might be returned." In this exercise that was true so I dealt with it by using a list comprehension similar to this for each possibility: isClass = [i for i in isClass if i != 'item']. Without it "item" would show up in each list.
__metaclass__ = type
from types import *
class Class_1: pass
class Class_2: pass
list_1 = [1, 2, 3]
list_2 = ['dog', 'cat', 'bird']
tuple_1 = ('one', 'two', 'three')
tuple_2 = (1000, 2000, 3000)
dict_1 = {'one': 1, 'two': 2, 'three': 3}
dict_2 = {'dog': 'collie', 'cat': 'calico', 'bird': 'robin'}
x = 23
y = 29
pie = 3.14159
eee = 2.71828
house = 'single story'
cabin = 'cozy'
isClass = []; isList = []; isTuple = []; isDict = []; isInt = []; isFloat = []; isString = []; other = []
mixedDataTypes = [Class_1, list_1, tuple_1, dict_1, x, pie, house, Class_2, list_2, tuple_2, dict_2, y, eee, cabin]
print '\nMIXED_DATA_TYPES total count:', len(mixedDataTypes)
for item in mixedDataTypes:
try:
# if isinstance(item, ClassType): # use this for old class types (before 3.0)
if isinstance(item, type):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isClass.append(mapping_as_str)
isClass = [i for i in isClass if i != 'item']
elif isinstance(item, ListType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isList.append(mapping_as_str)
isList = [i for i in isList if i != 'item']
elif isinstance(item, TupleType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isTuple.append(mapping_as_str)
isTuple = [i for i in isTuple if i != 'item']
elif isinstance(item, DictType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isDict.append(mapping_as_str)
isDict = [i for i in isDict if i != 'item']
elif isinstance(item, IntType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isInt.append(mapping_as_str)
isInt = [i for i in isInt if i != 'item']
elif isinstance(item, FloatType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isFloat.append(mapping_as_str)
isFloat = [i for i in isFloat if i != 'item']
elif isinstance(item, StringType):
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
isString.append(mapping_as_str)
isString = [i for i in isString if i != 'item']
else:
for k, v in list(locals().iteritems()):
if v is item:
mapping_as_str = k
other.append(mapping_as_str)
other = [i for i in other if i != 'item']
except (TypeError, AttributeError), e:
print e
print '\n isClass:', len(isClass), isClass
print ' isList:', len(isList), isList
print ' isTuple:', len(isTuple), isTuple
print ' isDict:', len(isDict), isDict
print ' isInt:', len(isInt), isInt
print ' isFloat:', len(isFloat), isFloat
print 'isString:', len(isString), isString
print ' other:', len(other), other
# my output and the output I wanted
'''
MIXED_DATA_TYPES total count: 14
isClass: 2 ['Class_1', 'Class_2']
isList: 2 ['list_1', 'list_2']
isTuple: 2 ['tuple_1', 'tuple_2']
isDict: 2 ['dict_1', 'dict_2']
isInt: 2 ['x', 'y']
isFloat: 2 ['pie', 'eee']
isString: 2 ['house', 'cabin']
other: 0 []
'''
you can use easydict
>>> from easydict import EasyDict as edict
>>> d = edict({'foo':3, 'bar':{'x':1, 'y':2}})
>>> d.foo
3
>>> d.bar.x
1
>>> d = edict(foo=3)
>>> d.foo
3
another example:
>>> d = EasyDict(log=False)
>>> d.debug = True
>>> d.items()
[('debug', True), ('log', False)]
On python3, this function will get the outer most name in the stack:
import inspect
def retrieve_name(var):
"""
Gets the name of var. Does it from the out most frame inner-wards.
:param var: variable to get name from.
:return: string
"""
for fi in reversed(inspect.stack()):
names = [var_name for var_name, var_val in fi.frame.f_locals.items() if var_val is var]
if len(names) > 0:
return names[0]
It is useful anywhere on the code. Traverses the reversed stack looking for the first match.
While this is probably an awful idea, it is along the same lines as rlotun's answer but it'll return the correct result more often.
import inspect
def getVarName(getvar):
frame = inspect.currentframe()
callerLocals = frame.f_back.f_locals
for k, v in list(callerLocals.items()):
if v is getvar():
callerLocals.pop(k)
try:
getvar()
callerLocals[k] = v
except NameError:
callerLocals[k] = v
del frame
return k
del frame
You call it like this:
bar = True
foo = False
bean = False
fooName = getVarName(lambda: foo)
print(fooName) # prints "foo"
should get list then return
def get_var_name(**kwargs):
"""get variable name
get_var_name(var = var)
Returns:
[str] -- var name
"""
return list(kwargs.keys())[0]
It will not return the name of variable but you can create dictionary from global variable easily.
class CustomDict(dict):
def __add__(self, other):
return CustomDict({**self, **other})
class GlobalBase(type):
def __getattr__(cls, key):
return CustomDict({key: globals()[key]})
def __getitem__(cls, keys):
return CustomDict({key: globals()[key] for key in keys})
class G(metaclass=GlobalBase):
pass
x, y, z = 0, 1, 2
print('method 1:', G['x', 'y', 'z']) # Outcome: method 1: {'x': 0, 'y': 1, 'z': 2}
print('method 2:', G.x + G.y + G.z) # Outcome: method 2: {'x': 0, 'y': 1, 'z': 2}
With python-varname you can easily do it:
pip install python-varname
from varname import Wrapper
foo = Wrapper(True)
bar = Wrapper(False)
your_dict = {val.name: val.value for val in (foo, bar)}
print(your_dict)
# {'foo': True, 'bar': False}
Disclaimer: I'm the author of that python-varname library.
>>> a = 1
>>> b = 1
>>> id(a)
34120408
>>> id(b)
34120408
>>> a is b
True
>>> id(a) == id(b)
True
this way get varname for a maybe 'a' or 'b'.

Categories