Python dynamic functions with keyword arguments avoid unused parameter - python

I have a part of code in python, which calls dynamically different functions, where I always want to pass 3 different arguments. However, these functions, might not always need to use those 3 different arguments.
Here is a very simple code that shows the issue:
def test_keyword_args():
def fn1(a, b, c):
return a + b
def fn2(a, b, c):
return a + c
def fn3(a, b, c):
return b + c
obj = {
'a': fn1,
'b': fn2,
'c': fn3,
}
for key in obj:
value = obj[key](a=1, b=2, c=3)
if key == 'a':
assert value == 3
if key == 'b':
assert value == 4
if key == 'c':
assert value == 5
How can I always call same function obj[key](a=1,b=2,c=3) passing this keyword arguments, and avoid complains about unused parameters? (c not used in fn1, b not used in fn2, a not used in fn3)
I can imagine suppressing warnings would do the trick, but I do not think it is the appropriate solution
I am using Python 3.7.3

You can define arguments as keyword only by prefixing the argument list with *, you can then avoid the unused parameter warnings by naming a parameter _. Using **_ allows us to ignore any keyword arguments not in our named parameters
def fn1(*, a, b, **_):
return a + b
def fn2(*, a, c, **_):
return a + c
def fn3(*, b, c, **_):
return b + c

You may use kwargs to pass key word arguments to a function. In such case kwargs is the dictionary with named arguments passed to function.
def fn1(a, b, **kwargs):
return a + b
You can pass anything to this function in format fn1(a_value, b_value, any_parameter_1=100, any_parameter_2=100) etc. In function you receive your variables a and b and also kwargs dictionary with following content
{
"any_parameter_1": 100,
"any_parameter_2": 1000,
}
Also you may pass all variables as kwargs
def fn1(**kwargs):
return kwargs["a"] + kwargs["b"]
But you need to assign names to your parameters like this fn1(a=a_value, b=b_value, any_parameter_1=100, any_parameter_2=100) and kwargs dictionary will looks like
{
"a": a_value,
"b": b_value,
"any_parameter_1": 100,
"any_parameter_2": 1000,
}

Related

equivalent to R's `do.call` in python

Is there an equivalent to R's do.call in python?
do.call(what = 'sum', args = list(1:10)) #[1] 55
do.call(what = 'mean', args = list(1:10)) #[1] 5.5
?do.call
# Description
# do.call constructs and executes a function call from a name or a function and a list of arguments to be passed to it.
There is no built-in for this, but it is easy enough to construct an equivalent.
You can look up any object from the built-ins namespace using the __builtin__ (Python 2) or builtins (Python 3) modules then apply arbitrary arguments to that with *args and **kwargs syntax:
try:
# Python 2
import __builtin__ as builtins
except ImportError:
# Python 3
import builtins
def do_call(what, *args, **kwargs):
return getattr(builtins, what)(*args, **kwargs)
do_call('sum', range(1, 11))
Generally speaking, we don't do this in Python. If you must translate strings into function objects, it is generally preferred to build a custom dictionary:
functions = {
'sum': sum,
'mean': lambda v: sum(v) / len(v),
}
then look up functions from that dictionary instead:
functions['sum'](range(1, 11))
This lets you strictly control what names are available to dynamic code, preventing a user from making a nuisance of themselves by calling built-ins for their destructive or disruptive effects.
do.call is pretty much the equivalent of the splat operator in Python:
def mysum(a, b, c):
return sum([a, b, c])
# normal call:
mysum(1, 2, 3)
# with a list of arguments:
mysum(*[1, 2, 3])
Note that I’ve had to define my own sum function since Python’s sum already expects a list as an argument, so your original code would just be
sum(range(1, 11))
R has another peculiarity: do.call internally performs a function lookup of its first argument. This means that it finds the function even if it’s a character string rather than an actual function. The Python equivalent above doesn’t do this — see Martijn’s answer for a solution to this.
Goes similar to previous answer, but why so complicated?
def do_call(what, args=[], kwargs = {}):
return what(*args, **kwargs)
(Which is more elegant than my previously posted definition:)
def do_call(which, args=None, kwargs = None):
if args is None and kwargs is not None:
return which(**kwargs)
elif args is not None and kwargs is None:
return which(*args)
else:
return which(*args, **kwargs)
Python's sum is different than R's sum (1 argument a list expected vs.
arbitraily many arguments expected in R). So we define our own sum (mysum)
which behaves similarly to R's sum. In a similar way we define mymean.
def mysum(*args):
return sum(args)
def mymean(*args):
return sum(args)/len(args)
Now we can recreate your example in Python - as a reasonable 1:1 translation of the R function call.
do_call(what = mymean, args=[1, 2, 3])
## 2.0
do_call(what = mysum, args=[1, 2, 3])
## 6
For functions with argument names, we use a dict for kwargs, where the parameter
names are keys of the dictionary (as strings) and their values the values.
def myfunc(a, b, c):
return a + b + c
do_call(what = myfunc, kwargs={"a": 1, "b": 2, "c": 3})
## 6
# we can even mix named and unnamed parts
do_call(what = myfunc, args = [1, 2], kwargs={"c": 3})
## 6

