BaseException.message устарело в Python 2.6

177

Я получаю предупреждение о том, что BaseException.message устарело в Python 2.6, когда я использую следующее пользовательское исключение:

class MyException(Exception):

    def __init__(self, message):
        self.message = message

    def __str__(self):
        return repr(self.message)

Это предупреждение:

DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
self.message = message

Что с этим не так? Что я должен изменить, чтобы избавиться от предупреждения об устаревании?

desolat
источник
9
См. PEP 352 по следующим причинам: python.org/dev/peps/pep-0352/#retracted-ideas
balpha

Ответы:

154

Решение - почти не требуется кодирование

Просто наследуйте ваш класс исключений от Exceptionи передайте сообщение в качестве первого параметра конструктору

Пример:

class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    print my # outputs 'my detailed description'

Вы можете использовать str(my)или (менее элегантно) my.args[0]для доступа к пользовательскому сообщению.

Задний план

В более новых версиях Python (начиная с 2.6) мы должны наследовать наши пользовательские классы исключений от Exception, которое ( начиная с Python 2.5 ) наследует от BaseException. Фон подробно описан в PEP 352 .

class BaseException(object):

    """Superclass representing the base of the exception hierarchy.
    Provides an 'args' attribute that contains all arguments passed
    to the constructor.  Suggested practice, though, is that only a
    single string argument be passed to the constructor."""

__str__и __repr__уже реализованы осмысленным образом, особенно в случае только одного аргумента (который может использоваться в качестве сообщения).

Вам не нужно повторять __str__или __init__реализацию или создание _get_messageв соответствии с предложением других.

geekQ
источник
5
Изменено BaseException на Exception, как предложено @Matt
geekQ
8
Использование strразрывов, если исключение было создано с аргументом Юникод: str(MyException(u'\xe5'))поднимается UnicodeEncodeError. Использование unicodeвместо strне является unicode(MyException('\xe5'))надежным, потому что вызывает UnicodeDecodeError. Означает ли это, что если я не знаю заранее, является ли аргумент strили unicode, я должен использовать, .args[0]где я ранее использовал .message?
Касперд
1
@kasperd Как и практически все проблемы Unicode в Python, это можно решить с помощью бутерброда Unicode .
Райан П
3
@RyanP Это предполагает, что я на самом деле имею контроль над тем, что происходит. Вот факт жизни, с которым я столкнулся. Я должен обрабатывать исключения из нескольких сторонних библиотек. Некоторые из них передают Unicode в свои исключения, а некоторые проходят ул. Одна из библиотек даже имеет свой собственный класс, который наследует от Unicode, но имеет свой собственный метод repr, который возвращает Unicode, а не str, как того требует спецификация.
Касперд
1
Я сделал, кстати. заменить все использование .messageв нашем проекте .args[0], и это не вызвало у нас никаких проблем.
Касперд
26

Да, это устарело в Python 2.6, потому что оно исчезает в Python 3.0

Класс BaseException больше не позволяет хранить сообщения об ошибках. Вам придется реализовать это самостоятельно. Вы можете сделать это с помощью подкласса, который использует свойство для хранения сообщения.

class MyException(Exception):
    def _get_message(self): 
        return self._message
    def _set_message(self, message): 
        self._message = message
    message = property(_get_message, _set_message)

Надеюсь это поможет

Sahas
источник
1
Как бы вы инициализировали сообщение во время рейза? Его код показывает, что сообщение устанавливается, вызывая MyException («некоторое сообщение»)
eric.frederich
Методы в моем примере предназначены только для реализации свойства сообщения. Как это свойство используется, зависит от кодера. В этом случае OP использует методы init и str, которые он разместил в своем коде.
Сахас
1
Попробуйте использовать открытую переменную вместо метода получения / установки, если она только читает другую переменную. Вы всегда можете обновить его до @propertyсинтаксиса, когда вам действительно нужна инкапсуляция.
vdboor
2
@vdboor: он использует, @propertyчтобы отключить предупреждение об устаревании.
Букзор
Это бессмысленно и бесполезно создавать свойства, которые ничего не делают, кроме получения и установки значения, без проверки и без изменений. Весь смысл свойств состоит в том, чтобы позволить свободно использовать общедоступные атрибуты до тех пор, пока на самом деле не понадобится метод получения или установки. Затем вы превращаете атрибут public в свойство, не нарушая код клиента.
Лучано Рамальо
9
class MyException(Exception):

    def __str__(self):
        return repr(self.args[0])

e = MyException('asdf')
print e

Это ваш класс в стиле Python2.6. Новое исключение принимает произвольное количество аргументов.

Максим Слойко
источник
1
Старый класс Exception также принимает любое количество аргументов. Вы можете полностью отказаться от свойства сообщения, например, от того, что вы делаете, но если это нарушит существующий код, вы можете решить эту проблему, внедрив собственное свойство сообщения.
Сахас
8

Как повторить предупреждение

Позвольте мне прояснить проблему, так как никто не может повторить это с примером кода вопроса, это будет повторять предупреждение в Python 2.6 и 2.7, если у вас включены предупреждения (через -Wфлаг , PYTHONWARNINGSпеременную среды или модуль предупреждений ):

>>> error = Exception('foobarbaz')
>>> error.message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foobarbaz'

Прекратить использование .message

Я предпочитаю repr(error), которая возвращает строку, содержащую имя типа ошибки, repr сообщения, если оно есть, и repr оставшихся аргументов.

>>> repr(error)
"Exception('foobarbaz',)"

Устранение предупреждения при использовании .message

