Assign contents of Python dict to multiple variables at once? - python

I would like to do something like this
def f():
return { 'a' : 1, 'b' : 2, 'c' : 3 }
{ a, b } = f() # or { 'a', 'b' } = f() ?
I.e. so that a gets assigned 1, b gets 2, and c is undefined
This is similar to this
def f()
return( 1,2 )
a,b = f()

It wouldn't make any sense for unpacking to depend on the variable names. The closest you can get is:
a, b = [f()[k] for k in ('a', 'b')]
This, of course, evaluates f() twice.
You could write a function:
def unpack(d, *keys)
return tuple(d[k] for k in keys)
Then do:
a, b = unpack(f(), 'a', 'b')
This is really all overkill though. Something simple would be better:
result = f()
a, b = result['a'], result['b']

Consider making f a namedtuple Then you can just use f.a, f.b directly

Hmm. Kind of odd since a dictionary is not ordered, so the value unpacking depends on the variable names. But, it's possible, if ugly:
>>> locals().update(f())
>>> a
1
Don't try this at home! It's a maintainability nightmare. But kinda cool too :-)

mydict = {'aa':2, 'bb':'john', 'cc':34, 'dd':'bye'}
a, b, c, d = [mydict[k] for k in ['aa', 'bb', 'cc', 'dd']]

You could use (or abuse) a function attribute to do this:
>>> def f():
... f.a=1
... f.b=2
... return { 'a' : f.a, 'b' : f.b, 'c' : 3 }
...
>>> f()
{'a': 1, 'c': 3, 'b': 2}
>>> a,b=f.a,f.b
>>> a,b
(1, 2)
Be aware that the attributes only have value after f() is called or manually assigned.
I (rarely) use function attributes as a fill-in for C's static class variables like so:
>>> def f(i):
... f.static+=i
... return f.static
>>> f.static=0
>>> f(1)
1
>>> f(3)
4
There are better ways to do this, but just to be complete...

Related

Creating a dict from a set of variables using their names [duplicate]

I quite regularly want to create a dictionary where keys are variable names. For example if I have variables a and b I want to generate: {"a":a, "b":b} (typically to return data at the end of a function).
Are there any (ideally built in) ways in python to do this automatically? i.e to have a function such that create_dictionary(a,b) returns {"a":a, "b":b}
Have you considered creating a class? A class can be viewed as a wrapper for a dictionary.
# Generate some variables in the workspace
a = 9; b = ["hello", "world"]; c = (True, False)
# Define a new class and instantiate
class NewClass(object): pass
mydict = NewClass()
# Set attributes of the new class
mydict.a = a
mydict.b = b
mydict.c = c
# Print the dict form of the class
mydict.__dict__
{'a': 9, 'b': ['hello', 'world'], 'c': (True, False)}
Or you could use the setattr function if you wanted to pass a list of variable names:
mydict = NewClass()
vars = ['a', 'b', 'c']
for v in vars:
setattr(mydict, v, eval(v))
mydict.__dict__
{'a': 9, 'b': ['hello', 'world'], 'c': (True, False)}
You can write your own function for create_dict
def create_dict(*args):
return dict({i:eval(i) for i in args})
a = "yo"
b = 7
print (create_dict("a", "b"))
Which gives {'a': 'yo', 'b': 7} output.
Here's a simple generator for the same:
vars = ["a", "b"]
create_dict = {i:eval(i) for i in args}
or you can use this one-liner lambda function
create_dict = lambda *args: {i:eval(i) for i in args}
print (create_dict("a", "b"))
But if you want to pass the variables to the function instead of the variable name as string, then its pretty messy to actually get the name of the variable as a string. But if thats the case then you should probably try using locals(), vars(), globals() as used by Nf4r
Extending on the code of #Nf4r, I use something like:
a, b = 1, 2
def make_dict(*args):
# Globals will change of size, so we need a copy
g = {k: v for k, v in globals().items() if not k.startswith('__')}
result = {}
for arg in args:
for k, v in g.items():
try:
if v == arg:
result[k] = v
except ValueError:
continue # objects that don't allow comparison
return result
make_dict(a, b)
Have you tried something like:
a, b, c, d = 1, 2, 3, 4
dt = {k:v for k, v in locals().items() if not k.startswith('__')}
print(dt)
{'a': 1, 'd': 4, 'b': 2, 'c': 3}

Python: can we get the value from the dict or object like the ts?

