30

I was wondering if converting to string i.e. str(sth) can raise an exception like for example float(sth) does? I am asking this to know if it is necessary to wrap my code in:

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

to be sure that execution does not stop because of conversion failure.

Also does this differ in Python 2 and 3, since the str class is different in them??

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
S.Mohsen sh
  • 2,028
  • 3
  • 21
  • 32
  • Every part of you code that does 'something' can possibly go wrong what will lead to an error. ;-) But since you can't put everything in an `try except` I would also look how big is the chance that something can go wrong. If the canhce is big, put in in their own `try except` block. If chance is lower, then you might be better of putting it in a `try except` block with multiple code lines. – Tenzin Aug 16 '16 at 12:37
  • 4
    @Tenzin: It's not just a matter of how likely the exception is, but of what the correct behavior is when there's an exception. The OP isn't asking vaguely "hey, can something go wrong?"; rather, (s)he's asking specifically if string conversion has the concept of "this value can't be converted to a string" (`ValueError`), because if so, (s)he has a reasonable way to handle that in his/her case. – ruakh Aug 16 '16 at 18:26
  • 1
    Highly nested data structure may trigger an exception due to recursion limit... – Bakuriu Aug 16 '16 at 20:51

4 Answers4

28

If you encounter a custom class that explicitly raises an exception in __str__ (or __repr__ if __str__ is not defined). Or, for example a class that returns a bytes object from __str__:

class Foo:
    def __str__(self):
        return b''

str(Foo()) # TypeError: __str__ returned non-string (type bytes)

But personally, I have never seen this and I'm pretty sure no one has; it would be daft to do it. Likewise, a silly mistake in the implementation of __str__ or edge cases might create another Exception. It is interesting to see how you could push Python to these edge cases (look at @user2357112 answer here).

Other than that case, no built-ins generally raise an exception in this case since it is defined for all of them in Py2 and Py3.

For user defined classes str will use object.__str__ by default if not defined in Python 3 and, in Python 2, use it if a class is a new style class (inherits from object).

If a class is an old style class I believe it is classobj.__str__ that is used for classes and instance.__str__ for instances.

In general, I would not catch this, special cases aren't special enough for this.

Community
  • 1
  • 1
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
19

In theory yes, it can, but in practice it almost certainly won't. The way that the str function works is it calls the __str__ function of the object that it's converting to a string. Built in types like List, numeric types, and others will return values about what you would expect, [x,y,z] for a List, the number as a string for a numerical type, etc. object has a __str__ method that gives an output like <generator object <genexpr> at 0x7fdb2fa6a640>, which often isn't very useful but won't raise an exception.

So, calling str on any builtin is almost certainly not going to raise an exception (Admittedly, I can't find a guarantee for this in the Python documentation, but I can't imagine a situation where it would happen)

That said, it is possible to override the __str__ method for a custom class. If this is done, then that method could raise an exception just like any other function that a programmer could write could - an intentional exception, an uncaught IndexError or the like, etc. Even if you're not overriding __str__, if you're using a module that does, it's entirely possible the author of that module made some mistake on some rare edge that will raise an exception.

The bottom line: It shouldn't if everyone's done their job right, but it could. In almost any practical situation I wouldn't bother try/catching.

Community
  • 1
  • 1
James
  • 2,843
  • 1
  • 14
  • 24
  • 3
    What if you run out of memory? That could realistically happen on long lists. – MSalters Aug 16 '16 at 14:46
  • 4
    That's a good point! Yes, a `MemoryError` could occur during the `str` method, along with a `KeyboardInterrupt`, and probably a few more I'm forgetting. If someone reasonably expects either of those could happen, it may be good to catch them ([Though it's sometimes challenging to catch MemoryErrors](http://stackoverflow.com/questions/19899910/python-not-catching-memoryerror)) – James Aug 16 '16 at 14:57
19

Easily. You could have a container class with a recursion-unsafe __str__ or __repr__ that ends up in a reference loop:

import numpy

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

Or just a really highly nested object:

x = []
for i in xrange(2000):
    x = [x]

str(x)  # RuntimeError: maximum recursion depth exceeded while getting the repr of a list

Heck, even with just built-in types, you could get something a lot worse than an exception:

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.

That's a C stack overflow, since dict_repr doesn't (currently) use Py_EnterRecursiveCall. You can't catch that with an except block!

Back to the more mundane errors, you could have a weakref.proxy to a dead object:

import weakref

class Foo(object):
    pass

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

Or you could just write a class that raises whatever you want in __str__ or __repr__:

class Foo(object):
    def __str__(self):
        raise Exception

str(Foo())  # Raises an Exception

Specific to Python 2, you could get a case where printing an object produces partial output and then raises an exception, even though it ostensibly should have never reached the stage of printing anything:

class Foo(object):
    def __repr__(self):
        raise Exception

print [Foo()]  # Prints a single opening bracket, then raises an Exception!

This is due to the obsolete tp_print C-level hook.

You could also get a Python 2-specific UnicodeEncodeError when str-ing a unicode object that contains non-ASCII characters, since Python 2 str objects are bytestrings:

str(u'\u1000')  # Fails on Python 2 only
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Is there a reason why you use python2 in your examples? – Chris_Rands Aug 16 '16 at 19:46
  • 1
    @Chris_Rands: Because I was thinking about adding the Python 2-specific case of `str`-ing a `unicode` object with non-ASCII characters in it, and I forgot to match my examples to the Python 3.x tag. – user2357112 Aug 16 '16 at 19:48
10

Just to add to the other answers, str() does not throw an exception in many (perhaps surprising) contexts, for example you can apply it to a function, a generator, or itself. However, I can't imagine a situation where you would actually want to do this in practice.

>>> def a(): pass
... 
>>> str(a)
'<function a at 0x7fc43614e048>'
>>> 
>>> str(x for x in range(3))
'<generator object <genexpr> at 0x7fc434b95f10>'
>>> 
>>> str(str())
''
>>> 
Chris_Rands
  • 38,994
  • 14
  • 83
  • 119