¿Puede una conversión a cadena generar un error?

Me preguntaba si convertir a una cadena, es decir, str(sth) puede generar una excepción, como por ejemplo float(sth) , ¿no? Le estoy pidiendo a esto que sepa si es necesario envolver mi código en:

 try: x = str(value) except ValueError: x = None 

para asegurarse de que la ejecución no se detenga debido a un error de conversión.

¿También difiere esto en Python 2 y 3, ya que la clase str es diferente en ellos?

Si encuentra una clase personalizada que __repr__ explícitamente una excepción en __str__ (o __repr__ si __str__ no está definido). O, por ejemplo, una clase que devuelve un objeto de bytes de __str__ :

 class Foo: def __str__(self): return b'' str(Foo()) # TypeError: __str__ returned non-string (type bytes) 

Pero personalmente, nunca he visto esto y estoy bastante seguro de que nadie lo ha visto; Sería una tontería hacerlo. Del mismo modo, un error tonto en la implementación de __str__ o casos de borde puede crear otra Exception . Es interesante ver cómo podría empujar a Python a estos casos de borde (mire la respuesta @ user2357112 aquí).

Aparte de ese caso, ningún Py2 Py3 Py2 Py3 una excepción en este caso, ya que está definido para todos ellos en Py2 y Py3 .

Para las clases definidas por el usuario, str usará el object.__str__ de manera predeterminada si no está definido en Python 3 y, en Python 2, lo usa si una clase es una nueva clase de estilo (se hereda del object ).

Si una clase es una clase de estilo antiguo, creo que es classobj.__str__ que se usa para las clases y la instance.__str__ para las instancias.

En general, no detectaría esto, los casos especiales no son lo suficientemente especiales para esto.

En teoría sí, puede, pero en la práctica es casi seguro que no. La forma en que funciona la función str es que llama a la función __str__ del objeto que está convirtiendo en una cadena. Los tipos List como List , tipos numéricos y otros devolverán valores sobre lo que usted esperaría, [x,y,z] para una List , el número como una cadena para un tipo numérico, etc. El object tiene un método __str__ que proporciona una salida como at 0x7fdb2fa6a640> , que a menudo no es muy útil pero no genera una excepción.

Por lo tanto, llamar a str en cualquier builtin es casi seguro que no va a generar una excepción (es cierto que no puedo encontrar una garantía para esto en la documentación de Python, pero no puedo imaginar una situación en la que suceda)

Dicho esto, es posible anular el método __str__ para una clase personalizada. Si se hace esto, entonces ese método podría generar una excepción al igual que cualquier otra función que pudiera escribir un progtwigdor: una excepción intencional, un IndexError o similar, etc. Incluso si no está anulando __str__ , si usando un módulo que lo hace, es completamente posible que el autor de ese módulo haya cometido algún error en algún borde raro que genere una excepción.

El resultado final: no debería hacerlo si todos han hecho bien su trabajo, pero podría hacerlo. En casi cualquier situación práctica no me molestaría en intentar / atrapar.

Fácilmente. Podría tener una clase contenedora con una __repr__ __str__ o __repr__ no __repr__ que termina en un bucle de referencia:

 import numpy x = numpy.empty([1], dtype=object) x[0] = x str(x) # RuntimeError: maximum recursion depth exceeded 

O simplemente un objeto realmente altamente nested:

 x = [] for i in xrange(2000): x = [x] str(x) # RuntimeError: maximum recursion depth exceeded while getting the repr of a list 

Diablos, incluso con los tipos incorporados, podrías obtener algo mucho peor que una excepción:

 x = {} for i in range(1000000): x = {1: x} str(x) # python.exe has stopped working # Windows can check online for a solution to the problem. 

Eso es un desbordamiento de stack C, ya que dict_repr no (actualmente) usa Py_EnterRecursiveCall . ¡No puedes atrapar eso con un bloque except !

De vuelta a los errores más mundanos, podría tener un weakref.proxy para un objeto muerto:

 import weakref class Foo(object): pass str(weakref.proxy(Foo())) # ReferenceError: weakly-referenced object no longer exists 

O simplemente puedes escribir una clase que __repr__ lo que quieras en __str__ o __repr__ :

 class Foo(object): def __str__(self): raise Exception str(Foo()) # Raises an Exception 

Específicamente para Python 2, podría obtener un caso en el que al print un objeto se produzca una salida parcial y luego se genere una excepción, aunque aparentemente nunca debería haber llegado a la etapa de imprimir nada:

 class Foo(object): def __repr__(self): raise Exception print [Foo()] # Prints a single opening bracket, then raises an Exception! 

Esto se debe al obsoleto tp_print C-level hook.

También puede obtener un UnicodeEncodeError específico de Python 2 cuando se ejecuta un objeto unicode que contiene caracteres no ASCII, ya que los objetos str Python 2 son bytestrings:

 str(u'\u1000') # Fails on Python 2 only 

Solo para agregar a las otras respuestas, str() no lanza una excepción en muchos contextos (tal vez sorprendentes), por ejemplo, puede aplicarla a una función, un generador o a sí misma. Sin embargo, no puedo imaginar una situación en la que realmente quieras hacer esto en la práctica.

 >>> def a(): pass ... >>> str(a) '' >>> >>> str(x for x in range(3)) ' at 0x7fc434b95f10>' >>> >>> str(str()) '' >>>