Что в Python определяет порядок при переборе kwargs?

На Python я написал эту функцию, чтобы узнать, как **kwargs работает в Python:

def fxn(a1, **kwargs):
    print a1
    for k in kwargs:
        print k, " : ", kwargs[k]

Затем я вызвал эту функцию с помощью

fxn(3, a2=2, a3=3, a4=4)

Вот вывод, который напечатал мой интерпретатор Python:

3
a3  :  3
a2  :  2
a4  :  4

Почему цикл for напечатал значение a3 раньше, чем a2, хотя я сначала передал a2 в свою функцию?


person dangerChihuahua007    schedule 23.01.2012    source источник


Ответы (6)


kwargs — это словарь. Словари неупорядочены - проще говоря, порядок не определен и является деталью реализации. Заглянув внутрь, вы увидите, что порядок сильно различается в зависимости от хеш-значений элементов, порядка вставки и т. д., поэтому вам лучше не полагаться ни на что, связанное с этим.

person Community    schedule 23.01.2012
comment
Разве это не изменилось в последней версии Python? - person Adrien H; 21.02.2020
comment
@AdrienH: Это было в 3.6, см. этот ответ. - person ShadowRanger; 19.01.2021

Это словарь. И, как указано в документации, словарь не имеет порядка (из http://docs.python.org/tutorial/datastructures.html#dictionaries):

Лучше всего думать о словаре как о неупорядоченном наборе пар ключ: значение с требованием, чтобы ключи были уникальными (в пределах одного словаря).

Но вы можете заставить его обрабатываться в определенном порядке, например:

  • используя sorted():

    def fxn(a1, **kwargs):
        print a1
        for k in sorted(kwargs): # notice "kwargs" replaced by "sorted(kwargs)"
            print k, " : ", kwargs[k]
    
  • или с помощью типа OrderedDict (вы можете передать объект OrderedDict в качестве параметра, содержащего все пары ключ-значение):

    from collections import OrderedDict
    
    def fxn(a1, ordkwargs):
        print a1
        for k in ordkwargs:
            print k, " : ", ordkwargs[k]
    
    fxn(3, OrderedDict((('a2',2), ('a3',3), ('a4',4))))
    
person Tadeck    schedule 23.01.2012
comment
Смотрите мой ответ относительно OrderedDict. - person PaulMcG; 24.01.2012
comment
@PaulMcGuire: см. мой комментарий к вашему ответу. - person Tadeck; 24.01.2012
comment
Не могли бы вы написать это как fxn(3, **OrderedDict(etc...? О, подождите, я вижу, вы изменили подпись, чтобы взять диктовку, а не набор аргументов с ключевыми словами. Если вы собираетесь изменить подпись, вы можете просто передать список своих кортежей "ключ-значение". Но ОП действительно хотел перебрать **kwargs. - person PaulMcG; 24.01.2012
comment
@PaulMcGuire: я прямо заявил, что kwargs не имеет порядка, поэтому я предложил альтернативные решения: 1) повторение упорядоченного kwargs и 2) передача OrderedDict вместо использования аргументов ключевого слова. Да, вы можете передавать кортежи, но если вы передаете кортежи, вы не сможете использовать их как dict или OrderedDict, не так ли? :) Если вы используете мое второе решение, тело функции не сильно изменится (так как его пришлось бы изменить в случае кортежей, переданных в аргументе). Сравните dict, OrderedDict и tuple, чтобы понять, что я имею в виду. - person Tadeck; 24.01.2012

Это, наконец, было представлено в версии 3.6: теперь dict упорядочены, поэтому порядок аргументов ключевого слова сохраняется.

Python 3.6.0 (default, Jan 13 2017, 13:27:48) 
>>> def print_args(**kwargs):
...     print(kwargs.keys())
... 
>>> print_args(first=1, second=2, third=3)
dict_keys(['first', 'second', 'third'])
person afxentios    schedule 13.01.2017
comment
**kwargs не является OrderedDict в Python 3.6. Они продолжают быть dict, но теперь заказывают dict. - person Nuno André; 20.09.2017

Печальная ирония заключается в том, что диктификация **kwargs означает, что следующее не будет работать (по крайней мере, не так, как можно было бы ожидать):

od = OrderedDict(a=1, b=2, c=3)

Поскольку аргументы с ключевыми словами сначала встраиваются в неупорядоченный словарь, вы не можете быть уверены, что они будут вставлены в OrderedDict в том порядке, в котором они перечислены. :(

person PaulMcG    schedule 23.01.2012
comment
Но следующее будет: od = OrderedDict((('a',1), ('b',2), ('c',3))). Таким образом, в основном **kwargs, поскольку это dict, не будет иметь никакого порядка, но вы всегда можете передать OrderedDict в качестве параметра, и он будет иметь порядок, который вы хотите. - person Tadeck; 24.01.2012

Поскольку kwargs – это словарь Python, реализованный в виде хеш-таблицы< /strong> его порядок не сохраняется и фактически является случайным.

На самом деле, в качестве исправления недавней проблемы безопасности во многих языках программирования, в будущем порядок может даже изменение между вызовами вашей программы (вызовами интерпретатора Python).

person drrlvn    schedule 23.01.2012
comment
Не случайно — на самом деле, оно полностью детерминировано. Просто это нелегко предсказать, так как это зависит от хэша. - person Daniel Roseman; 23.01.2012
comment
Вы конечно правы. Вот почему я сказал «эффективно случайный», чтобы показать читателю, что ему не следует пытаться угадать это. С другой стороны, почти все, что делает компьютер, является детерминированным, но не может быть легко предсказуемым :) - person drrlvn; 23.01.2012

kwargs - это словарь, в Python они не упорядочены, поэтому результат по существу (псевдо-)случайный.

person Wim    schedule 23.01.2012