2

Let's say I have a function (called function) that takes a boolean argument with default value of True like def function(argument1 = True).

Now let's say I want to use this function, specifying its argument using the output of another function (resolveArg) that checks for arguments in an existing infrastructure. This resolveArg method is used generally and if it can find the argument value destined for a given function (e.g. function), then it sets the function argument to it, otherwise it sets the argument to None.

How can I get function to use the default value of its argument if its argument is set to None by this other function (resolveArg)?

def resolveArg(argName = None):
    if argName in self.conf._argdict:
        return(self.conf._argdict[argName].value)
    else:
        return(None)

def function(argument1 = True)
    if argument1 is True:
        return(True)
    elif argument1 is False:
        return(False)
    else:
        raise(Exception)

function(argument1 = resolveArg("red"))
Jadav Bheda
  • 5,031
  • 1
  • 30
  • 28
d3pd
  • 7,935
  • 24
  • 76
  • 127
  • I don't think there is a built-in feature that allow that, in fact if you pass None you have already passed an object of 'NoneType' so the default value wouldn't be used – Jaay Feb 26 '15 at 09:02
  • 1
    Umm... seems a bit over-designed... I take it that `function` is actually doing something more complicated than that? – Jon Clements Feb 26 '15 at 09:47

4 Answers4

1

I have not seen this kind of problem before, so may be there is a way to improve your logic and omit this functionality.

However there is a way to do introspection of code in Python by using the module "inspect".

An example usage is:

>>> def a(q=5):
...  if q is None:
...   return dict(inspect.getmembers(a))['func_defaults'][0]
...  return q
...
>>> a()
5
>>> a(None)
5

The problem with this is that "func_defaults" returns a list which is inconvenient in case you have multiple argument function and as a whole makes the procedure error prone.

Lachezar
  • 6,523
  • 3
  • 33
  • 34
  • Thank you very much for your suggestion of taking an introspective approach to this. Conceptually, I think you're right. You can see that [J Richard Snape](http://stackoverflow.com/users/838992/j-richard-snape) has given [a detailed consideration](http://stackoverflow.com/a/28742862/1556092) of this approach. It may be that improving the code logic is the most rational approach, but in working with a large infrastructure this is often not an easy task. – d3pd Feb 27 '15 at 20:54
1

Caveat

You asked for a "good way to get a function to use its default value for an argument when the argument is specified as None in Python?"

I would argue that there isn't a good way, because it's not a good thing to do. This is a risky thing to do - it can mask nasty error cases where you might reasonably want to detect the fact that None was passed in where you expected a value. Debugging where this technique is used extensively could be horrible. Use with extreme caution.

Possible method

I'm guessing you want a generic solution that will work for more functions than the one you offer as an example. In the example case - you would be easiest to simply test for None in the argument value and replace it if necessary.

You could write a decorator to do the general case, that will use the inspect toolbox to work out the argument values across the list of arguments with default values and replace them if they are None.

The code below demonstrates the idea with some simple test cases - this will work, but only for functions with simple arguments (not *args, **kwargs) as currently constituted. It could be enhanced to cover those cases. It safely treats arguments without defaults as usual.

Python Fiddle

import inspect

def none2defaultdecorator(thisfunc):
    (specargs, _, _, defaults) = inspect.getargspec(thisfunc)

    def wrappedfunc(*instargs):        
        callargs = inspect.getcallargs(thisfunc, *instargs)

        if len(specargs) != len(callargs):
             print "Strange - different argument count in this call to those in spec"
             print specargs
             print callargs

        for i,argname in enumerate(specargs[-len(defaults):]):
            try:
                if callargs[argname] == None:
                    callargs[argname] = defaults[i]
            except KeyError:
                # no local value for this argument - it will get the default anyway
                pass

        return thisfunc(**callargs)

    return wrappedfunc

#Decorate the funtion with the "None replacer".  Comment this out to see difference
@none2defaultdecorator            
def test(binarg = True, myint = 5):
    if binarg == True:
        print "Got binarg == true"
    elif binarg == False:
        print "Got binarg == false"
    else:
        print "Didn't understand this argument - binarg == ", binarg

    print "Argument values - binarg:",binarg,"; myint:",myint

test()
test(False)
test(True)
test(None)
test(None, None)
J Richard Snape
  • 20,116
  • 5
  • 51
  • 79
  • Thank you very much for your detailed answer on this. Conceptually, it looks like your proposed introspective approach is exactly what I want, but I'm glad you've made clear the caveats and the possible obscurification of errors that could occur in this approach. I think I'll use your approach -- with as much logging output as possible -- while continuing to look for ways to make the logic of the infrastructure in which I'm working better. – d3pd Feb 27 '15 at 20:51
0

One way would be to give the same behavior to yourfunction if the argument is True or None.

def function(arg=True):
    if arg in [True, None]:
        do_stuff()
    else:
        do_something_else()
El Bert
  • 2,958
  • 1
  • 28
  • 36
  • Thank you very much for your suggestion. While your approach could work, I'm looking for something a bit more general -- preferably something that doesn't involve rewriting a library of existing functions to interpret arguments in this way. – d3pd Feb 27 '15 at 20:46
0

Probably you want to modify your implementation of function little bit as follow:

def function(argument1 = None)
    if argument1 is None:
       argument1 = True        

    return argument1

If you want to understand more about default function argument as None, you may want to refer to my other SO answer

Jadav Bheda
  • 5,031
  • 1
  • 30
  • 28