Есть ли разница между использованием dict-литерала и dict-конструктора?

204

Используя PyCharm, я заметил, что он предлагает преобразовать dict литерал :

d = {
    'one': '1',
    'two': '2',
}

в конструктор dict :

d = dict(one='1', two='2')

Различаются ли эти разные подходы каким-либо существенным образом?

(При написании этого вопроса я заметил, что с помощью dict()него невозможно указать числовой ключ ... d = {1: 'one', 2: 'two'}возможно, но, очевидно, dict(1='one' ...)нет. Что-нибудь еще?)

maligree
источник
4
dict()принимает список пар ключ-значение, а также разрешает именованные параметры, поэтому он может использовать для создания любого типа dict, только не с синтаксисом, который вы используете. Также, вероятно, ничего не стоит, что в pyCharm была ошибка ( youtrack.jetbrains.net/issue/PY-2512 ) именно из-за того, что вы обнаружили, и она была исправлена).
Вубл
1
связанные: stackoverflow.com/questions/5790860/… (резюме: поведение PyCharm медленнее и уродливее)
Wooble
1
По-видимому, CPython 2.7 dict () медленнее (в 6 раз медленнее?). См .: doughellmann.com/2012/11/… В любом случае, я все равно предпочитаю синтаксис конструктора, поскольку мне легче набирать и перемещать код между dict и вызовами функций.
Дэвид Уитон
2
Не забывайте пробелы: вы не можете создавать ключи, содержащие пробелы, используя второй способ. Первый способ, тем не менее, может принять любую строку, это не волнует. То же самое относится и к Unicode, конечно.
CamilB
2
В Python 2 dict(abc = 123)конструктор создает словарь с ключами байтовой строки 'abc', что может быть удивительно, если вы используете unicode_literalsи ожидаете, что ключи словаря будут в юникоде u'abc'. См. Stackoverflow.com/questions/20357210/… .
Ли Аунг Ип

Ответы:

116

Я думаю, что вы указали на наиболее очевидную разницу. Помимо этого,

первое не нуждается в поиске, dictчто должно сделать его чуть-чуть быстрее

второй смотрит dictв , locals()а затем globals()и находит встроенные, так что вы можете переключить поведение, определяя местный называется dict, например , хотя я не могу думать о любом месте это было бы хорошей идеей , кроме , возможно , при отладке

Джон Ла Рой
источник
4
Пример того, где может быть полезен
локальный файл
Я считаю, что использование dict () сначала создаст dict для аргументов dict (), а затем создаст второй dict для создаваемого фактического экземпляра dict. Брекеты создают dict экземпляр за один шаг.
NeilG
56

Literal намного быстрее, так как он использует оптимизированные коды операций BUILD_MAP и STORE_MAP, а не универсальный CALL_FUNCTION:

> python2.7 -m timeit "d = dict(a=1, b=2, c=3, d=4, e=5)"
1000000 loops, best of 3: 0.958 usec per loop

> python2.7 -m timeit "d = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5}"
1000000 loops, best of 3: 0.479 usec per loop

> python3.2 -m timeit "d = dict(a=1, b=2, c=3, d=4, e=5)"
1000000 loops, best of 3: 0.975 usec per loop

> python3.2 -m timeit "d = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5}"
1000000 loops, best of 3: 0.409 usec per loop
Даниэль Клюев
источник
10
@Ned: Большую часть времени для большинства пользователей это не имеет никакого значения, но бывают ситуации, когда создаются миллионы или миллиарды, и увеличение в 2 раза имеет смысл.
Мистер Фуз
5
@MrFooz: бывают такие ситуации. Я думаю, вы обнаружите, что 99,9% людей, занимающихся микромаймингом, не находятся в таких ситуациях.
Нед Бэтчелдер
29
@Ned Это уместно в теме, спрашивая, какая из них быстрее, хотя.
Эллиотт
11
@ Эллиот ОП не спросил, какой из них быстрее.
Том Фергюсон
5
Если вы производите миллионы диктов или один диктат с миллионами ключей из буквальных литералов в вашем источнике, вы делаете это неправильно.
jwg
41

Они выглядят почти одинаково на Python 3.2.

Как указал gnibbler, первый не нуждается в поиске dict, что должно сделать его чуть-чуть быстрее.

>>> def literal():
...   d = {'one': 1, 'two': 2}
...
>>> def constructor():
...   d = dict(one='1', two='2')
...
>>> import dis
>>> dis.dis(literal)
  2           0 BUILD_MAP                2
              3 LOAD_CONST               1 (1)
              6 LOAD_CONST               2 ('one')
              9 STORE_MAP
             10 LOAD_CONST               3 (2)
             13 LOAD_CONST               4 ('two')
             16 STORE_MAP
             17 STORE_FAST               0 (d)
             20 LOAD_CONST               0 (None)
             23 RETURN_VALUE
