Existe uma diferença entre usar um literal dict e um construtor dict?


204

Usando PyCharm, notei que ele oferece a conversão de um dict literal :

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

em um construtor de ditado :

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

Essas abordagens diferentes diferem de alguma maneira significativa?

(Ao escrever esta pergunta, notei que o uso dict()parece impossível especificar uma chave numérica .. d = {1: 'one', 2: 'two'}é possível, mas, obviamente, dict(1='one' ...)não é. Mais alguma coisa?)


4
dict()leva uma lista de pares de valores-chave e permite parâmetros nomeados, para que possa ser usado para criar qualquer tipo de regra, mas não com a sintaxe que você está usando. Provavelmente também não vale nada que tenha ocorrido um erro ( youtrack.jetbrains.net/issue/PY-2512 ) no pyCharm especificamente por causa do que você descobriu, que foi corrigido).
Wooble

1
related: stackoverflow.com/questions/5790860/... (resumo: o comportamento de PyCharm é mais lento e mais feio)
Wooble

1
Aparentemente, o CPython 2.7 dict () é mais lento (6 vezes mais lento?). Consulte: doughellmann.com/2012/11/… De qualquer forma, estou começando a preferir a sintaxe do construtor de qualquer maneira, pois acho mais fácil digitar e mover o código entre dict e chamadas de função.
David Wheaton

2
Não se esqueça de espaços: você não pode criar chaves que contenham espaços usando o segundo caminho. A primeira maneira, porém, pode levar qualquer corda, não vai se importar. O mesmo se aplica ao Unicode, é claro.
CamilB

2
No Python 2, o dict(abc = 123)construtor produz um dicionário com chaves de cadeia de bytes 'abc', o que pode ser surpreendente se você estiver usando unicode_literalse esperando que as chaves de dicionário sejam unicode u'abc'. Consulte stackoverflow.com/questions/20357210/… .
Li-aung Yip

Respostas:


116

Eu acho que você apontou a diferença mais óbvia. Além disso,

o primeiro não precisa procurar, o dictque deve torná-lo um pouco mais rápido

a segunda olha para cima dictem locals()e, em seguida, globals()e os achados da embutidas, para que possa mudar o comportamento definindo um local chamado dict, por exemplo, embora eu não consigo pensar em qualquer lugar esta seria uma boa ideia para além de, talvez, quando a depuração


4
Um exemplo de onde um chamado dict local pode ser útil: stackoverflow.com/a/7880276/313113
Bitek

Acredito que o uso de dict () primeiro construa um dict para os argumentos dict () e crie um segundo dict para a instância dict real a ser criada. O Braces cria a instância do dict em uma etapa.
NeilG 14/05/19

56

O literal é muito mais rápido, pois usa os opcodes BUILD_MAP e STORE_MAP otimizados, em vez dos genéricos 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: Na maioria das vezes, para a maioria dos usuários, isso não importa, mas há situações em que milhões ou bilhões deles estão sendo criados e uma aceleração de 2x é significativa.
Mr Fooz

5
@ MrFooz: existem situações como essa. Acho que você descobrirá que 99,9% das pessoas que fazem micro-temporizações não estão nessas situações.
Ned Batchelder

29
@ Ned É pertinente em um tópico perguntando qual é o mais rápido.
Elliott

11
@ Elliot O OP não perguntou qual é o mais rápido.
Tom Ferguson

5
Se você está produzindo milhões de dictos ou um dict com milhões de chaves, a partir de literais de dict em sua fonte, está fazendo errado.
GTC

41

Eles têm a mesma aparência no Python 3.2.

Como apontou o gnibbler, o primeiro não precisa pesquisar dict, o que deve torná-lo um pouco mais rápido.

>>> 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

Note-se que em algumas implementações, este não é realmente um "pouquinho", mais como um fator de 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

Essas duas abordagens produzem dicionários idênticos, exceto, como você observou, onde as regras lexicais do Python interferem.

