I am trying to define a function like so:
def get_event_stats(elengths, einds, *args, **kwargs):
master_list = []
if avg:
for arg in args:
do stuff...
if tot:
for arg in args:
do stuff...
return master_list
I would like elengths and einds to be fixed positional args (these are just arrays of ints). I am trying to use the function by passing it a variable length list of arrays as *args and some **kwargs, in this example two (avg and tot), but potentially more, for example,
avg_event_time = get_event_stats(event_lengths, eventInds, *alist, avg=True, tot=False)
where
alist = [time, counts]
and my kwargs are avg and tot, which are given the value of True and False respectively. Regardless of how I've tried to implement this function, I get some kind of error. What am I missing here in the correct usage of *args and **kwargs?
**kwargs creates a dict, it doesn't inject arbitrary names into your local namespace. If you want to look for whether a particular keyword was passed, you can't test if avg: (there is no variable named avg), you need to check if avg is in the dict, e.g. if 'avg' in kwargs:, or to check both existence and "truthiness", so passing avg=False is equivalent to not passing it at all, test if kwargs.get('avg'): (using kwargs.get('avg') ensures no exception is thrown if avg wasn't passed at all, unlike if kwargs['avg']:).
Note: You should really move to Python 3 if at all possible. It makes writing this function much more obvious and clean, as you could avoid the need for kwargs completely, and verify no unrecognized keyword arguments were passed by just defining the function as:
def get_event_stats(elengths, einds, *args, avg=False, tot=False):
master_list = []
if avg:
for arg in args:
do stuff...
if tot:
for arg in args:
do stuff...
return master_list
Note how the body of the function you already wrote works without modification if you explicitly name your keyword arguments after the positional varargs, making your code far more self-documenting (as well as more efficient, and with better self-checks; the clean Py3 code will error out informing you of the unrecognized argument if you pass avrg=True to it, while the **kwargs approach would require explicit checks for unknown arguments that would slow you down and bloat the code.
The closest you could get to the Py3 error-checks with minimal overhead and similar correctness/readability would be:
def get_event_stats(elengths, einds, *args, **kwargs):
master_list = []
# Unpack the recognized arguments (with default values), so kwargs left should be empty
avg = kwargs.pop('avg', False)
tot = kwargs.pop('tot', False)
# If any keywords left over, they're unrecognized, raise an error
if kwargs:
# Arbitrarily select alphabetically first unknown keyword arg
raise TypeError('get_event_stats() got an unexpected keyword argument {!r}'.format(min(kwargs)))
if avg:
for arg in args:
do stuff...
if tot:
for arg in args:
do stuff...
return master_list
If you meant that avg and tot should be passed in as keyword args, like in your example get_event_stats(..., avg=True, tot=False) then they are populated in kwargs. You can look them up in the kwargs dict using a key lookup (like kwargs['avg'].
However if they are not present at all, then that will give a key error, so use it with the dict.get() method: kwargs.get('avg') which returns None if it is not present, which is boolean False. Or use kwargs.get('avg', False) if you explicitly want a False if it's not present.
def get_event_stats(elengths, einds, *args, **kwargs):
master_list = []
if kwargs.get('avg'):
for arg in args:
do stuff...
if kwargs.get('tot'):
for arg in args:
do stuff...
return master_list
Related
Let's consider a function that accepts multiple arguments as under:
def my_function(*args):
my_list = []
for _ in args:
my_list.append(_)
another_function(my_list)
Now, the issue that I face in my scenario is that: I need my_list to contain at least one value, any argument, but at least one.
A user can do my_function(arg1), my_fuynction(arg1,arg2), my_function(arg1,arg2,arg3) and so on. But if a user does my_function(), I need to provide a default argument (say arg1) to the function.
If I put an default argument, then the argument defaultarg will be compulsory and it has to be supplied with some value:
def my_function(defaultarg, *args):
#function
If I put it as optional argument, then I will have to provide a default value to the argument:
def my_function(optionalarg = defaultvalue, *args):
#function
Both these ways do not work for me as I can't make it compulsory and I can't give a default value for it.
How do I create this function so that if no arguments are passed, the function assumes one argument to have been passed?
As of now, I am handling this by putting a if...else in the function as under:
def my_function(*args):
my_list = []
if len(args) == 0:
my_list.append('defaultarg')
else:
for _ in args:
my_list.append(_)
another_function(my_list)
Is there any better way to do this?
I can't give a default value for it
I don't understand this part of your question. You are providing the default value 'defaultarg' in your own solution?! My answer assumes that you can get the default value from somewhere.
Anyway, if you want to keep the f(*args) signature, you can check whether the args tuple is empty and supply a default value.
def my_function(*args):
if not args:
args = (default_value,)
# more code
or
def my_function(*args):
if not args:
return my_function(default_value)
# more code
You won't get around explicitly checking whether args is empty, but maybe you'll like one of these proposals better than your version.
edit:
You could also write a parameterized decorator.
def with_default_value(default_value):
def with_default_value_decorator(f):
def f_new(*args):
if not args:
return f(default_value)
return f(*args)
return f_new
return with_default_value_decorator
Example:
#with_default_value('hi')
def my_print(*args):
print(' '.join(args))
Demo:
>>> my_print('hello', 'world')
hello world
>>> my_print()
hi
I have a function that is responsible for getting data from the kwargs of several other functions.
The other functions pass their own kwargs to this function along with a keep argument that determines whether or not to keep these properties in the kwargs - i.e. whether to use get or pop.
def _handle_kwargs(keep, **kwargs):
# keep: whether to keep the kwarg when we're done with it (i.e. get or pop)
if keep: func = getattr(kwargs, 'get')
else: func = getattr(kwargs, 'pop')
# get or pop some kwargs individually
debug = func('debug', False)
assert isinstance(debug, bool)
...
# repeated for several different possible kwargs
return debug, some_other_kwarg, ...
def normal_function(**kwargs)
debug, some_other_kwarg = _handle_kwargs(False, **kwargs)
Getting the values from the kwargs works fine. However, if I try to pop the kwargs, then they are still present in the original function's kwargs. I suspect this is because _handle_kwargs is only modifying its own kwargs.
How can I ensure that the kwargs are removed if I use pop, even if that's coming from another function?
I doubt you can do that passing to **kwargs, as it appears to be passed by value, but if it's ok to modify the inner function, you could pass kwargs as a plain dictionary, i.e. without the **.
def test(x):
print(x)
x.pop('test')
print(x)
def real(**kwargs):
test(kwargs)
print(kwargs)
real(test='nothing', real='something')
Output
{'test': 'nothing', 'real': 'something'}
{'real': 'something'}
{'real': 'something'}
The problem is that you don't pass a dictionary to _handle_kwargs. The **kwargs syntax when calling a function actually "explodes" kwargs.
That is, if kwargs is {'a':1, 'b':2}, then _handle_kwargs(False, **kwargs) is equivalent to _handle_kwargs(False, kwargs['a'], kwargs['b']). You don't pass the kwargs dict at all!
_handle_kwargs collects them into a new dictionary, so it won't affect the original one.
The solution is very simple.
First, def _handle_kwargs(keep, kwargs): without asterisks. Just receive a dict.
Second, call it like so:
def normal_function(**kwargs)
debug, some_other_kwarg = _handle_kwargs(False, kwargs)
See the second line - calling _handle_kwargs without asterisks - just pass the dict.
I do not understand the following example, let's say I have these functions:
# python likes
def save(filename, data, **kwargs):
fo = openX(filename, "w", **kwargs) # <- #1
fo.write(data)
fo.close()
# python doesnt like
def save2(filename, data, **kwargs):
fo = openX(filename, "w", kwargs) # <- #2
fo.write(data)
fo.close()
def openX(filename, mode, **kwargs):
#doing something fancy and returning a file object
Why is #1 the right solution and #2 the wrong one? **kwargs is basically a dict, so if I want to pass down the argument to openX I think the correct way would be without ** and just giving the dict. But Python obviously doesn't like the second one and tells me I gave 3 instead of 2 arguments.
So what's the reason behind this?
In the second example you provide 3 arguments: filename, mode and a dictionary (kwargs). But Python expects: 2 formal arguments plus keyword arguments.
By prefixing the dictionary by '**' you unpack the dictionary kwargs to keywords arguments.
A dictionary (type dict) is a single variable containing key-value pairs.
"Keyword arguments" are key-value method-parameters.
Any dictionary can by unpacked to keyword arguments by prefixing it with ** during function call.
Expanding on #gecco 's answer, the following is an example that'll show you the difference:
def foo(**kwargs):
for entry in kwargs.items():
print("Key: {}, value: {}".format(entry[0], entry[1]))
# call using normal keys:
foo(a=1, b=2, c=3)
# call using an unpacked dictionary:
foo(**{"a": 1, "b":2, "c":3})
# call using a dictionary fails because the function will think you are
# giving it a positional argument
foo({"a": 1, "b": 2, "c": 3})
# this yields the same error as any other positional argument
foo(3)
foo("string")
Here you can see how unpacking a dictionary works, and why sending an actual dictionary fails
The ** syntax tells Python to collect keyword arguments into a dictionary. The save2 is passing it down as a non-keyword argument (a dictionary object). The openX is not seeing any keyword arguments so the **args doesn't get used. It's instead getting a third non-keyword argument (the dictionary). To fix that change the definition of the openX function.
def openX(filename, mode, kwargs):
pass
For #2
args will be only a formal parameter with dict value, but not a keyword type parameter.
If you want to pass a keyword type parameter into a keyword argument
You need to specific ** before your dictionary, which means **args
check this out for more detail on using **kw
http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
Because a dictionary is a single value. You need to use keyword expansion if you want to pass it as a group of keyword arguments.
The following code use kwargs and transfer it to another function:
def myprint( kwargs ):
# default values
a = kwargs.get('a', None)
b = kwargs.get('b', None)
# print both
print('a={}, b={}'.format(a,b))
def mytest( **kwargs ):
myprint( kwargs )
mytest()
mytest(b=2)
yields:
a=None, b=None
a=None, b=2
is there a way to check if a function accepts **kwargs before calling it e.g.
def FuncA(**kwargs):
print 'ok'
def FuncB(id = None):
print 'ok'
def FuncC():
print 'ok'
args = {'id': '1'}
FuncA(**args)
FuncB(**args)
FuncC(**args)
When I run this FuncA and FuncB would be okay but FuncC errors with got an unexpected keyword argument 'id' as it doesn't accept any arguments
try:
f(**kwargs)
except TypeError:
#do stuff
It's easier to ask forgiveness than permission.
def foo(a, b, **kwargs):
pass
import inspect
args, varargs, varkw, defaults = inspect.getargspec(foo)
assert(varkw=='kwargs')
This only works for Python functions. Functions defined in C extensions (and built-ins) may be tricky and sometimes interpret their arguments in quite creative ways. There's no way to reliably detect which arguments such functions expect. Refer to function's docstring and other human-readable documentation.
func is the function in question.
with python2, it's:
inspect.getargspec(func).keywords is not None
python3 is a bit tricker, following https://www.python.org/dev/peps/pep-0362/ the kind of parameter must be VAR_KEYWORD
Parameter.VAR_KEYWORD - a dict of keyword arguments that aren't bound to any other parameter. This corresponds to a "**kwargs" parameter in a Python function definition.
any(param for param in inspect.signature(func).parameters.values() if param.kind == param.VAR_KEYWORD)
For python > 3 you should to use inspect.getfullargspec.
import inspect
def foo(**bar):
pass
arg_spec = inspect.getfullargspec(foo)
assert arg_spec.varkw and arg_spec.varkw == 'bar'
Seeing that there are a multitude of different answers in this thread, I thought I would give my two cents, using inspect.signature().
Suppose you have this method:
def foo(**kwargs):
You can test if **kwargs are in this method's signature:
import inspect
sig = inspect.signature(foo)
params = sig.parameters.values()
has_kwargs = any([True for p in params if p.kind == p.VAR_KEYWORD])
More
Getting the parameters in which a method takes is also possible:
import inspect
sig = inspect.signature(foo)
params = sig.parameters.values()
for param in params:
print(param.kind)
You can also store them in a variable like so:
kinds = [param.kind for param in params]
# [<_ParameterKind.VAR_KEYWORD: 4>]
Other than just keyword arguments, there are 5 parameter kinds in total, which are as follows:
POSITIONAL_ONLY # parameters must be positional
POSITIONAL_OR_KEYWORD # parameters can be positional or keyworded (default)
VAR_POSITIONAL # *args
KEYWORD_ONLY # parameters must be keyworded
VAR_KEYWORD # **kwargs
Descriptions in the official documentation can be found here.
Examples
POSITIONAL_ONLY
def foo(a, /):
# the '/' enforces that all preceding parameters must be positional
foo(1) # valid
foo(a=1) #invalid
POSITIONAL_OR_KEYWORD
def foo(a):
# 'a' can be passed via position or keyword
# this is the default and most common parameter kind
VAR_POSITIONAL
def foo(*args):
KEYWORD_ONLY
def foo(*, a):
# the '*' enforces that all following parameters must by keyworded
foo(a=1) # valid
foo(1) # invalid
VAR_KEYWORD
def foo(**kwargs):
It appears that you want to check whether the function receives an 'id' keyword argument. You can't really do that by inspection because the function might not be a normal function, or you might have a situation like that:
def f(*args, **kwargs):
return h(*args, **kwargs)
g = lambda *a, **kw: h(*a, **kw)
def h(arg1=0, arg2=2):
pass
f(id=3) still fails
Catching TypeError as suggested is the best way to do that, but you can't really figure out what caused the TypeError. For example, this would still raise a TypeError:
def f(id=None):
return "%d" % id
f(**{'id': '5'})
And that might be an error that you want to debug. And if you're doing the check to avoid some side effects of the function, they might still be present if you catch it. For example:
class A(object):
def __init__(self): self._items = set([1,2,3])
def f(self, id): return self._items.pop() + id
a = A()
a.f(**{'id': '5'})
My suggestion is to try to identify the functions by another mechanism. For example, pass objects with methods instead of functions, and call only the objects that have a specific method. Or add a flag to the object or the function itself.
According to https://docs.python.org/2/reference/datamodel.html
you should be able to test for use of **kwargs using co_flags:
>>> def blah(a, b, kwargs):
... pass
>>> def blah2(a, b, **kwargs):
... pass
>>> (blah.func_code.co_flags & 0x08) != 0
False
>>> (blah2.func_code.co_flags & 0x08) != 0
True
Though, as noted in the reference this may change in the future, so I would definitely advise to be extra careful. Definitely add some unit tests to check this feature is still in place.
class a(object):
data={'a':'aaa','b':'bbb','c':'ccc'}
def pop(self, key, *args):
return self.data.pop(key, *args)#what is this mean.
b=a()
print b.pop('a',{'b':'bbb'})
print b.data
self.data.pop(key, *args) ←------ why is there a second argument?
The pop method of dicts (like self.data, i.e. {'a':'aaa','b':'bbb','c':'ccc'}, here) takes two arguments -- see the docs
The second argument, default, is what pop returns if the first argument, key, is absent.
(If you call pop with just one argument, key, it raises an exception if that key's absent).
In your example, print b.pop('a',{'b':'bbb'}), this is irrelevant because 'a' is a key in b.data. But if you repeat that line...:
b=a()
print b.pop('a',{'b':'bbb'})
print b.pop('a',{'b':'bbb'})
print b.data
you'll see it makes a difference: the first pop removes the 'a' key, so in the second pop the default argument is actually returned (since 'a' is now absent from b.data).
So many questions here. I see at least two, maybe three:
What does pop(a,b) do?/Why are there a second argument?
What is *args being used for?
The first question is trivially answered in the Python Standard Library reference:
pop(key[, default])
If key is in the dictionary, remove it and return its value, else return default.
If default is not given and key is not in the dictionary, a KeyError is raised.
The second question is covered in the Python Language Reference:
If the form “*identifier” is present,
it is initialized to a tuple receiving
any excess positional parameters,
defaulting to the empty tuple. If the
form “**identifier” is present, it is
initialized to a new dictionary
receiving any excess keyword
arguments, defaulting to a new empty
dictionary.
In other words, the pop function takes at least two arguments. The first two get assigned the names self and key; and the rest are stuffed into a tuple called args.
What's happening on the next line when *args is passed along in the call to self.data.pop is the inverse of this - the tuple *args is expanded to of positional parameters which get passed along. This is explained in the Python Language Reference:
If the syntax *expression appears in
the function call, expression must
evaluate to a sequence. Elements from
this sequence are treated as if they
were additional positional arguments
In short, a.pop() wants to be flexible and accept any number of positional parameters, so that it can pass this unknown number of positional parameters on to self.data.pop().
This gives you flexibility; data happens to be a dict right now, and so self.data.pop() takes either one or two parameters; but if you changed data to be a type which took 19 parameters for a call to self.data.pop() you wouldn't have to change class a at all. You'd still have to change any code that called a.pop() to pass the required 19 parameters though.
def func(*args):
pass
When you define a function this way, *args will be array of arguments passed to the function. This allows your function to work without knowing ahead of time how many arguments are going to be passed to it.
You do this with keyword arguments too, using **kwargs:
def func2(**kwargs):
pass
See: Arbitrary argument lists
In your case, you've defined a class which is acting like a dictionary. The dict.pop method is defined as pop(key[, default]).
Your method doesn't use the default parameter. But, by defining your method with *args and passing *args to dict.pop(), you are allowing the caller to use the default parameter.
In other words, you should be able to use your class's pop method like dict.pop:
my_a = a()
value1 = my_a.pop('key1') # throw an exception if key1 isn't in the dict
value2 = my_a.pop('key2', None) # return None if key2 isn't in the dict
>>> def func(a, *args, **kwargs):
... print 'a %s, args %s, kwargs %s' % (a, args, kwargs)
...
>>> func('one', 'two', 'three', four='four', five='five')
a one, args ('two', 'three'), kwargs {'four': 'four', 'five': 'five'}
>>> def anotherfunct(beta, *args):
... print 'beta %s, args %s' % (beta, args)
...
>>> def func(a, *args, **kwargs):
... anotherfunct(a, *args)
...
>>> func('one', 'two', 'three', four='four', five='five')
beta one, args ('two', 'three')
>>>