Setting parameter names and defaults in method created at runtime - python

I need to give an automatically constructed method positional and default parameters. If I didn't care about the parameter names or defaults I would have the method take *args and **kwargs. Here's an example of what I'm trying to do (note that
def make_method(params):
# Create method that's parameters are defined in params.
return method
params = {'param1': 'default_value', 'param2': None}
method = make_method(params)
For each non-None value I want to create a positional argument in method and for each None value I want to create a default parameter.
The problem is that I don't know how to set the parameter names and default values at runtime. I don't want to use *args and **kwars for method since it'd be better to have the signature correct when using help() etc.. Is there any way I can do this?

This is a HACK! Also I am not sure I understand how to generate kwargs based on what you asked. But this is a start. Although, I am not sure if it's a good start :)
def make_method(params):
def impl(*args, **kwargs):
# do stuff with args & kwargs
print "I was called with {args} and {kwargs}".format(
args=args,
kwargs=kwargs)
pass
name = 'some_name'
scope = dict(__impl=impl)
exec """\
def {name}({pos_arg_defs}):
'Very helpful function.'
return __impl({pos_arg_names})
""".format(name=name,
pos_arg_defs=', '.join('='.join((k, repr(v)))
for k, v in params.iteritems()
if v is not None),
pos_arg_names=', '.join(k
for k, v in params.iteritems()
if v is not None)) in scope
return scope[name]
Here's the some REPL output:
>>> help(make_method(dict(foo=42, bar=None, zomg='abc')))
Help on function some_name:
some_name(zomg='abc', foo=42)
Very helpful function.

Related

Default argument for a multiple argument function only when no argument supplied

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

How to pop elements from another function's kwargs?

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.

function not recognizing args and kwargs

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

python check if function accepts **kwargs

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.

In Python, what does dict.pop(a,b) mean?

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')
>>>

Categories