BaseException.message deprecato in Python 2.6


177

Ricevo un avviso che BaseException.message è deprecato in Python 2.6 quando utilizzo la seguente eccezione definita dall'utente:

class MyException(Exception):

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

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

Questo è l'avvertimento:

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

Cosa c'è di sbagliato in questo? Cosa devo modificare per eliminare l'avviso di deprecazione?


Risposte:


154

Soluzione: quasi nessuna codifica necessaria

Basta ereditare la classe di eccezione Exceptione passare il messaggio come primo parametro al costruttore

Esempio:

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

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

Puoi usare str(my)o (meno elegante) my.args[0]per accedere al messaggio personalizzato.

sfondo

Nelle nuove versioni di Python (dalla 2.6) dovremmo ereditare le nostre classi di eccezione personalizzate da Exception che (a partire da Python 2.5 ) eredita da BaseException. Lo sfondo è descritto in dettaglio in 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__e __repr__sono già implementati in modo significativo, specialmente per il caso di un solo argomento (che può essere usato come messaggio).

Non è necessario ripetere __str__o __init__implementare o creare _get_messagecome suggerito da altri.


5
BaseException modificata in Eccezione come suggerito da @Matt
geekQ

8
Usare le strinterruzioni se l'eccezione è stata costruita con un argomento unicode: str(MyException(u'\xe5'))solleva UnicodeEncodeError. L'uso al unicodeposto di strnon è neanche infallibile perché unicode(MyException('\xe5'))genera UnicodeDecodeError. Questo significa che se non so in anticipo se l'argomento è stro unicode, devo usare .args[0]dove ho usato in precedenza .message?
Kasperd,

1
@kasperd Come praticamente tutti i problemi Unicode di Python, questo può essere risolto con un sandwich Unicode .
Ryan P,

3
@RyanP Questo presuppone che io abbia effettivamente il controllo su ciò che accade. Ecco un fatto della vita che ho affrontato. Devo gestire le eccezioni da più librerie di terze parti. Alcuni di questi passano unicode alle loro eccezioni e altri passano str. Una delle librerie ha persino una sua classe che eredita da Unicode ma ha il suo metodo repr, che restituisce Unicode piuttosto che str come richiesto dalla specifica.
Kasperd,

1
A proposito. sostituisci tutti gli utilizzi del .messagenostro progetto con .args[0]e ciò non ci ha causato alcun problema.
Kasperd,

26

Sì, è deprecato in Python 2.6 perché sta andando via in Python 3.0

La classe BaseException non fornisce più un modo per memorizzare i messaggi di errore. Dovrai implementarlo da solo. È possibile farlo con una sottoclasse che utilizza una proprietà per archiviare il messaggio.

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

Spero che questo ti aiuti


1
Come inizializzeresti il ​​messaggio durante il rilancio? Il suo codice mostrava che il messaggio veniva impostato chiamando MyException ("alcuni messaggi")
eric.frederich

I metodi nel mio esempio sono solo per l'implementazione della proprietà message. Il modo in cui viene utilizzata la proprietà dipende dal programmatore. In questo caso OP utilizza i metodi init e str che ha pubblicato nel suo codice.
Sahas,

1
Prendi in considerazione l'utilizzo di una variabile pubblica anziché di un getter / setter, se legge solo un'altra variabile. Puoi sempre aggiornarlo in seguito a una @propertysintassi quando hai davvero bisogno di incapsulamento.
vdboor

2
@vdboor: sta usando @propertyper disabilitare l'avviso di deprecazione.
bukzor,

È poco pratico e inutile creare proprietà che non fanno altro che ottenere e impostare il valore, deselezionato e invariato. L'intero punto delle proprietà è quello di consentire a uno di usare liberamente gli attributi pubblici, fino a quando non è effettivamente necessario un getter o un setter. Quindi si trasforma l'attributo pubblico in una proprietà, senza rompere alcun codice client.
Luciano Ramalho,

9
class MyException(Exception):

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

e = MyException('asdf')
print e

Questa è la tua classe in stile Python2.6. La nuova eccezione accetta un numero arbitrario di argomenti.


1
Anche la vecchia classe Exception accetta un numero qualsiasi di argomenti. Puoi evitare del tutto la proprietà del messaggio come quella che stai facendo, ma se ciò rompesse il tuo codice esistente, puoi risolvere il problema implementando la tua proprietà del messaggio.
Sahas,

8