Python optional function argument to default to another argument's value

I want to define a function with some optional arguments, let's say A (mandatory) and B (optional). When B is not given, I want it to take the same value as A. How could I do that?
I have tried this, but it doesn't work (name 'B' is not defined):
def foo(A, B=A):
do_something()
I understand that the values of the arguments are not assigned before the body of the function.
You shall do this inside of your function.
Taking your original function:
def foo(A, B=A):
do_something()
try something like:
def foo(A, B=None):
if B is None:
B = A
do_something()
Important thing is, that function default values for function arguments are given at the time, the function is defined.
When you call the function with some value for A, it is too late as the B default value was already assigned and lives in function definition.
You could do it like this. If B has a value of None then assign it from A
def foo(A, B=None):
if B is None:
B = A
print 'A = %r' % A
print 'B = %r' % B

Optional Parameters, certain combination of them required

I have a general question as well as a specific use case.
Optional parameters are easy enough: def func(a, b, c=None): ... and then anywhere c might be used in the body just write if c: first, or something along those lines. But what about when a certain combination of parameters is required? The general case is to consider any arbitrary situation of which exact parameters exist or not. For a function def func(a, b, c=None, d=None, e=None, f=None): ... this would include silly things like: provide c and d but not e and f, or provide e only, or provide at least 3 of c, d, e, and f. But my use case doesn't require such generality.
For def func(a, b, c=None, d=None): ..., I want EXACTLY ONE OF c and d to be provided.
Solutions I've thought of include:
- in the body, manually check how many of c and d are not None, and if it's not exactly 1, return an error saying exactly 1 needs to be specified
ex.
def func(a, b, c=None, d=None):
how_many_provided = len([arg for arg in [c, d] if arg]) # count the non-None optional args
if not how_many_provided == 1:
return "Hey, provide exactly 1 of 'c' and 'd'"
if c:
# stuff to do if c is provided
elif d:
# stuff to do if d is provided
- change the function to be def func(a, b, e, f): ... where e represents either c or d and f indicates which one of those e represents.
ex.
def func(a, b, e, f):
if f == 'c':
# stuff to do if c is provided, with e as c
if f == 'd':
# stuff to do if d is provided, with e as d
These would work, but what is the standard/accepted/pythonic way of doing this?
I would say the easiest way for your user in your simple case is to refactor to separate functions. Each function does the different work as described and then a common one e.g. for your last case
def funcC(a, b, c):
# stuff to do if c is provided, with e as c
common_func(a,b,c, None)
def funcD(a, b, d):
# stuff to do if d is provided, with e as d
common_func(a,b,None, d)
The user then knows what parameters matter and only the valid possible combinations can be used, the user does not have to guess or have a chance to call them incorrectly. You as providing the function can provide whatever is needed for the parameter the caller does not supply.
There are longer explanations of these found by googling for "flag parameters" e.g. Martin Fowler Stack Overflow these tend to mention Boolean arguments but this in effect the same thing a different code path depending on a parameter which has no other effect.
Another phrase to look for is "control coupling"
You could just use the keyword args dict:
def func(a, b, **kwargs):
valid_args = len(kwargs) == 1 and ('c' in kwargs or 'd' in kwargs)
if not valid_args:
return "Hey, provide exactly 1 of 'c' and 'd'"
if 'c' in kwargs:
# stuff to do if c is provided
elif 'd' in kwargs:
# stuff to do if d is provided
Here is another one, which will allow the arguments be specified, and differentiates between c=None and c not given, while still providing the argument names explicitly:
undefined = object()
def func(a, b, c=undefined, d=undefined):
if (c is undefined) ^ (d is undefined):
raise TypeError("Hey, provide exactly 1 of 'c' and 'd'")
...
On Python 3, keyword only arguments make it even nicer, making sure that the caller explicitly specifies c or d:
def func(a, b, *, c=undefined, d=undefined):
if (c is undefined) ^ (d is undefined):
raise TypeError("Hey, provide exactly 1 of 'c' and 'd'")

