В Python 2.6 застарілий BaseException.message


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

Що з цим? Що потрібно змінити, щоб позбутися попередження про депресію?


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як пропонують інші.


5
Змінено BaseException на Виняток, як запропонував @Matt
geekQ

8
Використання strперерв, якщо виняток був побудований з аргументом unicode: str(MyException(u'\xe5'))підвищує UnicodeEncodeError. Використання unicodeзамість strне unicode(MyException('\xe5'))дурно захищене, оскільки підвищує UnicodeDecodeError. Чи означає це, що якщо я не знаю заздалегідь, чи є аргументом, strабо unicodeя повинен використовувати .args[0]там, де раніше я використовував .message?
kasperd

1
@kasperd Як і практично всі проблеми з унікодом Python, це можна вирішити за допомогою сендвіча з унікодом .
Райан П

3
@RyanP Це передбачає, що я фактично маю контроль над тим, що відбувається. Ось факт життя, з яким я стикався. Мені доводиться обробляти винятки з кількох сторонніх бібліотек. Деякі з них передають unicode за винятками, а деякі передають str. В одній з бібліотек навіть є власний клас, який успадковується від unicode, але є власний метод repr, який повертає unicode, а не str, як вимагає специфікація.
kasperd

1
Я зробив btw. замініть все використання .messageв нашому проекті на .args[0], і це не спричинило для нас жодних проблем.
kasperd

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)

Сподіваюся, це допомагає


1
Як би ви ініціалізували повідомлення під час підвищення? Його код показав, що повідомлення встановлюється, зателефонувавши на MyException ("якесь повідомлення")
eric.frederich

Методи в моєму прикладі лише для реалізації властивості повідомлення. Як використовується властивість, - це кодер. У цьому випадку ОП використовує методи init і str, які він розмістив у своєму коді.
Сахас

1
Подумайте про використання загальнодоступної змінної замість getter / setter, якщо вона зчитує лише іншу змінну. Ви завжди можете оновити це пізніше до @propertyсинтаксису, коли вам дійсно потрібна інкапсуляція.
vdboor

2
@vdboor: він використовує @propertyдля відключення попередження про депресію .
bukzor

Непітонічно і марно створювати властивості, які нічого не роблять, окрім отримання та встановлення значення, неперевіреного та незмінного. Вся суть властивостей полягає в тому, щоб дозволити вільно використовувати публічні атрибути до тих пір, поки геттер або сеттер фактично не знадобляться. Потім ви перетворите публічний атрибут у властивість, не порушуючи жодного коду клієнта.
Лучано Рамальо

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(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)

Мені це здається хаком.

Можливо, хтось може пояснити, чому попередження видається навіть тоді, коли підклас чітко визначає атрибут повідомлення. Якщо базовий клас більше не має цього атрибута, не повинно виникнути проблем.


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

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'δσφφδσ')) 

працює, як очікувалося, і є кращим у тих випадках, коли частина вмісту рядка помилок включає введення користувача


1

Допис pzrq говорить про використання:

str(e)

Це саме те, що мені було потрібно.

(Якщо ви перебуваєте в середовищі unicode, виявляється, що:

unicode(e)

буде працювати, і, здається, він буде добре працювати в не-кодовому середовищі)

Pzrq сказав багато інших добрих речей, але я майже не пропустив їх відповідь через усі хороші речі. Оскільки у мене немає 50 балів, я не можу коментувати їх відповідь, щоб спробувати звернути увагу на просте рішення, яке працює, і оскільки у мене немає 15, я не можу проголосувати за цю відповідь, але можу розмістити повідомлення (відчуває себе відсталим, але ну добре) - так ось я розміщую повідомлення - ймовірно, втрачаю за це бали ...

Оскільки моя думка полягає в тому, щоб звернути увагу на відповідь pzrq, будь ласка, не затуляйте погляд і не пропустіть його у всьому нижче. перші кілька рядків цієї публікації є найважливішими.

Моя історія:

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

Я використовував:

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

яка, як ми всі зараз знаємо, дає попередження, про яке запитували ОП (що мене тут привело), ​​і це, що pzrq дає як спосіб це зробити:

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

не.

Я не в середовищі unicode, але відповідь jjc змусила мене замислитися, тому мені довелося спробувати. У цьому контексті це стає:

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

який, на мій подив, працював так само, як str (e) - тож ось що я використовую.

Не знаю, чи "str (e) / unicode (e)" є "схваленим способом Python", і я, мабуть, дізнаюся, чому це не добре, коли я перейду до 3.0, але можна сподіватися, що здатність обробляти несподіваний виняток (*), не вмираючи, і все-таки отримати інформацію від нього ніколи не піде ...

(*) Гм. "несподіваний виняток" - я думаю, що я просто заїкався!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.