Распаковка более одного списка в качестве аргумента для функции
Если у меня есть функция вроде:
def f(a,b,c,d):
print a,b,c,d
Тогда почему это работает:
f(1,2,3,4)
f(*[1,2,3,4])
Но не это:
f(*[1,2] , *[3,4])
f(*[1,2] , *[3,4])
^
SyntaxError: invalid syntax
?
EDIT:
Для информации исходная проблема заключалась в замене одного из аргументов в оболочке функции. Я хотел заменить данный член введенных * args и попробовал что-то вроде:
def vectorize_pos(f,n=0):
'''
Decorator, vectorize the processing of the nth argument
:param f: function that dont accept a list as nth argument
'''
def vectorizedFunction(*args,**kwargs):
if isinstance(args[n],list):
return map(lambda x : f( *(args[:n]) , x , *(args[n+1,:]), **kwargs),args[n])
else:
return f(*args,**kwargs)
return vectorizedFunction
То, откуда возник вопрос. И я знаю, что есть другой способ сделать то же самое, но только хотел понять, почему распаковка одной последовательности сработала, но не для большего.
Ответы
Ответ 1
Поскольку в соответствии с синтаксисом вызова функций, так определяется список аргументов
argument_list ::= positional_arguments ["," keyword_arguments]
["," "*" expression] ["," keyword_arguments]
["," "**" expression]
| keyword_arguments ["," "*" expression]
["," keyword_arguments] ["," "**" expression]
| "*" expression ["," keyword_arguments] ["," "**" expression]
| "**" expression
Таким образом, вы можете передать только один * expression
для вызова функции.
Ответ 2
Начиная с Python 3.5, этот работает .
PEP 448 был реализован в Python 3.5. Цитируя из PEP, он позволяет, среди прочего:
Произвольно упорядоченные операторы распаковки:
>>> print(*[1], *[2], 3)
1 2 3
>>> dict(**{'x': 1}, y=2, **{'z': 3})
{'x': 1, 'y': 2, 'z': 3}
Ответ 3
Вы можете объединить списки:
>>> f(*[1,2]+[3,4])
1 2 3 4
или используйте itertools.chain
:
>>> from itertools import chain
>>> f(*chain([1,2], [3,4]))
1 2 3 4
Ответ 4
Это будет работать.
>>>def f(a,b,c,d):
print('hello') #or whatever you wanna do.
>>>f(1,2,*[3,4])
hello
Причина, по которой это не получается, заключается в том, что Python реализует это с помощью this
Один список распаковывается и, следуя семантике, любой аргумент после этого должен быть именованным аргументом ключевого слова (или профайлом, передающим именованные аргументы ключевого слова через **)
По контрасту это сработает.
>>>def f(a,b,c,k):
pass
>>>f(1,*[2,3],k=4)
Ответ 5
Это не работает, потому что это недействительный синтаксис, то есть на самом деле это не Python, хотя он выглядит так.
В сигнатурах функций Python 2 разрешен только один отмеченный параметр, и он должен следовать любым позиционным параметрам и предшествовать любым параметрам ключевых слов. Аналогично допускается только один аргумент с двумя звездочками, и он должен следовать за всеми параметрами ключевого слова в сигнатуре. Если у вас есть несколько списков аргументов, которые вы хотели бы отправить, вам действительно придется сначала создать из них один список.
В Python 3 также можно использовать звезду самостоятельно, чтобы указать, что любые следующие параметры являются так называемыми параметрами только для ключевого слова, но я не думаю, что нам нужно только в это время.
Ответ 6
*
здесь не действует как оператор. Это больше похоже на часть синтаксиса вызовов функций, и это позволяет только ограниченные возможности. Было бы возможно определить язык, чтобы вы могли делать то, что хотите (я это сделал!), Но это был не тот выбор, который был сделан.
Ответ 7
Это может помочь. Обратите внимание, что аналогия - это переменное количество аргументов на других языках. Это означает, что, как только вы скажете, что собираетесь использовать переменное количество аргументов, все следующие аргументы являются частью этого списка (аналогия с использованием varargs на C или С++).
например f = [1,2,3,4,5]
def func(a, b, c, d)
print a, b, c, d
func(f) # Error 1 argument, 4 required
func(*f) # Error 5 arguments 4 required
http://www.python-course.eu/passing_arguments.php
Переменная длина параметров
Введем теперь функции, которые может принимать произвольное количество аргументов. Те, у кого есть программирование фона на C или С++ знает об этом из функции varargs этих языков. Звездочка "*" используется в Python для определения переменное количество аргументов. Символ звездочки должен предшествовать Идентификатор переменной в списке параметров.
>>> def varpafu(*x): print(x)
...
>>> varpafu()
()
>>> varpafu(34,"Do you like Python?", "Of course")
(34, 'Do you like Python?', 'Of course')
>>>
Из предыдущего примера мы узнаем, что аргументы, переданные вызов функции varpafu() собираются в кортеже, что может быть доступ как "нормальная" переменная x внутри тела функции. Если функция вызывается без каких-либо аргументов, значение x является пустой кортеж.
Иногда необходимо использовать позиционные параметры, за которыми следует произвольное количество параметров в определении функции. Это возможно, но позиционные параметры всегда должны предшествовать произвольные параметры. В следующем примере у нас есть позиционный параметр "город", - основное место, - которое всегда должно быть а затем произвольное количество других мест:
>>> def locations(city, *other_cities): print(city, other_cities)
...
>>> locations("Paris")
('Paris', ())
>>> locations("Paris", "Strasbourg", "Lyon", "Dijon", "Bordeaux", "Marseille")
('Paris', ('Strasbourg', 'Lyon', 'Dijon', 'Bordeaux', 'Marseille'))
>>>
http://docs.python.org/2.7/reference/expressions.html
Если выражение функции синтаксиса * появляется в вызове функции, выражение должен оцениваться итерабельным. Элементы из этого итерабельного обрабатываются как если бы они были дополнительными позиционными аргументами; если есть позиционные аргументы x1,..., xN и выражение оцениваются как последовательность y1,..., yM, это эквивалентно вызову с позицией M + N аргументы x1,..., xN, y1,..., yM.
Следствием этого является то, что хотя синтаксис выражения * появляются после некоторых аргументов ключевого слова, они обрабатываются до аргументы ключевого слова (и аргумент выражения **, если есть - см. ниже). Итак:
<Р →
>>> def f(a, b): ... print a, b ...
>>> f(b=1, *(2,)) 2 1
>>> f(a=1, *(2,)) Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: f() got multiple values for keyword argument
'a'
>>> f(1, *(2,)) 1 2
Это необычно для обоих аргументов ключевого слова и синтаксиса выражения * можно использовать в одном и том же вызове, поэтому на практике эта путаница не возникают.
Если выражение функции синтаксиса ** появляется в вызове функции, выражение должны оцениваться до отображения, содержание которого трактуется как дополнительные аргументы ключевых слов. В случае появления ключевого слова в оба выражения и как явный аргумент ключевого слова, TypeError исключение.