Is there a "missing" function in Python? - python

In R, there is a missing() function to test, quote : "whether a value was specified as an argument to a function" :
my_function <- function(argn1){
if(missing(argn1)){
print("argn1 has not been supplied")
} else {
print("argn1 has been supplied")
}
}
Then when calling :
my_function("hello")
[1] "argn1 has been supplied"
my_function()
[1] "argn1 has not been supplied"
Is there such a thing in Python ?

Well usually arguments without a default value are mandatory. So you can provide a default object missing for instance to check whether the attribute was given explicitly. Like:
missing = object()
def foo(arg1 = missing):
if arg1 is missing:
print('Arg1 is missing')
else:
print('Arg1 is not missing')
Using the is over == can be of vital importance, since is checks reference equality.
Sometimes one uses None, like:
def foo(arg1 = None):
if arg1 is None:
# ...
But note that here Python cannot make a difference between an implicit argument, like foo() or an explicit call with None, like foo(None).
Furthermore there is also the option to use *args:
def foo(*args):
# ...
If you call foo(None,1) then all the arguments will be put into a tuple an that tuple is named args (here args will be args = (None,1)). So then we can check if the tuple contains at least one element:
def foo(*args):
if args:
print('At least one element provided')
else:
print('No element provided')

No, because Python does not support calling a function with the wrong number of arguments. If you define a function to take one argument (without a default):
def my_func(arg1):
pass
and then call it just via my_func(), Python will raise a TypeError.

No, there is no missing function.
However, you can do the same thing by using function arguments with default values:
def foo(arg=None):
if arg is None:
print('arg is missing.')
else:
print('arg is present.')

In python, you will often find this kind of function declaration:
def my_function(arg1=None):
if arg1:
# Do something
else:
# Do something else
to achieve what you are looking for

There is no obvious equivalent. But you can use default parameters.
def my_function(str=None):
if str is None:
print("argn1 has not been supplied")
else:
print("argn1 has been supplied")

Related

How to handle the *args in Python when no arguments are passed over to the Function parameter

Let's say I have a function given below:
def splitter(*params):
rep, msg = params
if rep:
for i in range(rep):
print(i)
else:
print('-----------------------------------')
splitter(2,'Let the Game Begin!! šŸ')
Now, in the above case it will pass since I'm giving the arguments, but what I want is, that suppose I don't want to give the arguments when calling the function, then how can I handle it? Since *args cannot have a default value.
Define the function with named arguments having default values:
def splitter(rep=None, msg=None):
if rep is not None:
...

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

Is it possible to use an argument's value in a default argument value assignment in Python?

I am trying to use the left-side argument's value in the below function inside of the default argument value to the right.
def function(var, vartwo = var*2):
return vartwo
print(function(7)) #should print 14
print(function(7,6)) #should print 6
However I am getting NameError: name 'var' is not defined. Is it not possible use an argument's value in a default argument value in Python? How would you suggest I tackle this problem?
It's not possible; how about this?
def function(var, vartwo = None):
if vartwo is None:
vartwo = var*2
return vartwo
print(function(7))
print(function(7,6))
The hack is to set the default value to None, then set it internally. If not none, then return whatever was entered by the user.
Pass the buck:
def function(var, *args):
return (lambda var, vartwo=var * 2: vartwo)(var, *args)
print(function(7)) # should print 14
print(function(7, 6)) # should print 6
I know about lambda functions but mind adding explanation?
Since we can't use var in vartwo's definition, we delay by setting up a new argument list, via a lambda, where var is now defined and the default for vartwo is legal.
I've made this somewhat confusing by reusing the var variable name to represent two different variables (a formal parameter to function and a formal parameter to lambda) and also act as a positional place holder.
I could, and probably should, have written this more simply as:
def function(var, *args):
return (lambda vartwo=var * 2: vartwo)(*args)
But I wanted to retain the flavor of the original problem.
What your doing won't work because var hasn't been declared. Here's a different approach:
def function(var, multiply=False):
if multiply:
return var * 2
else:
return var
print(function(7, True)) # 14
print(function(6)) # 6
No, it is not possible to use a value that is not defined outside of the function as a argument value for that function.
For an alternative, I'm late to the party, but how about something short and simple that will work if None is the second parameter?
def function(var, *args):
return args[0] if args else var * 2
print(function(7)) # prints 14
print(function(7, 6)) # prints 6
print(function(7, None)) # prints 'None'
Here, args is the list of arguments provided after var, and could be an empty list. We then return the first element of this list if the list is not empty (which is equivalent to the second argument). If the list is empty, we return var * 2.
The caveat here is that this will not throw the appropriate error if more than two arguments are provided. You can choose to manually raise a TypeError if len(args) > 1 (remembering that args are parameters after the first one) if this is important to enforce.
It is not possible to use the value of another argument in default value. The reason is very simple: the default values are evaluated exactly once: at the time when the function definition was executed.
One common pattern to use is to have the second argument be set to a special sentinel value by default, then test if the argument was set again within the function:
SENTINEL = object()
def function(var, vartwo=SENTINEL):
# was vartwo set?
if vartwo is not SENTINEL:
return vartwo
return var * 2
This way the function would return vartwo correctly even when you'd pass in None or any other value for it, unless you specifically passed the SENTINEL constant.