function default value not defined

I'm trying to use
def my_function(a,b)
If I try to print function like this
print(my_function()), when values start from none, I get
"missing 2 required positional arguments"
I want to use default value so when I use, print(my_function()),
a=10 and b=a.
So I tried
def my_function(a=10,b=a)
and I got a not defined.
I don't want to define a before with global.
Is it possible? or something like this
def my_function(a,b)
if a == None:
a = 10
if b == None:
b = a
This didn't work either when I used print(my_function()).
You can set the default to None:
def my_function(a=10, b=None):
if b is None:
b = a
Here the default for a is 10, and b is set to a if left to the default value.
If you need to accept None as well, pick a different, unique default to act as a sentinel. An instance of object() is an oft-used convention:
_sentinel = object()
def my_function(a=10, b=_sentinel):
if b is _sentinel:
b = a
Now you can call my_function(11, None) and b will be set to None, call it without specifying b (e.g. my_function() or my_function(42), and b will be set to whatever a was set to.
Unless a parameter has a default (e.g. is a keyword parameter), they are required.
This function my_function(a,b) expected two positional arguments without default value so It can't be called without them passed
So the main question how can we pass two argument so that second is set to first if not passed
There are two way for this:
kwargs unpacking
def my_function(a=10, **kwargs):
b = kwargs.get('b', a)
sentinel as default Value
_sentinel = object()
def my_function(a=10, b=_sentinel):
if b is _sentinel:
b = a
Hum! I thing you can't define my_function like this. But you can use a decorator to hide the default values computation.
For example:
import functools
def my_decorator(f):
#functools.wraps(f)
def wrapper(a=10, b=None):
if b is None:
b = a
return f(a, b)
return wrapper
You can then define your function like this:
#my_decorator
def my_function(a, b):
return (a, b)
You can use your function with zero, one or two parameters:
>>> print(my_function())
(10, 10)
>>> print(my_function(5))
(5, 5)
>>> print(my_function(5, 12))
(5, 12)

Conditional statements based on function parameter name [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to get the original variable name of variable passed to a function
Is there something like below in python where based on the name of the parameter passed to a function we can execute conditional statements like below.
def func(a,b,c,d)
if fourth paramter = d:
#code
elif fourth parameter = e:
#code
else:
#code
def main():
func(a,b,c,d)
func(a,b,c,e)
You should use keyword arguments:
def func(a, b, c, **kwargs):
if 'd' in kwargs:
....
elif 'c' in kwargs:
....
This is not exactly equivalent to what you want, though. If you want to ensure that d or e is the fourth parameter, that would be more troublesome.
Then you could try something like:
def func(a, b, c, d=None, e=None):
if d is not None and e is None:
....
elif e is not None and d is None:
....
Note that this is not exactly what you're asking. It will not access the "original" name of the variable passed to the function as a positional argument, as in the question linked by Stuart. You shouldn't even try to do that, it's pointless.
kwargs will not give you exactly what you want, but it can be close:
def func(a, b, c, **kwargs):
if 'd' in kwargs:
print "do something"
elif 'e' in kwargs:
print "do something else"
def main():
func(a = 1 ,b = 2 ,c = 3, d = 'some_value')
func(a = 1 ,b = 2 ,c = 3, e = 'some_value')

Categories

Resources