AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 78427983
Accepted
Waket Zheng
Waket Zheng
Asked: 2024-05-04 14:19:19 +0800 CST2024-05-04 14:19:19 +0800 CST 2024-05-04 14:19:19 +0800 CST

Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError?

  • 772

Normalmente, se você tentar passar vários valores para o mesmo argumento de palavra-chave, obterá um TypeError:

In [1]: dict(id=1, **{'id': 2})
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [1], in <cell line: 1>()
----> 1 dict(id=1, **{'id': 2})

TypeError: dict() got multiple values for keyword argument 'id'

Mas se você fizer isso enquanto lida com outra exceção , você receberá um KeyError:

In [2]: try:
   ...:     raise ValueError('foo') # no matter what kind of exception
   ...: except:
   ...:     dict(id=1, **{'id': 2}) # raises: KeyError: 'id'
   ...: 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [2], in <cell line: 1>()
      1 try:
----> 2     raise ValueError('foo') # no matter what kind of exception
      3 except:

ValueError: foo

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
Input In [2], in <cell line: 1>()
      2     raise ValueError('foo') # no matter what kind of exception
      3 except:
----> 4     dict(id=1, **{'id': 2})

KeyError: 'id'

O que está acontecendo aqui? Como uma exceção completamente não relacionada poderia afetar o tipo de exceção dict(id=1, **{'id': 2})lançada?

Para contextualizar, descobri esse comportamento ao investigar o seguinte relatório de bug: https://github.com/tortoise/tortoise-orm/issues/1583

Isso foi reproduzido no CPython 3.11.8, 3.10.5 e 3.9.5.

python
  • 1 1 respostas
  • 376 Views

1 respostas

  • Voted
  1. Best Answer
    user2357112
    2024-05-04T15:15:42+08:002024-05-04T15:15:42+08:00

    Isso parece um bug do Python.

    O código que deveria aumentar TypeErrorfunciona detectando e substituindo um inicial KeyError, mas esse código não funciona direito. Quando a exceção ocorre no meio de outro manipulador de exceção, o código que deveria gerar o TypeErrorarquivo KeyError. Acaba deixando passar KeyError, em vez de substituí-lo por um arquivo TypeError.

    O bug parece ter desaparecido na versão 3.12, devido a mudanças na implementação da exceção.


    Aqui está o aprofundamento do código-fonte do CPython 3.11.8. Código semelhante existe em 3.10 e 3.9.

    Como podemos ver usando o dismódulo para examinar o bytecode para dict(id=1, **{'id': 2}):

    In [1]: import dis
    
    In [2]: dis.dis("dict(id=1, **{'id': 2})")
      1           0 LOAD_NAME                0 (dict)
                  2 LOAD_CONST               3 (())
                  4 LOAD_CONST               0 ('id')
                  6 LOAD_CONST               1 (1)
                  8 BUILD_MAP                1
                 10 LOAD_CONST               0 ('id')
                 12 LOAD_CONST               2 (2)
                 14 BUILD_MAP                1
                 16 DICT_MERGE               1
                 18 CALL_FUNCTION_EX         1
                 20 RETURN_VALUE
    

    Python usa o DICT_MERGEopcode para mesclar dois dictos, para construir o dict do argumento da palavra-chave final.

    A parte relevante do DICT_MERGE código é a seguinte:

                if (_PyDict_MergeEx(dict, update, 2) < 0) {
                    format_kwargs_error(tstate, PEEK(2 + oparg), update);
                    Py_DECREF(update);
                    goto error;
                }
    

    Ele tenta _PyDict_MergeExmesclar dois dictos e, se isso falhar (e gerar uma exceção), ele tenta format_kwargs_errorgerar uma exceção diferente .

    Quando o terceiro argumento _PyDict_MergeExfor 2, essa função gerará um KeyErrorpara chaves duplicadas, dentro da dict_mergefunção auxiliar. É daí que KeyErrorvem.

    Uma vez que o KeyErroré gerado, format_kwargs_errortem a função de substituí-lo por um TypeError. Ele tenta fazer isso com o seguinte código :

        else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
            PyObject *exc, *val, *tb;
            _PyErr_Fetch(tstate, &exc, &val, &tb);
            if (val && PyTuple_Check(val) && PyTuple_GET_SIZE(val) == 1) {
    

    mas este código está procurando uma exceção não normalizada , uma forma interna de representar exceções que não está exposta ao código em nível Python. Ele espera que o valor da exceção seja uma tupla de 1 elemento contendo a chave para a qual KeyError foi gerado, em vez de um objeto de exceção real.

    Exceções levantadas dentro do código C geralmente não são normalizadas, mas não se ocorrerem enquanto o Python estiver lidando com outra exceção. Exceções não normalizadas não podem tratar o encadeamento de exceções , que ocorre automaticamente para exceções geradas dentro de um manipulador de exceções. Neste caso, a _PyErr_SetObjectrotina interna normalizará automaticamente a exceção:

        exc_value = _PyErr_GetTopmostException(tstate)->exc_value;
        if (exc_value != NULL && exc_value != Py_None) {
            /* Implicit exception chaining */
            Py_INCREF(exc_value);
            if (value == NULL || !PyExceptionInstance_Check(value)) {
                /* We must normalize the value right now */
    

    Como o KeyErrorfoi normalizado, format_kwargs_errornão entende o que está vendo. Ele deixa KeyErrorpassar, em vez de aumentar o TypeErrorque deveria.


    No Python 3.12, as coisas são diferentes. A representação da exceção interna foi alterada, portanto qualquer exceção levantada é sempre normalizada. Assim, a versão Python 3.12 format_kwargs_errorprocura uma exceção normalizada em vez de uma exceção não normalizada e, se _PyDict_MergeExgerou um KeyError, o código irá reconhecê-lo:

        else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
            PyObject *exc = _PyErr_GetRaisedException(tstate);
            PyObject *args = ((PyBaseExceptionObject *)exc)->args;
            if (exc && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1) {
    
    • 22

relate perguntas

  • Como divido o loop for em 3 quadros de dados individuais?

  • Como verificar se todas as colunas flutuantes em um Pandas DataFrame são aproximadamente iguais ou próximas

  • Como funciona o "load_dataset", já que não está detectando arquivos de exemplo?

  • Por que a comparação de string pandas.eval() retorna False

  • Python tkinter/ ttkboostrap dateentry não funciona quando no estado somente leitura

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve