BaseException.message jest przestarzała w Pythonie 2.6

177

Otrzymuję ostrzeżenie, że BaseException.message jest przestarzała w Pythonie 2.6, gdy używam następującego wyjątku zdefiniowanego przez użytkownika:

class MyException(Exception):

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

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

To jest ostrzeżenie:

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

Co w tym złego? Co muszę zmienić, aby pozbyć się ostrzeżenia o wycofaniu?

opuszczony
źródło
9
Zobacz PEP 352 z przyczynami: python.org/dev/peps/pep-0352/#retracted-ideas
balpha

Odpowiedzi:

154

Rozwiązanie - prawie bez kodowania

Po prostu odziedzicz swoją klasę wyjątku od Exceptioni przekaż wiadomość jako pierwszy parametr do konstruktora

Przykład:

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

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

Możesz użyć str(my)lub (mniej eleganckie), my.args[0]aby uzyskać dostęp do wiadomości niestandardowej.

tło

W nowszych wersjach Pythona (od 2.6) mamy dziedziczyć nasze niestandardowe klasy wyjątków z Exception, które ( począwszy od Pythona 2.5 ) dziedziczy po BaseException. Tło zostało szczegółowo opisane w 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__i __repr__są już zaimplementowane w znaczący sposób, szczególnie w przypadku tylko jednego argumentu (który może być użyty jako komunikat).

Nie trzeba powtarzać __str__lub __init__realizacja lub utwórz _get_messagesugerowane przez innych.

geekQ
źródło
5
Zmieniono BaseException na Exception zgodnie z sugestią @Matt
geekQ,
8
Używanie strprzerw, jeśli wyjątek został skonstruowany z argumentem Unicode: str(MyException(u'\xe5'))raises UnicodeEncodeError. Używanie unicodezamiast tego również strnie jest niezawodne, ponieważ unicode(MyException('\xe5'))wywołuje UnicodeDecodeError. Czy to oznacza, że ​​jeśli nie wiem z góry, czy argument jest, strczy unicodemuszę użyć tego, .args[0]co wcześniej użyłem .message?
kasperd
1
@kasperd Podobnie jak praktycznie wszystkie problemy związane z Unicode w Pythonie, można to rozwiązać za pomocą kanapki Unicode .
Ryan P,
3
@RyanP Zakłada się, że faktycznie mam kontrolę nad tym, co się dzieje. Oto fakt z życia, z którym się spotkałem. Muszę obsługiwać wyjątki z wielu bibliotek innych firm. Niektóre z nich przekazują wyjątki unicode, a niektóre przekazują str. Jedna z bibliotek ma nawet własną klasę, która dziedziczy po Unicode, ale ma własną metodę repr, która zwraca kod Unicode zamiast str, zgodnie z wymaganiami specyfikacji.
kasperd
1
Tak przy okazji. zamień wszystkie użycie .messagew naszym projekcie na .args[0], a to nie sprawiło nam żadnych problemów.
kasperd
26

Tak, jest przestarzały w Pythonie 2.6, ponieważ odchodzi w Pythonie 3.0

Klasa BaseException nie zapewnia już sposobu przechowywania komunikatów o błędach. Będziesz musiał sam to wdrożyć. Możesz to zrobić z podklasą, która używa właściwości do przechowywania wiadomości.

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

Mam nadzieję że to pomoże

Sahas
źródło
1
Jak zainicjowałbyś wiadomość podczas podbicia? Jego kod wskazywał na ustawianie wiadomości przez wywołanie MyException („jakaś wiadomość”)
eric.frederich
Metody w moim przykładzie służą tylko do implementacji właściwości wiadomości. Sposób wykorzystania właściwości zależy od kodera. W tym przypadku OP używa metod init i str , które zamieścił w swoim kodzie.
Sahas
1
Rozważ użycie zmiennej publicznej zamiast metody pobierającej / ustawiającej, jeśli czyta ona tylko inną zmienną. Zawsze możesz zaktualizować to później do @propertyskładni, gdy naprawdę potrzebujesz hermetyzacji.
vdboor
2
@vdboor: używa, @propertyaby wyłączyć ostrzeżenie o wycofaniu.
bukzor
Tworzenie właściwości, które nie robią nic oprócz pobierania i ustawiania wartości, niezaznaczonych i niezmienionych, jest niepytoniczne i bezużyteczne. Cały sens właściwości polega na umożliwieniu swobodnego używania atrybutów publicznych, do czasu, gdy faktycznie potrzebny jest getter lub setter. Następnie zamieniasz atrybut public na właściwość, bez łamania kodu klienta.
Luciano Ramalho
9
class MyException(Exception):

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

e = MyException('asdf')
print e

To jest Twoja klasa w stylu Python2.6. Nowy wyjątek przyjmuje dowolną liczbę argumentów.

Maxim Sloyko
źródło
1
Stara klasa Exception również przyjmuje dowolną liczbę argumentów. Możesz całkowicie uniknąć właściwości wiadomości, takiej jak to, co robisz, ale jeśli spowodowałoby to uszkodzenie istniejącego kodu, możesz rozwiązać problem, implementując własną właściwość wiadomości.
Sahas
8

Jak powtórzyć ostrzeżenie

