Question

I want to know how to call a function, passing a parameter that it might not be expecting.

I faced this problem a few days ago, and found a way around it. But today, I decided I'd see if what I wanted to do was possible. Unfortunately, I don't remember the context which I used it in. So here is a stupid example in which there are plenty of better ways to do this, but just ignore that:

def test(func, arg1, arg2):
    return func(arg1, arg2, flag1=True, flag2=False) #Only pass the flags if the function accepts them.

def func1(a, b, flag1, flag2):
    ret = a
    if flag1:
        ret *= b
    if flag2:
        ret += b
    return ret

def func2(a, b):
    return a*b

print test(func1, 5, 6) #prints 30

The alternative I came up with looked like this:

def test(func, arg1, arg2):
    numArgs = len(inspect.getargspec(func).args)
    if numArgs >= 4:
        return func(arg1, arg2, True, False)
    elif numArgs == 3:
        return func(arg1, arg2, True)
    else:
        return func(arg1, arg2)

print test(func2, 5, 6) #prints 30

or a try..except.. block

But there's got to be a better way of doing this without altering func1 and func2, right?

(Edit): Making use of the solution provided by Max S, I'm thinking this is the best approach:

def callWithOptionalArgs(func, *args):
    argSpec = inspect.getargspec(func)
    lenArgSpec = len(argSpec.args or ())
    argsToPass = args[:lenArgSpec] #too many args
    defaults = argSpec.defaults or ()
    lenDefaults = len(defaults)
    argsToPass += (None, )*(lenArgSpec-len(argsToPass)-lenDefaults) #too few args
    argsToPass += defaults[len(argsToPass)+len(defaults)-lenArgSpec:] #default args

    return func(*argsToPass)
print callWithOptionalArgs(func1, 5, 6, True) #prints 30
print callWithOptionalArgs(func2, 5, 6, True) #prints 30
Was it helpful?

Solution

Inspecting the function is the only way to explicitly differentiate between functions with different numbers of arguments without altering or decorating the originals. The only change I would do to your wrapper is to generalize it for any number of arguments:

def padArgsWithTrue(func, *args):
    passed_args = list(args)
    num_args = len(inspect.getargspec(func).args)
    passed_args += [True] * (num_args - len(args))
    return func(*passed_args)

print padArgsWithTrue(lambda x,y,z,w: (x*y, z, w), 5, 6)

EDIT: Note that this does not accommodate functions with variable number of args or keyword args. You'll have to decide on a policy to deal with those before a complete solution could be written.

OTHER TIPS

If I understand you, you want to be able to call both func1 and func2 using test.

def test(func, arg1, arg2):
    try:
        return func(arg1, arg2, flag1=True, flag2=False)
    except TypeError:
        return func(arg1, arg2)


def func1(a, b, flag1, flag2):
    ret = a
    if flag1:
        ret *= b
    if flag2:
        ret += b
    return ret

def func2(a, b):
    return a*b

print test(func1, 5, 6) #prints 30
print test(func2, 5, 6) #prints 30
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top