Зачем использовать упакованные * 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 перед передачей списка чисел? Или есть ситуации, когда один из них более уместен?

2 10

2 ответа:

*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(), было бы более разумно исключить синтаксис *.

Речь идет об API: * args предоставляет лучший интерфейс, поскольку он утверждает, что метод принимает произвольное число аргументов, и это все - никаких дальнейших предположений. Вы точно знаете, что сам метод не будет делать ничего другого со структурой данных, содержащей различные аргументы, и что никакая специальная структура данных не требуется.

Теоретически можно также принять словарь со значениями, равными None. А почему бы и нет? Это накладно и не нужно. Для меня, принимая список, когда вы можете принять, что вараргс добавляет накладные расходы. (как было указано в одном из комментариев)

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

Когда и если вам нужен список, то вы знаете, что вам нужен список!

Ах, обратите внимание, что f (*args) не то же самое, что f(list): второй хочет список, первый принимает произвольное число параметров (0 включено). Ладно, давайте определите второй аргумент как необязательный:

def f(l = []): pass

Круто, теперь у вас есть две проблемы, потому что вы должны убедиться, что вы не изменяете аргумент l: значения параметров по умолчанию. По какой причине? Потому что тебе не нравятся Арги. :)

PS: Я думаю, что это один из самых больших недостатков динамических языков: вы больше не видите интерфейс, но да! есть интерфейс!