Pozwól, że wyjaśnię problem, ponieważ nie można tego powtórzyć za pomocą przykładowego kodu pytania, spowoduje to powtórzenie ostrzeżenia w Pythonie 2.6 i 2.7, jeśli masz włączone ostrzeżenia (za pomocą -Wflagi , PYTHONWARNINGSzmiennej środowiskowej lub modułu ostrzeżeń ):

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

Przestań używać .message

Wolę repr(error), która zwraca ciąg zawierający nazwę typu błędu, repr komunikatu, jeśli taki istnieje, oraz repr pozostałych argumentów.

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

Eliminacja ostrzeżenia podczas dalszego używania .message

A sposobem pozbycia się tego DeprecationWarningjest utworzenie podklasy wbudowanego wyjątku zgodnie z zamierzeniami projektantów Pythona:

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',)"

uzyskanie tylko .messageatrybutu bezerror.message

Jeśli wiesz, że do wyjątku był jeden argument, komunikat, i właśnie tego chcesz, lepiej jest unikać atrybutu komunikatu i po prostu wziąć strbłąd. Powiedz o podklasie Exception:

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

I użycie:

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

Zobacz także tę odpowiedź:

Właściwy sposób na deklarowanie niestandardowych wyjątków we współczesnym Pythonie?

Aaron Hall
źródło
4

O ile wiem, po prostu użycie innej nazwy dla atrybutu wiadomości pozwala uniknąć konfliktu z klasą bazową, a tym samym zatrzymuje ostrzeżenie o wycofaniu:

class MyException(Exception):

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

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

Wydaje mi się, że to hack.

Może ktoś może wyjaśnić, dlaczego ostrzeżenie jest wyświetlane, nawet jeśli podklasa wyraźnie definiuje atrybut komunikatu. Jeśli klasa bazowa nie ma już tego atrybutu, nie powinno być problemu.

Hollister
źródło
4

Kontynuując od odpowiedzi geekQ , preferowana zamiana kodu zależy od tego, co musisz zrobić:

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

Czasami wyjątki mają więcej niż jeden argument, więc my.args[0]nie gwarantuje się dostarczenia wszystkich istotnych informacji.

Na przykład:

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

Drukuje jako wyjście:

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)

Jest to jednak kompromis zależny od kontekstu, ponieważ na przykład:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected
pzrq
źródło
1
Tak, nie myśl za dużo. Użyj str(my)zamiast my.message.
Christian Long,
1

Porada dotycząca używania str (myexception) prowadzi do problemów z Unicode w Pythonie 2.7, np .:

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

działa zgodnie z oczekiwaniami i jest preferowane w przypadkach, gdy część zawartości ciągu błędu zawiera dane wejściowe użytkownika

jjc
źródło
1

Post pzrq mówi, aby użyć:

str(e)

To było dokładnie to, czego potrzebowałem.

(Jeśli pracujesz w środowisku Unicode, wygląda na to, że:

unicode(e)

będzie działać i wygląda na to, że działa dobrze w środowisku innym niż Unicode)

Pzrq powiedział wiele innych dobrych rzeczy, ale prawie przegapiłem ich odpowiedź z powodu wszystkich dobrych rzeczy. Ponieważ nie mam 50 punktów, nie mogę komentować ich odpowiedzi, aby spróbować zwrócić uwagę na proste rozwiązanie, które działa, a ponieważ nie mam 15, nie mogę zagłosować na tę odpowiedź, ale mogę publikować (wydaje się być zacofany, ale no cóż) - tak oto piszę - pewnie stracę za to punkty ...

Ponieważ moim celem jest zwrócenie uwagi na odpowiedź pzrq, nie glazuruj i nie przegap tego we wszystkich poniższych. kilka pierwszych linijek tego posta jest najważniejszych.

Moja historia:

Problem, dla którego tu przyszedłem, polegał na tym, że jeśli chcesz złapać wyjątek z zajęć, nad którymi nie masz kontroli - co wtedy ??? Z pewnością nie zamierzam tworzyć podklas wszystkich możliwych klas, których używa mój kod, aby móc uzyskać komunikat ze wszystkich możliwych wyjątków!

Używałem:

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

który, jak wszyscy teraz wiemy, daje ostrzeżenie OP, o które pytano (które mnie tu przywiodło), a to, co podaje pzrq jako sposób na zrobienie tego:

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

nie.

Nie jestem w środowisku Unicode, ale odpowiedź jjc sprawiła, że ​​zacząłem się zastanawiać, więc musiałem spróbować. W tym kontekście staje się to:

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

który, ku mojemu zaskoczeniu, działał dokładnie tak, jak str (e) - więc teraz tego używam.

Nie wiem, czy „str (e) / unicode (e)” jest „zatwierdzoną metodą Pythona” i prawdopodobnie dowiem się, dlaczego to nie jest dobre, kiedy dojdę do 3.0, ale można mieć nadzieję, że umiejętność obsługi nieoczekiwany wyjątek (*) bez umierania, a mimo to otrzymanie z niego pewnych informacji nigdy nie zniknie ...

(*) Hmm. „nieoczekiwany wyjątek” - wydaje mi się, że się jąkałem!

rustycar
źródło