Come replicare l'avviso

Permettetemi di chiarire il problema, poiché non è possibile replicarlo con il codice di esempio della domanda, questo replicherà l'avvertimento in Python 2.6 e 2.7, se sono attivi gli avvisi (tramite il -Wflag , la PYTHONWARNINGSvariabile di ambiente o il modulo di avvertimenti ):

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

Smetti di usare .message

Preferisco repr(error), che restituisce una stringa che contiene il nome del tipo di errore, la repr del messaggio, se presente, e la repr degli argomenti rimanenti.

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

Eliminare l'avviso mentre si sta ancora utilizzando .message

E il modo in cui ti sbarazzi di DeprecationWarningè sottoclassare un'eccezione incorporata come intendevano i progettisti di 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',)"

ottenere solo l' .messageattributo senzaerror.message

Se sai che c'era un argomento, un messaggio, all'eccezione ed è quello che vuoi, è preferibile evitare l'attributo del messaggio e prendere semplicemente strl'errore. Dì per una sottoclasse Exception:

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

E utilizzo:

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

Vedi anche questa risposta:

Modo corretto per dichiarare eccezioni personalizzate nel moderno Python?


4

Per quanto ne so, usare semplicemente un nome diverso per l'attributo message evita il conflitto con la classe base e quindi interrompe l'avvertimento di deprecazione:

class MyException(Exception):

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

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

Mi sembra un trucco.

Forse qualcuno può spiegare perché l'avviso viene emesso anche quando la sottoclasse definisce esplicitamente un attributo del messaggio. Se la classe base non ha più questo attributo, non dovrebbe esserci un problema.


4

Continuando dalla risposta di geekQ , la sostituzione del codice preferita dipende da cosa devi fare:

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

A volte le eccezioni hanno più di un argomento, quindi my.args[0]non è garantito fornire tutte le informazioni pertinenti.

Per esempio:

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

Stampa come output:

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)

Tuttavia è un compromesso sensibile al contesto, perché ad esempio:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected

1
Sì, non pensarci troppo. Usa str(my)invece di my.message.
Christian Long,

1

Il consiglio di usare str (myexception) porta a problemi di Unicode in Python 2.7, ad esempio:

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

funziona come previsto ed è preferito nei casi in cui parte del contenuto della stringa di errore includa l'input dell'utente


1

Il post di pzrq dice di usare:

str(e)

Questo era esattamente ciò di cui avevo bisogno.

(Se ti trovi in ​​un ambiente Unicode, sembra che:

unicode(e)

funzionerà e sembra funzionare bene in un ambiente non Unicode)

Pzrq ha detto molte altre cose buone, ma ho quasi perso la loro risposta a causa di tutte le cose buone. Dato che non ho 50 punti, non posso commentare la loro risposta per tentare di attirare l'attenzione sulla semplice soluzione che funziona, e poiché non ne ho 15 non posso votare quella risposta, ma posso postare (mi sento indietro, ma vabbè) - quindi eccomi qui - probabilmente perdo punti per quello ...

Dal momento che il mio punto è quello di attirare l'attenzione sulla risposta di PZRQ, per favore, non smalto e non perdere in tutto il seguito. le prime righe di questo post sono le più importanti.

La mia storia:

Il problema per cui sono venuto qui è se vuoi prendere un'eccezione da una classe su cui non hai alcun controllo - e allora ??? Sicuramente non ho intenzione di sottoclassare tutte le possibili classi utilizzate dal mio codice nel tentativo di riuscire a ottenere un messaggio da tutte le possibili eccezioni!

Stavo usando:

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

che, come ormai tutti sappiamo, dà l'avvertimento richiesto dall'OP (che mi ha portato qui), e questo, che pzrq offre come un modo per farlo:

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

no.

Non sono in un ambiente Unicode, ma la risposta di JJC mi ha fatto meravigliare, quindi ho dovuto provarlo. In questo contesto questo diventa:

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

che, con mia sorpresa, ha funzionato esattamente come str (e) - quindi ora è quello che sto usando.

Non so se 'str (e) / unicode (e)' sia la 'via Python approvata', e probabilmente scoprirò perché non va bene quando arrivo a 3.0, ma si spera che la capacità di gestire un eccezione imprevista (*) senza morire e ancora ottenere alcune informazioni da esso non andrà mai via ...

(*) Hmm. "eccezione inaspettata" - Penso di aver appena balbettato!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.