packing named arguments into a dict - python

I know I can turn function arguments into a dictionary if the function takes in **kwargs.
def bar(**kwargs):
return kwargs
print bar(a=1, b=2)
{'a': 1, 'b': 2}
However, is the opposite true? Can I pack named arguments into a dictionary and return them? The hand-coded version looks like this:
def foo(a, b):
return {'a': a, 'b': b}
But it seems like there must be a better way. Note that i am trying to avoid using **kwargs in the function (named arguments work better for an IDE with code completion).

It sounds like you are looking for locals:
>>> def foo(a, b):
... return locals()
...
>>> foo(1, 2)
{'b': 2, 'a': 1}
>>> def foo(a, b, c, d, e):
... return locals()
...
>>> foo(1, 2, 3, 4, 5)
{'c': 3, 'b': 2, 'a': 1, 'e': 5, 'd': 4}
>>>
Note however that this will return a dictionary of all names that are within the scope of foo:
>>> def foo(a, b):
... x = 3
... return locals()
...
>>> foo(1, 2)
{'b': 2, 'a': 1, 'x': 3}
>>>
This shouldn't be a problem if your functions are like that given in your question. If it is however, you can use inspect.getfullargspec and a dictionary comprehension to filter locals():
>>> def foo(a, b):
... import inspect # 'inspect' is a local name
... x = 3 # 'x' is another local name
... args = inspect.getfullargspec(foo).args
... return {k:v for k,v in locals().items() if k in args}
...
>>> foo(1, 2) # Only the argument names are returned
{'b': 2, 'a': 1}
>>>

Related

Conditionally including multiple keyword arguments in a function call

What is the 'pythonic' way to implement a waterfall if-statement like situation like this, when it applies to kwargs? I'm trying to avoid a situation where c=None is added to kwargs (because having c in the kwargs keys causes all sorts of problems downstream).
def run_something(**kwargs):
print(kwargs)
def func(a = None, b=None, c=None):
if a and b and c:
run_something(a=a,b=b,c=c)
elif a and b:
run_something(a=a,b=b)
elif a:
run_something(a=a)
else:
run_something()
I know the easy answer is that I could just do:
def func(**kwargs):
run_something(**kwargs)
however my particular use case doesn't make this easy
First of all, instead of doing tests like if a: I am guessing what you really want to do is only pass the a argument of it isn't None so testing if a is not None: will pass a value for a that is 0 or an empty string, i.e. anything other than the default. So this is what I would do:
def run_something(**kwargs):
print(kwargs)
def func(a = None, b=None, c=None):
d = {}
if a is not None:
d['a'] = a
if b is not None:
d['b'] = b
if c is not None:
d['c'] = c
run_something(**d)
func(c=7, b=4)
Prints:
{'b': 4, 'c': 7}
Note that the above handles any combination of passed arguments, which your "waterfall" if statements do not. If you really want to test for any non-False value as in your original code, then of course you can do:
def run_something(**kwargs):
print(kwargs)
def func(a=None, b=None, c=None):
d = {}
if a:
d['a'] = a
if b:
d['b'] = b
if c:
d['c'] = c
run_something(**d)
func(c=7, b=4)
You can do something like this:
def run_something(**kwargs):
print(kwargs)
def func(a=None, b=None, c=None):
kwargs = {"a": a, "b": b, "c": c}
run_something(**{key_word: arg for key_word, arg in kwargs.items() if arg is not None})
func() # {}
func("a") # {'a': 'a'}
func("a", "b") # {'a': 'a', 'b': 'b'}
func("a", "b", "c") # {'a': 'a', 'b': 'b', 'c': 'c'}
func(b="b") # {'b': 'b'}
func("a", c="c") # {'a': 'a', 'c': 'c'}
func(b="b", c="c") # {'b': 'b', 'c': 'c'}
func(c="c") # {'c': 'c'}
this will handle any combination of a b and c
I used dictionary comprehension.
in case you don't know what it means you can check this out python dictionary comprehension
Short and sweet:
def foo(a=None, b=None, c=None):
run_something(
**({ 'a': a } if a else {}),
**({ 'b': b } if b else {}),
**({ 'c': c } if c else {}),
)
Whether it’s ‘pythonic’, I will not say. Personally, I find it a meaningless notion, vague and ad-hoc at best and self-contradictory at worst.

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}

How to build arguments for a python function in a variable

I have a Python (3) function
def f(a, b, c=1, d=2):
pass
I would like to build up a list of arguments in code. The actual use-case is to do this based on some command line argument parsing, but it's a general question (I'm fairly new to python) e.g.
myargs = {}
myargs['positionalParams'] = ['foo', 'bar']
myargs['c']=2
myargs['d']=3
f(myargs)
I know that I can do f(*somelist) to expand a list into separate args, but how to do that with a mix of positional and optional params? Or do I do it twice, once for positionals and once for a dict of others?
args = ['foo', 'bar']
kwargs = {'c': 2, 'd': 3}
f(*args, **kwargs)
Note that this will also do just fine:
kwargs = {'a': 'foo', 'b': 'bar', 'c': 2, 'd': 3}
f(**kwargs)
how to do that with a mix of positional and optional params?
You can pass values to positional arguments also with a dictionary by unpacking it like this
>>> def func(a, b, c=1, d=2):
... print(a, b, c, d)
...
...
>>> myargs = {"a": 4, "b": 3, "c": 2, "d": 1}
>>> func(**myargs)
4 3 2 1
Or do I do it twice, once for positionals and once for a dict of others?
Yes. You can unpack the values for the positional arguments also, like this
>>> pos_args = 5, 6
>>> keyword_args = {"c": 7, "d": 8}
>>> func(*pos_args, **keyword_args)
5 6 7 8
Or you might choose to pass the values explicitly, like this
>>> func(9, 10, **keyword_args)
9 10 7 8

how could I pass a collection of the arguments that a function needed?

For example, I have a function:
def foo(a, b, c):
pass
Now I have a dict:
d = {'a': 1, 'b': 2, 'c': 3}
I have to write something like:
foo(d['a'], d['b'], d['c'])
I'd like to know, could I just pass a collection of the arguments(like d) to the function?
Sure, you can pass a dict as kwargs:
def foo(a, b, c):
print a, b, c
d = {'a': 1, 'b': 2, 'c': 3}
foo(**d)
Output:
1 2 3
Use the dictionary unpacking operator (**):
foo(**d)
foo(**d) should work.
See http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists

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.

Categories

Resources