How do I define a function with optional arguments?

I have a Python function which takes several arguments. Some of these arguments could be omitted in some scenarios.
def some_function (self, a, b, c, d = None, e = None, f = None, g = None, h = None):
#code
The arguments d through h are strings which each have different meanings. It is important that I can choose which optional parameters to pass in any combination. For example, (a, b, C, d, e), or (a, b, C, g, h), or (a, b, C, d, e, f, or all of them (these are my choices).
It would be great if I could overload the function - but I read that Python does not support overloading. I tried to insert some of the required int arguments in the list - and got an argument mismatch error.
Right now I am sending empty strings in place of the first few missing arguments as placeholders. I would like to be able to call a function just using actual values.
Is there any way to do this? Could I pass a list instead of the argument list?
Right now the prototype using ctypes looks something like:
_fdll.some_function.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_char_p, c_char_p, c_char_p, c_char_p]
Just use the *args parameter, which allows you to pass as many arguments as you want after your a,b,c. You would have to add some logic to map args->c,d,e,f but its a "way" of overloading.
def myfunc(a,b, *args, **kwargs):
for ar in args:
print ar
myfunc(a,b,c,d,e,f)
And it will print values of c,d,e,f
Similarly you could use the kwargs argument and then you could name your parameters.
def myfunc(a,b, *args, **kwargs):
c = kwargs.get('c', None)
d = kwargs.get('d', None)
#etc
myfunc(a,b, c='nick', d='dog', ...)
And then kwargs would have a dictionary of all the parameters that are key valued after a,b
Try calling it like: obj.some_function( '1', 2, '3', g="foo", h="bar" ). After the required positional arguments, you can specify specific optional arguments by name.
It is very easy just do this
def foo(a = None):
print(a)
Instead of None you can type anything that should be in place if there was no argument for example if you will not write the value of the parameter like this foo() then it will print None because no argument is given and if you will GIVE it an argument like foo("hello world") then it will print hello world... oh well I just forgot to tell y'all that these types of parameters i.e optional parameters, need to be behind all the other parameters. This means that, let's take the previous function and add another parameter b
def foo(a = None, b):
print(a)
Now if you'll execute your python file it is going to raise an exception saying that Non-default arguments follow default arguments,
SyntaxError: non-default argument follows default argument
so you gotta put the optional or non-default argument after the arguments which are required
which means
def foo (a, b=None): ... #This one is right
def foo(b=None, a): ... #and this isn't
Required parameters first, optional parameters after. Optional parameters always with a =None.
Easy and fast example:
def example_function(param1, param2, param3=None, param4=None):
pass
# Doesn't work, param2 missing
example_function("hello")
# Works
example_function("hello", "bye")
# Works. Both the same
example_function("hello", "bye", "hey")
example_function("hello", "bye", param3="hey")
# Works. Both the same
example_function("hello", "bye", "hey", "foo")
example_function("hello", "bye", param3="hey", param4="foo")
Check this:
from typing import Optional
def foo(a: str, b: Optional[str] = None) -> str or None:
pass
To get a better sense of what's possible when passing parameters it's really helpful to refer to the various options: positional-or-keyword (arg or arg="default_value"), positional-only (before /, in the parameter list), keyword-only (after *, in the parameter list), var-positional (typically *args) or var-keyword (typically **kwargs). See the Python documentation for an excellent summary; the various other answers to the question make use of most of these variations.
Since you always have parameters a, b, c in your example and you appear to call them in a positional manner, you could make this more explicit by adding /,,
def some_function (self, a, b, c, /, d = None, e = None, f = None, g = None, h = None):
#code
To make AviĆ³n's answer work for vector argument inputs;
def test(M,v=None):
try:
if (v==None).all() == False:
print('argument passed')
return M + v
except:
print('no argument passed')
return M
Where M is some matrix and v some vector. Both test(M) and test(M,v) produce errors when I attempted to use if statements without using 'try/ except' statements.
As mentioned by cem, upgrading to python 3.10 would allow the union (x|y) (or the Optional[...])functionality which might open some doors for alternative methods, but I'm using Anaconda spyder so I think I have to wait for a new release to use python 3.10.

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.

Categories