Как передать dict как позиционный аргумент, а не как аргумент ключевого слова

Я создал функцию (send_to_api), которая принимает три аргумента. user_id и event_type принимают строки, а event_properties - словарь, который я передаю в качестве позиционного аргумента. Это формат, который требуется для внешнего API. Вот упрощенная версия функции:

def send_to_api(user_id, event_type, *args):

    event_args = {"user_id": user_id,
              "event_type": event_type,
              "event_properties": args}

    data = json.dumps(event_args)
    r = requests.post(url=url, data=data)

В другом месте функция вызывается так:

event_properties = {"property_1": "a", "property_whatever": "b"}

send_to_api("my_user_id", "my_event", event_properties)

После ознакомления с * args и ** kwargs я все еще, кажется, делаю что-то не так, потому что dict event_properties не проходит.

Думаю, причина в том, что я пытаюсь передать словарь в качестве позиционного аргумента? Или любой другой указатель на то, где здесь что-то не так?

Изменить *args на args?

Aran-Fey 05.06.2018 09:06
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
1 718
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

с одним * это будет кортеж, но с двумя ** будет словарь

def send_to_api(user_id, event_type, **args):
...
...
send_to_api("my_user_id", "my_event", **event_properties)

какой-нибудь полезный документы

Выход

{
 "user_id": "my_user_id", 
 "event_type": "my_event", 
 "event_properties": {
                       "property_1": "a", 
                       "property_whatever": "b"
                      }
}

Вы должны использовать распаковку dict следующим образом:

send_to_api("my_user_id", "my_event", **event_properties)

Подробнее об этом здесь.

Ответ принят как подходящий

Для лучшего понимания вы можете просто отладить свою функцию send_to_api. Но вкратце:

  1. * args используется для отправки в функцию списка аргументов переменной длины без ключевых слов.

    def send_to_api(param1, param2, *args):
        print(param1, param2, args)
    

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

    send_to_api(1,2, {"a":1, "b":2})
    ...
    1 2 ({'a': 1, 'b': 2},)

И вы можете получить к нему доступ как аргументы [0], поэтому ваш код может выглядеть так:

    def send_to_api(user_id, event_type, *args):
        event_args = {"user_id": user_id,
                      "event_type": event_type,
                      "event_properties": args[0]}

Но, вероятно, будет лучше, если вы сможете изменить подпись на что-то вроде этого:

    def send_to_api(user_id, event_type, event_properties, *args):
        event_args = {"user_id": user_id,
                      "event_type": event_type,
                      "event_properties": event_properties}

Как по мне он намного чище;)

Если вам нужно обрабатывать любое количество позиционных аргументов - используйте * args в определении функции, тогда вы можете получить к ним доступ в кортеже аргументы внутри функции.

Если вам нужно обрабатывать любое количество аргументов с ключевыми словами - используйте ** kwargs в определении функции, тогда внутри функции вы можете получить к ним доступ в словаре kwargs.

так например:

def send_to_api(arg1, *args, **kwargs):
    print(arg1, args, kwargs)

затем вы можете вызвать функцию с помощью:

 send_to_api(1, 2 , key1=1, key2=2)

он напечатает:

 1 (2,) {'key1': 1, 'key2': 2}

так что вы можете видеть, что аргументы - это кортеж (2,); kwargs - это словарь {'key1': 1, 'key2': 2}

Также вы можете передать kwargs в функцию как словарь с оператором распаковки:

  send_to_api(1, 2, **{'key1': 1, 'key2': 2})

он напечатает то же самое:

   1 (2,) {'a': 1, 'b': 2}

Итак, словарь {'key1': 1, 'key2': 2} был распакован с помощью оператора ** в аргументы ключевого слова key1 = 1, key2 = 2 и доступен внутри вашей функции в kwargs dict.

Никакого волшебства, просто попробуйте поиграть в интерактивной консоли Python.

* args и ** kwargs предназначены для передачи функции переменного количества аргументов. Перед их использованием вам необходимо знать, нужен ли вам аргумент keyworded или аргументы non-keyworded.

В вашем случае вы используете аргумент на основе ключевых слов, поэтому вам нужно изменить его на **kwargs

def test_arg_kwargs(arg1, arg2, **kwargs):
    print("arg1:", arg1)
    print("arg2:", arg2)
    print("arg3:", kwargs)


event_properties = {"property_1": "a", "property_whatever": "b"}

test_arg_kwargs('arg1', 'arg2', **event_properties)

выход:

arg1: arg1
arg2: arg2
arg3: {'property_1': 'a', 'property_whatever': 'b'}

Другие вопросы по теме