2009-06-01 6 views
1

Когда вы вызываете функцию с неправильным числом аргументов или с аргументом ключевого слова, который не находится в его определении, вы получаете TypeError. Я хотел бы, чтобы часть кода выполняла обратный вызов и вызывала его с переменными аргументами, основанными на том, что поддерживает обратный вызов. Один из способов сделать это - обратиться за обратным вызовом cb, использовать cb.__code__.cb_argcount и cb.__code__.co_varnames, но я бы скорее отвлекся на что-то вроде apply, но это применимо только к аргументам, которые «подходят».Есть ли способ вызвать функцию Python с неправильным количеством аргументов без вызова TypeError?

Например:

def foo(x,y,z): 
    pass 

cleanvoke(foo, 1)   # should call foo(1, None, None) 
cleanvoke(foo, y=2)  # should call foo(None, 2, None) 
cleanvoke(foo, 1,2,3,4,5) # should call foo(1, 2, 3) 
          # etc. 

Есть ли что-нибудь, как это уже в Python, или это то, что я должен написать с нуля?

ответ

7

Вместо того чтобы копаться в деталях самостоятельно, вы можете inspect подпись функции - вы, вероятно, хотите inspect.getargspec(cb).

Точно, как вы хотите использовать эту информацию и ваши аргументы, чтобы вызвать функцию «правильно», мне не совсем понятно. Предполагая для простоты, что вы заботитесь только о простых именованных аргументов, и значения, которые вы хотели бы передать находятся в Словаре d ...

args = inspect.getargspec(cb)[0] 
cb(**dict((a,d.get(a)) for a in args)) 

Может быть, вы хотите что-то любитель, и может уточнить точно что?

+0

getargspec, безусловно, сделает это намного более «чистые» в том, что беспокоило меня (не надо смотреть через __magic__ материал , например). Я намерен фильтровать названные args, как вы предложили (без «k» вместо «a» typo;)), и нормальные аргументы по длине - заполнить None, если в вызове недостаточно аргументов, и обрезать лишние аргументы. Возможно, я тоже сделаю это в декораторе :) –

+0

С другой стороны, вы абсолютно правы, что я не очень ясно (в своей голове тоже), как я хочу, чтобы это было, когда обратный вызов имеет разные комбинации значений по умолчанию, * args и ** kwargs ... –

+0

Хех, спасибо, исправил опечатку. Подумав об этом больше, я уверен, что сосредоточиться на названных args - хорошая идея - смешение позиционного и названного всегда суетливо. Одним из ключевых улучшений для моего кода было бы: если аргумент a имеет значение по умолчанию и не находится в d, пусть он один, а не принуждает его к None, как мой код (через .get). –

3

Возможно, это?

def fnVariableArgLength(*args, **kwargs): 
    """ 
    - args is a list of non keywords arguments 
    - kwargs is a dict of keywords arguments (keyword, arg) pairs 
    """ 
    print args, kwargs 


fnVariableArgLength() #() {} 
fnVariableArgLength(1, 2, 3) # (1, 2, 3) {} 
fnVariableArgLength(foo='bar') #() {'foo': 'bar'} 
fnVariableArgLength(1, 2, 3, foo='bar') # (1, 2, 3) {'foo': 'bar'} 

Редактировать Ваши Прецеденты

def foo(*args,*kw): 
    x= kw.get('x',None if len(args) < 1 else args[0]) 
    y= kw.get('y',None if len(args) < 2 else args[1]) 
    z= kw.get('z',None if len(args) < 3 else args[2]) 
    # the rest of foo 

foo(1)   # should call foo(1, None, None) 
foo(y=2)  # should call foo(None, 2, None) 
foo(1,2,3,4,5) # should call foo(1, 2, 3) 
+0

Нах, жаль, что я недостаточно ясен. Я добавил несколько примеров, чтобы объяснить, что я имел в виду. –