И способ, которым вы избавляетесь от этого, DeprecationWarningсостоит в том, чтобы создать подкласс встроенного исключения, как задумывалось дизайнерами Python:

class MyException(Exception):

    def __init__(self, message, *args):
        self.message = message
        # delegate the rest of initialization to parent
        super(MyException, self).__init__(message, *args)

>>> myexception = MyException('my message')
>>> myexception.message
'my message'
>>> str(myexception)
'my message'
>>> repr(myexception)
"MyException('my message',)"

получить только .messageатрибут безerror.message

Если вы знаете, что для Исключения был один аргумент, сообщение, и это то, что вам нужно, предпочтительно избегать атрибута сообщения и просто принимать сообщение strоб ошибке. Скажи для подкласса Exception:

class MyException(Exception):
    '''demo straight subclass'''

И использование:

>>> myexception = MyException('my message')
>>> str(myexception)
'my message'

Смотрите также этот ответ:

Правильный способ объявить пользовательские исключения в современном Python?

Аарон Холл
источник
4

Насколько я могу судить, простое использование другого имени для атрибута сообщения позволяет избежать конфликта с базовым классом и, таким образом, останавливает предупреждение об устаревании:

class MyException(Exception):

def __init__(self, message):
    self.msg = message

def __str__(self):
    return repr(self.msg)

Похоже, взломать для меня.

Может быть, кто-то может объяснить, почему выдается предупреждение, даже если подкласс явно определяет атрибут сообщения. Если базовый класс больше не имеет этого атрибута, проблем не должно быть.

Hollister
источник
4

Продолжая от ответа geekQ , предпочтительная замена кода зависит от того, что вам нужно сделать:

### Problem
class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    ### Solution 1, fails in Python 2.x if MyException contains 🔥
    # with UnicodeEncodeError: 'ascii' codec can't encode characters in position 24-25: ordinal not in range(128)
    print(my)  # outputs 'my detailed description'

### Solution 2
# Works in Python 2.x if exception only has ASCII characters,
# should always work in Python 3.x
str(my)

### Solution 3
# Required in Python 2.x if you need to handle non-ASCII characters,
# such as δσφφδσ (as pointed out by jjc) or emoji 🔥 💕 🎁 💯 🌹
# but does not work in Python 3.x
unicode(my)

Иногда исключения имеют более одного аргумента, поэтому my.args[0]не гарантируется предоставление всей необходимой информации.

Например:

# Python 2.7
try:
    u'\u12345'.encode('utf-8').encode('utf-8')
except UnicodeDecodeError as e:
    print e.args[0]
    print e.args
    print str(e)

Печать как вывод:

ascii
('ascii', '\xe1\x88\xb45', 0, 1, 'ordinal not in range(128)')
'ascii' codec can't decode byte 0xe1 in position 0: ordinal not in range(128)

Однако это контекстно-зависимый компромисс, потому что, например:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected
pzrq
источник
1
Да, не думай об этом. Используйте str(my)вместо my.message.
Кристиан Лонг
1

Совет использовать str (myexception) приводит к проблемам с юникодом в python 2.7, например:

str(Exception(u'δσφφδσ'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

:(

unicode(Exception(u'δσφφδσ')) 

работает должным образом и является предпочтительным в тех случаях, когда часть содержимого строки ошибки включает в себя пользовательский ввод

JJC
источник
1

пост pzrq говорит, что использовать:

str(e)

Это было именно то, что мне было нужно.

(Если вы находитесь в среде Unicode, кажется, что:

unicode(e)

будет работать, и, кажется, работает нормально в среде не-Unicode)

Пзрк сказал много других хороших вещей, но я почти пропустил их ответ из-за всех хороших вещей. Поскольку у меня нет 50 баллов, я не могу прокомментировать их ответ, чтобы попытаться привлечь внимание к простому работающему решению, и, поскольку у меня нет 15 баллов, я не могу проголосовать за этот ответ, но я могу опубликовать (чувствует себя отсталым, но да ладно) - так вот я и выкладываю - наверно теряю очки за это ...

Так как моя цель - привлечь внимание к ответу pzrq, пожалуйста, не застекляйте и не пропустите его во всем нижеприведенном. первые несколько строк этого поста являются наиболее важными.

Моя история:

Проблема, ради которой я пришел сюда, заключалась в том, что если вы хотите поймать исключение из класса, который вы не можете контролировать - что тогда ??? Я, конечно, не собираюсь создавать подклассы для всех возможных классов, которые использует мой код, чтобы попытаться получить сообщение из всех возможных исключений!

Я использовал:

except Exception as e:
   print '%s (%s)' % (e.message,type(e))

который, как мы все теперь знаем, дает предупреждение, о котором спрашивал OP (что привело меня сюда), и это, которое pzrq дает как способ сделать это:

except Exception as e:
   print '%s (%s)' % (str(e),type(e))

не.

Я не нахожусь в среде Юникода, но ответ JJC заставил меня задуматься, поэтому я должен был попробовать. В этом контексте это становится:

except Exception as e:
   print '%s (%s)' % (unicode(e),type(e))

который, к моему удивлению, работал точно так же, как str (e) - так что теперь это то, что я использую.

Не знаю, является ли 'str (e) / unicode (e)' 'одобренным способом Python', и я, вероятно, выясню, почему это не очень хорошо, когда я доберусь до 3.0, но можно надеяться, что способность обрабатывать Неожиданное исключение (*) без смерти и получения информации от него никогда не исчезнет ...

(*) Хм. «неожиданное исключение» - думаю, я просто заикался!

rustycar
источник