Чи є різниця між використанням прямої диктату та конструктором диктату?


204

Використовуючи PyCharm, я помітив, що він пропонує перетворити буквальний текст :

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

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

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

Чи відрізняються ці різні підходи якось суттєвим чином?

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


4
dict()приймає список пар ключових значень, а також дозволяє називати параметри, тому він може використовуватись для створення будь-якого типу дикту, тільки не з синтаксисом, який ви використовуєте. Також, мабуть, нічого не варто, щоб у pyCharm була помилка ( youtrack.jetbrains.net/issue/PY-2512 ) саме через те, що ви виявили, що було виправлено).
Wooble

1
пов'язані: stackoverflow.com/questions/5790860/… (резюме: поведінка PyCharm повільніша та
бридкіша

1
Мабуть, CPython 2.7 dict () повільніше (у 6 разів повільніше?). Дивіться: doughellmann.com/2012/11/… У будь-якому випадку я починаю віддавати перевагу синтаксису конструктора, так як мені легше набирати та переміщувати код між диктами та функціональними викликами.
Девід Уітон

2
Не забувайте пробіли: ви не можете створити ключі, які містять пробіли, використовуючи другий спосіб. Перший спосіб, однак, може взяти будь-який рядок, це не хвилюватиметься. Те саме стосується і Unicode, звичайно.
CamilB

2
В Python 2, dict(abc = 123)конструктор створює словник з байтами-строковими ключами 'abc', які можуть бути дивно , якщо ви використовуєте unicode_literalsі чекаючи словникові ключі бути юнікодом u'abc'. Дивіться stackoverflow.com/questions/20357210/… .
Li-aung Yip

Відповіді:


116

Я думаю, ви вказали на найбільш очевидну різницю. Крім того,

Перший не потребує пошуку, dictякий повинен зробити його трохи більш швидким

другий дивиться dictв , locals()а потім globals()і знаходить вбудовані, так що ви можете перемкнути поведінку, визначаючи місцевий називається dict, наприклад , хоча я не можу думати про будь-якому місці це було б гарною ідеєю , крім , можливо , при налагодженні


4
Приклад , коли локальний називається диктує може бути корисним: stackoverflow.com/a/7880276/313113
Бітек

Я вважаю, що також використання dict () спочатку побудує дикт для аргументів до dict (), а потім створить другий dict для фактичного dict, який буде створений. Braces створює екземпляр dict за один крок.
NeilG

56

Літерал набагато швидше, оскільки використовує оптимізовані 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
@Elliot ОП не запитував, хто з них швидший.
Том Фергюсон

5
Якщо ви створюєте або мільйони диктів, або один дикт з мільйонами клавіш, з дикталів dict у своєму джерелі, ви робите це неправильно.
jwg

41

Вони майже однаково виглядають на Python 3.2.

Як зазначав гніблер, перший не потребує пошуку 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 просто намагається бути надзвичайно приємним. Так само, як завжди здається, пропонують перетворити рядки з одним котируванням у подвійне цитування - без видимих ​​причин.
малігрей

1
Цитувати свої ключі потрібно лише в тому випадку, якщо ваші клавіші - це рядки. Вони так само легко можуть бути кортежами фрозенсетів поплавців, хоча це може стати трохи потворним.
Wooble

7

Велика різниця з python 3.4 + pycharm полягає в тому, що конструктор dict () створює повідомлення про "синтаксичну помилку", якщо кількість клавіш перевищує 256.

Зараз я вважаю за краще використовувати буквальний текст.


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}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.