Зачем использовать упакованные * args/** kwargs вместо передачи списка/dict?
Если я не знаю, сколько аргументов передаст функция, я мог бы написать функцию, используя упаковку аргументов:
def add(factor, *nums):
"""Add numbers and multiply by factor."""
return sum(nums) * factor
В качестве альтернативы я мог избежать упаковки аргументов, передав в качестве аргумента список чисел:
def add(factor, nums):
"""Add numbers and multiply by factor.
:type factor: int
:type nums: list of int
"""
return sum(nums) * factor
Есть ли преимущество использования упаковки аргументов *args
для передачи списка чисел? Или есть ситуации, когда один из них более уместен?
Ответы
Ответ 1
*args
/**kwargs
имеет свои преимущества, как правило, в тех случаях, когда вы хотите иметь возможность проходить в распакованной структуре данных, сохраняя при этом способность работать с упакованными. Python 3 print()
- хороший пример.
print('hi')
print('you have', num, 'potatoes')
print(*mylist)
Сравните это с тем, как это было бы, если бы print()
занял только упакованную структуру, а затем расширил ее внутри функции:
print(('hi',))
print(('you have', num, 'potatoes'))
print(mylist)
В этом случае *args
/**kwargs
действительно очень удобно.
Конечно, если вы ожидаете, что функция всегда будет передана несколько аргументов, содержащихся в структуре данных, как это делают sum()
и str.join()
, может возникнуть больше смысла оставить синтаксис *
.
Ответ 2
Это о API: * args обеспечивает лучший интерфейс, поскольку он утверждает, что метод принимает произвольное количество аргументов и что он - никаких дополнительных предположений. Вы точно знаете, что сам метод не будет делать ничего с структурой данных, содержащей различные аргументы, и что никакой специальной структуры данных не требуется.
В теории вы также можете принять словарь со значениями, установленными в None. Почему нет? Он накладные и ненужные. Для меня, принимая список, когда вы можете принимать varargs, добавляется накладные расходы. (как отметил один из комментариев)
Кроме того, varargs - хороший способ гарантировать согласованность и хороший контракт между вызывающим и вызываемой функцией. Никакие предположения не могут быть сделаны.
Когда и если вам нужен список, то вы знаете, что вам нужен список!
Ah, обратите внимание, что f (* args) не совпадает с f (список): второй хочет список, первый принимает произвольное количество параметров (включая 0). Итак, давайте определим второй как необязательный аргумент:
def f(l = []): pass
Прохладный, теперь у вас есть две проблемы, потому что вы должны убедиться, что вы не изменяете аргумент l: значения по умолчанию. По какой причине? Потому что вам не нравятся * args.:)
PS: Я думаю, что это один из самых больших недостатков динамических языков: вы больше не видите интерфейс, но да! есть интерфейс!
Ответ 3
Это своего рода старый вариант, но для ответа на запросы @DBrenko о том, когда потребуются * args и ** kwargs, самый ясный пример, который я нашел, - это когда вам нужна функция, которая выполняет другую функцию как часть своего выполнения. В качестве простого примера:
import datetime
def timeFunction(function, *args, **kwargs):
start = datetime.datetime.now()
output = function(*args, **kwargs)
executionTime = datetime.datetime.now() - start
return executionTime, output
timeFunction принимает функцию и аргументы для этой функции в качестве аргументов. Используя синтаксис * args, ** kwargs, он не ограничивается конкретными функциями.