Os literais de dicionário são um pouco mais obviamente dicionários, e você pode criar qualquer tipo de chave, mas precisa citar os nomes das chaves. Por outro lado, você pode usar variáveis ​​para chaves se precisar, por algum motivo:

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

o dict() construtor oferece mais flexibilidade devido à variedade de formas de entrada necessárias. Por exemplo, você pode fornecer a ele um iterador de pares e ele os tratará como pares de chave / valor.

Não faço ideia por que o PyCharm se ofereceria para converter um formulário no outro.


2
Bem, acho que o PyCharm está apenas tentando ser mais agradável. Assim como sempre parece oferecer a conversão de cadeias simples entre aspas duplas - sem motivo aparente.
maligree

1
Você só precisa citar suas chaves se elas forem cadeias de caracteres. Eles poderiam facilmente ser tuplas de conjuntos de carros alegóricos, embora isso possa ficar um pouco feio.
Wooble

7

Uma grande diferença com o python 3.4 + pycharm é que o construtor dict () produz uma mensagem de "erro de sintaxe" se o número de chaves exceder 256.

Eu prefiro usar o literal dict agora.


3
Não é apenas python 3.4. Isso ocorre porque o CPython <3.7 possui um número máximo de 255 argumentos literais passados ​​para uma chamada. ( stackoverflow.com/a/8932175/2718295 ) #
31428 cowbert

6

Do tutorial do python 2.7:

Um par de chaves cria um dicionário vazio: {}. Colocar uma lista separada por vírgula de pares de chave: valor entre chaves adiciona pares de chave: valor inicial ao dicionário; também é assim que os dicionários são gravados na saída.

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

Enquanto:

O construtor dict () cria dicionários diretamente de listas de pares de valores-chave armazenados como tuplas. Quando os pares formam um padrão, a compreensão da lista pode especificar de forma compacta a lista de valores-chave.

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)))

Quando as chaves são cadeias simples, às vezes é mais fácil especificar pares usando argumentos de palavra-chave:

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

Portanto, {} e dict () produzem dicionário, mas fornecem maneiras um pouco diferentes de inicialização dos dados do dicionário.


3

Acho o literal do ditado d = {'one': '1'}muito mais legível, seus dados definidores, em vez de atribuir valores às coisas e enviá-los ao dict()construtor.

Por outro lado, tenho visto pessoas digitarem o dict literal como d = {'one', '1'}que no python moderno 2.7+ criará um conjunto.

Apesar disso, eu ainda prefiro usar o conjunto literal de todas as maneiras, porque acho que é a sua preferência pessoal mais legível, suponho.


Eu esqueço regularmente que a sintaxe literal de sets existe. Eu gostaria que houvesse uma sintaxe literal para ditados ordenados ... com certeza os uso com mais frequência do que conjuntos.
ArtOfWarfare

2

o literal dict () é bom quando você está copiando e colando valores de outra coisa (nenhum python). Por exemplo, uma lista de variáveis ​​de ambiente. se você tivesse um arquivo bash, diga

FOO='bar'
CABBAGE='good'

você pode colar facilmente em um dict()literal e adicionar comentários. Também facilita fazer o oposto, copiar para outra coisa. Enquanto a {'FOO': 'bar'}sintaxe é bastante exclusiva para python e json. Portanto, se você usa muito o json, convém usar {}literais com aspas duplas.


2

Não há literal de dict para criar classes herdadas por dict, classes de dict personalizadas com métodos adicionais. Nesse caso, o construtor de classe dict personalizado deve ser usado, por exemplo:

class NestedDict(dict):

    # ... skipped

state_type_map = NestedDict(**{
    'owns': 'Another',
    'uses': 'Another',
})

0

Considere também o fato de que os tokens correspondentes aos operadores não podem ser usados ​​na sintaxe do construtor, ou seja, chaves comherização.

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

>>> {'foo-bar': 1}
{'foo-bar': 1}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.