Может ли преобразование в строку вызвать ошибку?
Мне было интересно, может ли преобразование в строку i.e. str(sth)
вызвать исключение, например, например, float(sth)
?
Я прошу об этом знать, нужно ли вставлять мой код в:
try:
x = str(value)
except ValueError:
x = None
чтобы убедиться, что выполнение не прекращается из-за отказа преобразования.
Также отличается ли это от Python 2 и 3, так как класс str
отличается от них?
Ответы
Ответ 1
Если вы столкнулись с пользовательским классом, который явно вызывает исключение в __str__
(или __repr__
, если __str__
не определен). Или, например, класс, который возвращает объект bytes
из __str__
:
class Foo:
def __str__(self):
return b''
str(Foo()) # TypeError: __str__ returned non-string (type bytes)
Но лично я никогда этого не видел, и я уверен, что никто не имеет; было бы глупо это делать. Точно так же глупая ошибка при реализации __str__
или краевых случаев может создать еще один Exception
. Интересно посмотреть, как вы могли бы подталкивать Python к этим крайним случаям (смотрите @user2357112 здесь).
Кроме этого, никакие встроенные функции вообще не генерируют исключение в этом случае, поскольку он определен для всех из них в Py2
и Py3
.
Для пользовательских классов str
будет использовать object.__str__
по умолчанию, если он не определен в Python 3, а в Python 2 использовать его, если класс является новым классом стиля (наследуется от object
).
Если класс является старым классом стиля, я считаю, что для экземпляров используется classobj.__str__
для классов и instance.__str__
.
В общем, я бы этого не заметил, для этого особых случаев недостаточно.
Ответ 2
В теории да, это возможно, но на практике это почти наверняка не будет. Способ работы str
- это вызов функции __str__
объекта, который он преобразует в строку. Встроенные типы типа List
, числовые типы и другие будут возвращать значения о том, что вы ожидаете, [x,y,z]
для List
, число как строка для числового типа и т.д. object
имеет __str__
, который дает результат типа <generator object <genexpr> at 0x7fdb2fa6a640>
, который часто не очень полезен, но не вызывает исключения.
Таким образом, вызов str
для любого встроенного устройства почти наверняка не приведет к возникновению исключения (по общему признанию, я не могу найти гарантии для этого в документации на Python, но я не могу представить ситуацию, в которой это произойдет )
Тем не менее, можно переопределить метод __str__
для настраиваемого класса. Если это будет сделано, то этот метод может вызвать исключение, как и любая другая функция, которую программист мог бы написать, - это преднамеренное исключение, неотображаемое IndexError
или тому подобное и т.д. Даже если вы не переопределяете __str__
, если вы используете модуль, который делает это, вполне возможно, что автор этого модуля допустил некоторую ошибку на каком-то редком крае, которое вызовет исключение.
Нижняя строка: Это не должно, если все выполняли свою работу правильно, но это могло бы быть. Почти в любой практической ситуации я бы не стал пытаться поймать.
Ответ 3
Легко. У вас может быть класс контейнера с рекурсивно-небезопасным __str__
или __repr__
, который заканчивается опорным циклом:
import numpy
x = numpy.empty([1], dtype=object)
x[0] = x
str(x) # RuntimeError: maximum recursion depth exceeded
Или просто очень сильно вложенный объект:
x = []
for i in xrange(2000):
x = [x]
str(x) # RuntimeError: maximum recursion depth exceeded while getting the repr of a list
Если даже с помощью встроенных типов вы можете получить что-то намного хуже, чем исключение:
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.
Это переполнение стека C, поскольку dict_repr
не использует (в настоящее время) Py_EnterRecursiveCall
. Вы не можете поймать это с помощью блока except
!
Возвращаясь к более мирским ошибкам, вы можете иметь weakref.proxy
для мертвого объекта:
import weakref
class Foo(object):
pass
str(weakref.proxy(Foo())) # ReferenceError: weakly-referenced object no longer exists
Или вы могли бы просто написать класс, который поднимает все, что вам нужно, в __str__
или __repr__
:
class Foo(object):
def __str__(self):
raise Exception
str(Foo()) # Raises an Exception
Конкретно для Python 2 вы можете получить случай, когда print
объект создает частичный вывод, а затем вызывает исключение, хотя он якобы никогда не должен был доходить до стадии печати:
class Foo(object):
def __repr__(self):
raise Exception
print [Foo()] # Prints a single opening bracket, then raises an Exception!
Это связано с устаревшим tp_print
крюком уровня C.
Вы также можете получить Python 2-специфичный UnicodeEncodeError
, когда str
-ing a unicode
объект, который содержит символы, отличные от ASCII, поскольку объекты Python 2 str
являются байтами:
str(u'\u1000') # Fails on Python 2 only
Ответ 4
Чтобы добавить к другим ответам, str()
не генерирует исключение во многих (возможно, неожиданных) контекстах, например, вы можете применить его к функции, генератору или самому себе. Однако я не могу представить ситуацию, в которой вы действительно хотели бы сделать это на практике.
>>> def a(): pass
...
>>> str(a)
'<function a at 0x7fc43614e048>'
>>>
>>> str(x for x in range(3))
'<generator object <genexpr> at 0x7fc434b95f10>'
>>>
>>> str(str())
''
>>>