Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

140
Views
¿Por qué la excepción de Python realiza un seguimiento de los objetos pasados a __init__?

Por favor, consulte el siguiente fragmento de código:

 In [1]: class A(Exception): ...: def __init__(self, b): ...: self.message = b.message ...: In [2]: class B: ...: message = "hello" ...: In [3]: A(B()) Out[3]: __main__.A(<__main__.B at 0x14af96790>) In [4]: class A: ...: def __init__(self, b): ...: self.message = b.message ...: In [5]: A(B()) Out[5]: <__main__.A at 0x10445b0a0>

si A subclase de Exception , su repr devuelve una referencia a B() aunque solo pasemos el atributo de mensaje de B() .

¿Por qué es este el comportamiento intencional en Exception.repr y cómo funciona en python psuedocode si es posible dado que el código cpython no es demasiado legible?

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

Muy bien, creo que he encontrado el truco. Aquí está el código fuente de C, pero volveré a implementar algo similar en Python para demostrarlo.

Además del __init__ habitual (que está anulando), Python también tiene un método mágico llamado __new__ . Cuando construyes una A como A(B()) , está haciendo algo más o menos como

 b = B() a = A.__new__(A, b) a.__init__(b)

Ahora, ha anulado A.__init__ , por lo que Exception.__init__ nunca se llama. Pero A.__new__ es simplemente Exception.__new__ (más precisamente, es BaseException.__new__ , que es esencialmente el código fuente C que vinculé). Y, según el código vinculado, eso es aproximadamente

 class BaseException: def __new__(cls, *args): obj = object.__new__(cls) obj.args = args return obj

Entonces almacenamos explícitamente la tupla de argumentos en un campo llamado args en el objeto de excepción. Esta es la tupla de argumentos reales pasada al constructor, incluso si __init__ . Entonces, repr solo hace referencia a self.args para recuperar los argumentos originales.

Tenga en cuenta que estoy siendo un poco impreciso aquí. Si BaseException.__new__ en REPL, verá que todavía es object.__new__ . Las devoluciones de llamada de C funcionan de manera diferente y usan algo de magia de compilación a la que no tenemos acceso, pero la idea básica es la misma.

over 4 years ago · Santiago Trujillo Report

0

Cuando se crea un objeto de Python, se llama al método __new__ de la clase, y luego se llama a __init__ en la nueva instancia que devuelve el método __new__ (suponiendo que devuelva una nueva instancia, que a veces no lo hace).

Su método __init__ anulado no mantiene una referencia a b , pero no __new__ , por lo que hereda el método __new__ definido aquí ( enlace fuente de CPython ):

 static PyObject * BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { // ... if (args) { self->args = args; Py_INCREF(args); return (PyObject *)self; } // ... }

Omití las partes que no son relevantes. Como puede ver, el método __new__ de la clase BaseException almacena una referencia a la tupla de argumentos utilizados al crear la excepción y, por lo tanto, esta tupla está disponible para que el método __repr__ imprima una referencia a los objetos utilizados para instanciar la excepción. Entonces es esta tupla la que retiene una referencia al argumento original b . Esto es consistente con la expectativa general de que repr debería devolver el código de Python que crearía un objeto en el mismo estado, si es posible.

Tenga en cuenta que solo args , no kwds , tiene este comportamiento; el método __new__ no almacena una referencia a kwds y __repr__ no la imprime, por lo que deberíamos esperar no ver el mismo comportamiento si se llama al constructor con un argumento de palabra clave en lugar de un argumento posicional. Efectivamente, eso es lo que observamos:

 >>> A(B()) A(<__main__.B object at 0x7fa8e7a23860>,) >>> A(b=B()) A()

Un poco extraño ya que se supone que los dos objetos A tienen el mismo estado, pero así es como se escribe el código, de todos modos.

over 4 years ago · Santiago Trujillo Report

0

Podemos explorar la diferencia con la pestaña de Ipython completa.

 In [230]: a=A(B()) In [231]: a Out[231]: __main__.A(<__main__.B at 0x7f29cd1e71c0>)

Con la pestaña de ipython completa:

 In [234]: a. args a.bin message a.npy with_traceback() a.t1 In [234]: a.args Out[234]: (<__main__.B at 0x7f29cd1e71c0>,)

La clase sin excepción, solo tiene un atributo, message :

 In [235]: a1=A1(B()) In [236]: a1 Out[236]: <__main__.A1 at 0x7f29ccc21a60> In [237]: a1.message Out[237]: 'hello'

y una simple excepción:

 In [238]: e=Exception(B()) In [239]: e Out[239]: Exception(<__main__.B at 0x7f29ccd0e790>) In [240]: e. args with_traceback()

Y usando a como excepción:

 In [240]: raise(a) Traceback (most recent call last): File "<ipython-input-240-cf5e5bb7d43e>", line 1, in <module> raise(a) A: <__main__.B object at 0x7f29cd1e71c0>
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!