необязательные аргументы при вызове функции без изменения определения функции - PullRequest
0 голосов
/ 06 февраля 2010

Я хочу знать, как вызывать функцию, передавая параметр, который может не ожидать.

Я столкнулся с этой проблемой несколько дней назад и нашел способ ее обойти. Но сегодня я решил, что посмотрю, возможно ли то, что я хотел сделать. К сожалению, я не помню контекст, в котором я его использовал. Так что это глупый пример, в котором есть много лучших способов сделать это, но просто игнорируйте это:

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

Альтернатива, которую я придумал, выглядела так:

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

или try..except .. block

Но должен быть лучший способ сделать это без изменения func1 и func2, верно?

(Изменить): Используя решение, предоставленное Max S, я думаю, что это лучший подход:

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

Ответы [ 2 ]

1 голос
/ 06 февраля 2010

Проверка функции является единственным способом явного различия между функциями с различным количеством аргументов без изменения или декорирования оригиналов. Единственное изменение, которое я бы сделал в вашей оболочке, - это обобщить ее на любое количество аргументов:

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)

РЕДАКТИРОВАТЬ: Обратите внимание, что это не распространяется на функции с переменным числом аргументов или аргументов ключевых слов. Вам нужно будет выбрать политику, чтобы справиться с ними, прежде чем можно будет написать полное решение.

0 голосов
/ 06 февраля 2010

Если я вас понимаю, вы хотите иметь возможность вызывать func1 и func2 с помощью 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
...