Isn't the python can get the object or dict like ts:
let test = {a: 1, b: 2, c:3}
const {a, b} = test
The a and b are the key and the variable, it looks simple and not need two line code or for...in to get the a and b.
You can do something like this.
>>>
>>> d = {"a": 1, "b": 2, "c": 3}
>>>
>>> a, b, c = (lambda a, b, c: (a, b, c))(**d)
>>> a
1
>>> b
2
>>> c
3
>>>
Here is another example where we will only get few values.
>>> d2 = {"a": 1, "b": 2, "c": 3, "d": 4}
>>> a, b = (lambda a, b, **kwargs: (a, b))(**d2)
>>> a
1
>>> b
2
>>>
The closest you'll get is something along the lines of:
a, b = test['a'], test['b'] # dicts
a, b = test.a, test.b # objects
Or, less repetitive if you have more:
a, b, c = (test[i] for i in ('a', 'b', 'c')) # dicts
a, b, c = (getattr(test, i) for i in ('a', 'b', 'c')) # objects
You could probably do funky things with using the locals() or globals() dicts and .updateing them with a set intersection for example, but you should always explicitly declare variables and not muck around with scope dicts.
Just use two lines. Anything else which works in one line is too clever, too hard to understand, and too easy to break in the future:
test = {'a': 1, 'b': 2, 'c':3}
a = test['a']
b = test['b']

Is it possible to "unpack" a dict in one call?

I was looking for a way to "unpack" a dictionary in a generic way and found a relevant question (and answers) which explained various techniques (TL;DR: it is not too elegant).
That question, however, addresses the case where the keys of the dict are not known, the OP anted to have them added to the local namespace automatically.
My problem is possibly simpler: I get a dict from a function and would like to dissecate it on the fly, knowing the keys I will need (I may not need all of them every time). Right now I can only do
def myfunc():
return {'a': 1, 'b': 2, 'c': 3}
x = myfunc()
a = x['a']
my_b_so_that_the_name_differs_from_the_key = x['b']
# I do not need c this time
while I was looking for the equivalent of
def myotherfunc():
return 1, 2
a, b = myotherfunc()
but for a dict (which is what is returned by my function). I do not want to use the latter solution for several reasons, one of them being that it is not obvious which variable corresponds to which returned element (the first solution has at least the merit of being readable).
Is such operation available?
If you really must, you can use an operator.itemgetter() object to extract values for multiple keys as a tuple:
from operator import itemgetter
a, b = itemgetter('a', 'b')(myfunc())
This is still not pretty; I'd prefer the explicit and readable separate lines where you first assign the return value, then extract those values.
Demo:
>>> from operator import itemgetter
>>> def myfunc():
... return {'a': 1, 'b': 2, 'c': 3}
...
>>> itemgetter('a', 'b')(myfunc())
(1, 2)
>>> a, b = itemgetter('a', 'b')(myfunc())
>>> a
1
>>> b
2
You could also use map:
def myfunc():
return {'a': 1, 'b': 2, 'c': 3}
a,b = map(myfunc().get,["a","b"])
print(a,b)
In addition to the operator.itemgetter() method, you can also write your own myotherfunc(). It takes list of the required keys as an argument and returns a tuple of their corresponding value.
def myotherfunc(keys_list):
reference_dict = myfunc()
return tuple(reference_dict[key] for key in keys_list)
>>> a,b = myotherfunc(['a','b'])
>>> a
1
>>> b
2
>>> a,c = myotherfunc(['a','c'])
>>> a
1
>>> c
3

How to iterate over function arguments