>>> dis.dis(constructor)
  2           0 LOAD_GLOBAL              0 (dict)
              3 LOAD_CONST               1 ('one')
              6 LOAD_CONST               2 ('1')
              9 LOAD_CONST               3 ('two')
             12 LOAD_CONST               4 ('2')
             15 CALL_FUNCTION          512
             18 STORE_FAST               0 (d)
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE
Паоло Моретти
источник
Обратите внимание, что в некоторых реализациях это на самом деле не «крошечный кусочек», а скорее в 100 раз:$ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' "{'a': 1, 'b': 2, 'c': 3}" ....... Mean +- std dev: 1.73 ns +- 0.14 ns $ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' '{k:v for k,v in i}' ....... Mean +- std dev: 139 ns +- 10 ns $ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' 'dict(i)' ....... Mean +- std dev: 188 ns +- 16 ns
DylanYoung
13

Эти два подхода создают идентичные словари, за исключением, как вы заметили, где лексические правила Python мешают.

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

a = "hello"
d = {
    a: 'hi'
    }

dict()Конструктор дает большую гибкость из - за многообразия форм ввода он принимает. Например, вы можете предоставить ему итератор пар, и он будет обрабатывать их как пары ключ / значение.

Я понятия не имею, почему PyCharm предложил бы преобразовать одну форму в другую.

Нед Бэтчелдер
источник
2
Ну, я думаю, что PyCharm просто пытается быть очень милым. Точно так же, как всегда кажется, предлагают преобразовать строки в одинарных кавычках в двойные - без видимой причины.
Maligree
1
Вам нужно только заключить в кавычки ваши ключи, если ваши ключи являются строками. С таким же успехом они могли бы быть кортежами морозилок поплавков, хотя это могло бы стать немного уродливым.
Wooble
7

Одна большая разница с python 3.4 + pycharm заключается в том, что конструктор dict () выдает сообщение «синтаксическая ошибка», если количество ключей превышает 256.

Я предпочитаю использовать dict literal сейчас.

Мишель Буарон
источник
3
Это не просто Python 3.4. Это связано с тем, что CPython <3.7 имеет максимальное количество 255 буквенных аргументов, передаваемых вызываемой. ( stackoverflow.com/a/8932175/2718295 )
Cowbert
6

Из Python 2.7 учебник:

Пара скобок создает пустой словарь: {}. Размещение разделенного запятыми списка пар ключ: значение в фигурных скобках добавляет начальные пары ключ: значение в словарь; так же словари пишутся на выходе.

tel = {'jack': 4098, 'sape': 4139}
data = {k:v for k,v in zip(xrange(10), xrange(10,20))}

Пока:

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

tel = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) {'sape': 4139, 'jack': 4098, 'guido': 4127}
data = dict((k,v) for k,v in zip(xrange(10), xrange(10,20)))

Когда ключи являются простыми строками, иногда проще указать пары, используя аргументы ключевых слов:

dict(sape=4139, guido=4127, jack=4098)
>>>  {'sape': 4139, 'jack':4098, 'guido': 4127}

Таким образом, и {}, и dict () создают словарь, но предоставляют немного разные способы инициализации словарных данных.

Артём Рудзенка
источник
3

Я нахожу, что литерал dict d = {'one': '1'}гораздо более удобен для чтения, определяя данные, а не присваивая значения вещей и отправляя ихdict() конструктору.

С другой стороны, я видел, как люди неправильно набирают буквальный d = {'one', '1'} неправильно набирают который в современном python 2.7+ создаст набор.

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

Ли Пенкман
источник
Я регулярно забываю, что буквальный синтаксис для sets существует. Хотелось бы, чтобы для упорядоченных диктов был буквальный синтаксис ... уверен, что я использую их чаще, чем наборы
ArtOfWarfare
2

Литерал dict () удобен, когда вы копируете вставленные значения из чего-то другого (без python). Например, из списка переменных окружения. если у вас есть файл Bash, скажем

FOO='bar'
CABBAGE='good'

Вы можете легко вставить затем в dict()литерал и добавлять комментарии. Это также упрощает обратное, копирование в другое. Принимая во внимание, что {'FOO': 'bar'}синтаксис довольно уникален для Python и JSON. Так что если вы часто используете json, вы можете использовать {}литералы с двойными кавычками.

Ник Хамрич
источник
2

Не существует dict-литерала для создания классов, унаследованных от dict, пользовательских классов dict с дополнительными методами. В таком случае должен использоваться пользовательский конструктор класса dict, например:

class NestedDict(dict):

    # ... skipped

state_type_map = NestedDict(**{
    'owns': 'Another',
    'uses': 'Another',
})
Дмитрий Синцов
источник
0

Также учтите тот факт, что токены, которые соответствуют операторам, не могут использоваться в синтаксисе конструктора, то есть в деширированных ключах.

>>> dict(foo-bar=1)
File "<stdin>", line 1
SyntaxError: keyword can't be an expression

>>> {'foo-bar': 1}
{'foo-bar': 1}
Брайан Уиттон
источник