2013-06-19 2 views
0

В частности, когда у вас есть одна функция, которая вызывает другую функцию, но подпись второй функции неизвестна.Как вы прозрачно передаете аргументы через функцию в python?

Я знаю, что вы можете использовать * args и ** kwargs для чего-то похожего на это, но я не могу понять, что это совершенно правильно, что всегда работает.

Так, например, что-то вроде этого почти работает:

class Invoker(): 
    def __init__(self, call): 
    self._call = call 
    def call(self, *args, **kwargs): 
    self._call(*args, **kwargs) 

.. если я пишу некоторые тесты для него я получаю этот выход:

1 -> 2 ('Hello') 
1 -> 2 ((3, 'Hello')) 
1 -> 2 ('Hello'), ({'left': 'right'}) 
1 -> 2 ((3,)), ({'value': 'Hello'}) 
call5() got multiple values for keyword argument 'value' 
call5() got multiple values for keyword argument 'value' 

из этого кода:

# Known argument list: works fine 
def call(x, y, value=None): 
    print("%d -> %d (%r)" % (x, y, value)) 

invoker = Invoker(call) 
invoker.call(1, 2, value="Hello") 

# Unknown argument list with no kwargs: works fine 
def call2(x, y, *args): 
    print("%d -> %d (%r)" % (x, y, args)) 

invoker = Invoker(call2) 
invoker.call(1, 2, 3, "Hello") 

# Unknown kwargs with default value: works fine 
def call3(x, y, value=None, **kwargs): 
    print("%d -> %d (%r), (%r)" % (x, y, value, kwargs)) 

invoker = Invoker(call3) 
invoker.call(1, 2, value="Hello", left="right") 

# Unknown args and kwargs: works fine 
def call4(x, y, *args, **kwargs): 
    print("%d -> %d (%r), (%r)" % (x, y, args, kwargs)) 

invoker = Invoker(call4) 
invoker.call(1, 2, 3, value="Hello") 

# Default value with unknown args: fails 
try: 
    def call5(x, y, value=None, *args): 
    print("%d -> %d (%r), (%r)" % (x, y, value, args)) 

    invoker = Invoker(call5) 
    invoker.call(1, 2, 3, value="Hello") 
except Exception, e: 
    print(e) 

# Default value with unknown args and kwargs: fails 
try: 
    def call6(x, y, value=None, *args, **kwargs): 
    print("%d -> %d (%r), (%r), (%r)" % (x, y, value, args, kwargs)) 

    invoker = Invoker(call5) 
    invoker.call(1, 2, 3, value="Hello", left="right") 
except Exception, e: 
    print(e) 

Как написать метод, который вызывает другой метод, который всегда работает, когда подпись метода, который будет вызываться, является un известно только во время выполнения?

ответ

5

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

Например, посмотрите на call5, который вы определяете с помощью def call5(x, y, value=None, *args). Функция имеет три аргумента, один из которых имеет значение по умолчанию, а затем имеет значение *args.

Затем вы пытаетесь вызвать эту функцию с тремя позиционными аргументами (1, 2, 3) и аргументом ключевого слова value="Hello". Это не является допустимым аргументом для этой функции. Тот факт, что вы пытаетесь сделать это динамически, - это просто красная селедка; он все равно не сработает, даже если вы попытаетесь буквально сделать call5(1, 2, 3 value="Hello"), а call5(1, 2, value="Hello", 3) - синтаксическая ошибка. Эта функция просто не может принять три позиционных аргумента плюс аргумент «значение», заданный ключевыми словами.

Вы можете определить функцию, такую ​​как ваш «invoker», который может вызывать любую функцию, но это не меняет того факта, что некоторые комбинации аргументов positional и keyword могут быть неверными для этой функции. Это не артефакт вашей динамической структуры вызовов, а именно то, как работают функции: их нужно вызывать с аргументами, которые соответствуют их сигнатуре. Если вы передадите слишком много аргументов, функция завершится с ошибкой. Если подпись включает в себя переменные аргументы, это дает вам некоторую свободу действий, но это не меняет того факта, что функции все еще может потребоваться определенное количество аргументов, и если вы пройдете меньше этого, это не сработает. Вы не можете ожидать каких-либо произвольных кортежей и аргументов аргументов и работать для любой функции.

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

+0

Ого, вы сделали совершенно ужасную работу, объясняя поэтому я добавил еще одну ссылку в качестве альтернативного ответа, но спасибо, это действительно то, что было неправильно. – Doug

0

Причины, почему это не работает в python2 (и как это правильно сделать в Python3) также лучше объяснить в этом вопросе:

http://www.stackoverflow.com/questions/4372346/uses-of-combining-kwargs-and-key-word-arguments-in-a-method-signature

+0

Этот ответ показывает, как использовать аргументы только для ключевого слова в Python 3, но обратите внимание, что это не меняет того факта, что некоторые наборы аргументов недействительны.Создание 'value' аргумента только для ключевого слова только изменит, какие массивы аргументов действительны; это не сделает их действительными. Например, если 'value' является аргументом только для ключевого слова, тогда вы можете сделать' call5 (1, 2, 3, value = "Hello") ', но вы больше не можете делать' call5 (1, 2, "Hello «, 3)', как вы можете, с вашим текущим определением. – BrenBarn

Смежные вопросы