I have a Python function accepting several string arguments def foo(a, b, c): and concatenating them in a string.
I want to iterate over all function arguments to check they are not None. How it can be done?
Is there a quick way to convert None to ""?
Thanks.
locals() may be your friend here if you call it first thing in your function.
Example 1:
>>> def fun(a, b, c):
... d = locals()
... e = d
... print e
... print locals()
...
>>> fun(1, 2, 3)
{'a': 1, 'c': 3, 'b': 2}
{'a': 1, 'c': 3, 'b': 2, 'e': {...}, 'd': {...}}
Example 2:
>>> def nones(a, b, c, d):
... arguments = locals()
... print 'The following arguments are not None: ', ', '.join(k for k, v in arguments.items() if v is not None)
...
>>> nones("Something", None, 'N', False)
The following arguments are not None: a, c, d
Answer:
>>> def foo(a, b, c):
... return ''.join(v for v in locals().values() if v is not None)
...
>>> foo('Cleese', 'Palin', None)
'CleesePalin'
Update:
'Example 1' highlights that we may have some extra work to do if the order of your arguments is important as the dict returned by locals() (or vars()) is unordered. The function above also doesn't deal with numbers very gracefully. So here are a couple of refinements:
>>> def foo(a, b, c):
... arguments = locals()
... return ''.join(str(arguments[k]) for k in sorted(arguments.keys()) if arguments[k] is not None)
...
>>> foo(None, 'Antioch', 3)
'Antioch3'
def func(*args):
' '.join(i if i is not None else '' for i in args)
if you're joining on an empty string, you could just do ''.join(i for i in args if i is not None)
You can use the inspect module and define a function like that:
import inspect
def f(a,b,c):
argspec=inspect.getargvalues(inspect.currentframe())
return argspec
f(1,2,3)
ArgInfo(args=['a', 'b', 'c'], varargs=None, keywords=None, locals={'a': 1, 'c': 3, 'b': 2})
in argspec there are all the info you need to perform any operation with argument passed.
To concatenate the string is sufficient to use the arg info received:
def f(a,b,c):
argspec=inspect.getargvalues(inspect.currentframe())
return ''.join(argspec.locals[arg] for arg in argspec.args)
For reference:
http://docs.python.org/library/inspect.html#inspect.getargvalues
Is this perhaps what you'd like?
def foo(a, b, c):
"SilentGhost suggested the join"
' '.join(i if i is not None else '' for i in vars().values())
def bar(a,b,c):
"A very usefull method!"
print vars()
print vars().values()
Notice the use of vars(), which returns a dict.
I would use sed s/None//g, but that's not in python, but you can probably use os.popen() to do that.

Destructuring-bind dictionary contents

