2

I'm not sure if this is possible in Python and I was wondering, is there any method for checking at runtime whether a parameter is passed to a Python function without doing some sort of check on the parameter value?

def my_func(req, opt=0):
    return was_opt_passed() # Returns bool 

print(my_func(0)) # Prints False
print(my_func(0, 0)) # Prints True

This would be superior in some circumstances, if possible, because it relieves the need to remember and check for sentinel values. Is it possible?

martineau
  • 119,623
  • 25
  • 170
  • 301
ICW
  • 4,875
  • 5
  • 27
  • 33
  • 7
    The common convention is to use `opt=None`. The convention would not have become common if there was a way to do what you want. – Mark Ransom Sep 09 '19 at 17:57

5 Answers5

1

the standard method of detecting if an argument has been passed is sentinel values; however, if you're willing to lose your function signature, you can use **kwargs:

def my_func(**kwargs):
  return 'opt' in kwargs
print(my_func()) #=> False
print(my_func(opt=0)) $=> True
Zae Zoxol
  • 109
  • 6
1

As Mark has already stated in his comment the typical convention is to use the default value None. Then you can check if it’s still None upon calling.

def my_func(req, opt=None):
    if opt is None:
        #opt wasn’t passed.
        return False
    #opt was passed
    return True

Although if you’d like to do more research on other options (most unconventional for most cases) feel free to check out these answers

Jab
  • 26,853
  • 21
  • 75
  • 114
  • He still can pass None as argument this means that the parameter is passed. – Charif DZ Sep 09 '19 at 18:14
  • 1
    That’s why I linked to more sophisticated answers as they can provide more depth into the passed arguments. Although it’s rarely the case that it’s needed. My thought is this question is already answered there as is. – Jab Sep 09 '19 at 18:16
0

A great way would be to use *args or **kwargs.

def was_opt_passed(*args, **kwargs):
  return len(args) > 0 or 'opt' in kwargs

def my_func(req, *args, **kwargs):
    return was_opt_passed(*args, **kwargs) # Returns bool 

print(my_func(0)) # Prints False
print(my_func(0, 0)) # Prints True
print(my_func(0, opt=0)) # Prints True
print(my_func(0, not_opt=0)) # Prints False

*args collects any positional arguments passed to your function that are not already enumerated, while **kwargs collects any keyword arguments passed to your function that are not already enumerated. If args contains a positional argument, we assume it was opt, and it must have been passed. Otherwise, if it is in kwargs, it was passed, and then if we didn't find it in either place, it must not have been passed.

See also https://docs.python.org/3/tutorial/controlflow.html#keyword-arguments

mattbornski
  • 11,895
  • 4
  • 31
  • 25
  • While, `kwargs` is an option, doesn't this *require* OP to not specify keyword arguments? For instance, OP can't have the named keyword `opt` and have this code work. – Brian Sep 09 '19 at 18:09
  • I have revised to also handle positional arguments as well You can actually specify keyword arguments prior to the **kwargs catchall which will be plucked separately (eg, fn(req, opt=0, **kwargs)), but if you want to find it in kwargs you cannot specify it separately. – mattbornski Sep 09 '19 at 18:09
0

One solution is to use decorators/wrappers. They allow you to interface with what's being passed to your function at runtime and then handle said things as you see fit Consider this code:

def check_keywords(func):
    def wrapper(*args, **kwargs):
        if kwargs:
            print('Keyword was passed!')
        return func(*args, **kwargs)
    return wrapper

@check_keywords
def my_func(req, opt=0):
    print(req)

check_keywords captures the function and if it detects keywords being passed into my_func, it then prints something. This print statement can be converted to any arbitrary code you want.

e.g.:

my_func(1)
>>> 1

my_func(1, opt = 1)
>>> Keyword was passed!
>>> 1
Brian
  • 1,572
  • 9
  • 18
0

Using decorator

    def check_opt_passed(methd):
        def m(req, *args, **kwarg):
            # this will check even if opt is passed as positional argument
            # and check if opt is passed not any other keyword
            if args or (kwarg and 'opt' in kwarg):
                print('opt is passed')
            else:
                print('opt is not passed')
            return methd(req, *args, **kwarg)

        return m

    @check_opt_passed
    def my_func(req, opt=0):
        # dummy expression for testing
        return req * opt


    print(my_func(1))           # opt is not passed
    print(my_func(1, 0))        # opt is passed
    print(my_func(1, opt=0))    # opt is passed
Charif DZ
  • 14,415
  • 3
  • 21
  • 40