I am trying to 'destructure' a dictionary and associate values with variables names after its keys. Something like
params = {'a':1,'b':2}
a,b = params.values()
But since dictionaries are not ordered, there is no guarantee that params.values() will return values in the order of (a, b). Is there a nice way to do this?
from operator import itemgetter
params = {'a': 1, 'b': 2}
a, b = itemgetter('a', 'b')(params)
Instead of elaborate lambda functions or dictionary comprehension, may as well use a built in library.
One way to do this with less repetition than Jochen's suggestion is with a helper function. This gives the flexibility to list your variable names in any order and only destructure a subset of what is in the dict:
pluck = lambda dict, *args: (dict[arg] for arg in args)
things = {'blah': 'bleh', 'foo': 'bar'}
foo, blah = pluck(things, 'foo', 'blah')
Also, instead of joaquin's OrderedDict you could sort the keys and get the values. The only catches are you need to specify your variable names in alphabetical order and destructure everything in the dict:
sorted_vals = lambda dict: (t[1] for t in sorted(dict.items()))
things = {'foo': 'bar', 'blah': 'bleh'}
blah, foo = sorted_vals(things)
How come nobody posted the simplest approach?
params = {'a':1,'b':2}
a, b = params['a'], params['b']
Python is only able to "destructure" sequences, not dictionaries. So, to write what you want, you will have to map the needed entries to a proper sequence. As of myself, the closest match I could find is the (not very sexy):
a,b = [d[k] for k in ('a','b')]
This works with generators too:
a,b = (d[k] for k in ('a','b'))
Here is a full example:
>>> d = dict(a=1,b=2,c=3)
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> a, b = [d[k] for k in ('a','b')]
>>> a
1
>>> b
2
>>> a, b = (d[k] for k in ('a','b'))
>>> a
1
>>> b
2
Here's another way to do it similarly to how a destructuring assignment works in JS:
params = {'b': 2, 'a': 1}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
What we did was to unpack the params dictionary into key values (using **) (like in Jochen's answer), then we've taken those values in the lambda signature and assigned them according to the key name - and here's a bonus - we also get a dictionary of whatever is not in the lambda's signature so if you had:
params = {'b': 2, 'a': 1, 'c': 3}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
After the lambda has been applied, the rest variable will now contain:
{'c': 3}
Useful for omitting unneeded keys from a dictionary.
Hope this helps.
Maybe you really want to do something like this?
def some_func(a, b):
print a,b
params = {'a':1,'b':2}
some_func(**params) # equiv to some_func(a=1, b=2)
If you are afraid of the issues involved in the use of the locals dictionary and you prefer to follow your original strategy, Ordered Dictionaries from python 2.7 and 3.1 collections.OrderedDicts allows you to recover you dictionary items in the order in which they were first inserted
(Ab)using the import system
The from ... import statement lets us desctructure and bind attribute names of an object. Of course, it only works for objects in the sys.modules dictionary, so one could use a hack like this:
import sys, types
mydict = {'a':1,'b':2}
sys.modules["mydict"] = types.SimpleNamespace(**mydict)
from mydict import a, b
A somewhat more serious hack would be to write a context manager to load and unload the module:
with obj_as_module(mydict, "mydict_module"):
from mydict_module import a, b
By pointing the __getattr__ method of the module directly to the __getitem__ method of the dict, the context manager can also avoid using SimpleNamespace(**mydict).
See this answer for an implementation and some extensions of the idea.
One can also temporarily replace the entire sys.modules dict with the dict of interest, and do import a, b without from.
Warning 1: as stated in the docs, this is not guaranteed to work on all Python implementations:
CPython implementation detail: This function relies on Python stack frame support
in the interpreter, which isn’t guaranteed to exist in all implementations
of Python. If running in an implementation without Python stack frame support
this function returns None.
Warning 2: this function does make the code shorter, but it probably contradicts the Python philosophy of being as explicit as you can. Moreover, it doesn't address the issues pointed out by John Christopher Jones in the comments, although you could make a similar function that works with attributes instead of keys. This is just a demonstration that you can do that if you really want to!
def destructure(dict_):
if not isinstance(dict_, dict):
raise TypeError(f"{dict_} is not a dict")
# the parent frame will contain the information about
# the current line
parent_frame = inspect.currentframe().f_back
# so we extract that line (by default the code context
# only contains the current line)
(line,) = inspect.getframeinfo(parent_frame).code_context
# "hello, key = destructure(my_dict)"
# -> ("hello, key ", "=", " destructure(my_dict)")
lvalues, _equals, _rvalue = line.strip().partition("=")
# -> ["hello", "key"]
keys = [s.strip() for s in lvalues.split(",") if s.strip()]
if missing := [key for key in keys if key not in dict_]:
raise KeyError(*missing)
for key in keys:
yield dict_[key]
In [5]: my_dict = {"hello": "world", "123": "456", "key": "value"}
In [6]: hello, key = destructure(my_dict)
In [7]: hello
Out[7]: 'world'
In [8]: key
Out[8]: 'value'
This solution allows you to pick some of the keys, not all, like in JavaScript. It's also safe for user-provided dictionaries
With Python 3.10, you can do:
d = {"a": 1, "b": 2}
match d:
case {"a": a, "b": b}:
print(f"A is {a} and b is {b}")
but it adds two extra levels of indentation, and you still have to repeat the key names.
Look for other answers as this won't cater to the unexpected order in the dictionary. will update this with a correct version sometime soon.
try this
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
keys = data.keys()
a,b,c = [data[k] for k in keys]
result:
a == 'Apple'
b == 'Banana'
c == 'Carrot'
Well, if you want these in a class you can always do this:
class AttributeDict(dict):
def __init__(self, *args, **kwargs):
super(AttributeDict, self).__init__(*args, **kwargs)
self.__dict__.update(self)
d = AttributeDict(a=1, b=2)
Based on #ShawnFumo answer I came up with this:
def destruct(dict): return (t[1] for t in sorted(dict.items()))
d = {'b': 'Banana', 'c': 'Carrot', 'a': 'Apple' }
a, b, c = destruct(d)
(Notice the order of items in dict)
An old topic, but I found this to be a useful method:
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
for key in data.keys():
locals()[key] = data[key]
This method loops over every key in your dictionary and sets a variable to that name and then assigns the value from the associated key to this new variable.
Testing:
print(a)
print(b)
print(c)
Output
Apple
Banana
Carrot
An easy and simple way to destruct dict in python:
params = {"a": 1, "b": 2}
a, b = [params[key] for key in ("a", "b")]
print(a, b)
# Output:
# 1 2
I don't know whether it's good style, but
locals().update(params)
will do the trick. You then have a, b and whatever was in your params dict available as corresponding local variables.
Since dictionaries are guaranteed to keep their insertion order in Python >= 3.7, that means that it's complete safe and idiomatic to just do this nowadays:
params = {'a': 1, 'b': 2}
a, b = params.values()
print(a)
print(b)
Output:
1
2

Categories

Resources