BaseException.message ditinggalkan dalam Python 2.6

177

Saya mendapatkan peringatan bahwa BaseException.message sudah usang dalam Python 2.6 ketika saya menggunakan pengecualian yang ditentukan pengguna berikut:

class MyException(Exception):

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

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

Ini peringatannya:

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

Ada apa dengan ini? Apa yang harus saya ubah untuk menyingkirkan peringatan penghinaan?

sunyi sepi
sumber
9
Lihat PEP 352 untuk alasan: python.org/dev/peps/pep-0352/#retracted-ideas
balpha

Jawaban:

154

Solusi - hampir tidak diperlukan pengkodean

Cukup mewarisi kelas pengecualian Anda dari Exceptiondan meneruskan pesan sebagai parameter pertama ke konstruktor

Contoh:

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

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

Anda dapat menggunakan str(my)atau (kurang elegan) my.args[0]untuk mengakses pesan khusus.

Latar Belakang

Dalam versi Python yang lebih baru (dari 2.6) kita seharusnya mewarisi kelas pengecualian khusus kami dari Exception yang ( mulai dari Python 2.5 ) mewarisi dari BaseException. Latar belakang dijelaskan secara rinci dalam 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__dan __repr__sudah diimplementasikan dengan cara yang bermakna, terutama untuk kasus hanya satu arg (yang dapat digunakan sebagai pesan).

Anda tidak perlu mengulang __str__atau __init__mengimplementasikan atau membuat _get_messageseperti yang disarankan oleh orang lain.

geekQ
sumber
5
Mengubah BaseException ke Pengecualian seperti yang disarankan oleh @Matt
geekQ
8
Menggunakan strjeda jika pengecualian dibangun dengan argumen unicode: str(MyException(u'\xe5'))raises UnicodeEncodeError. Menggunakan unicodebukannya strtidak mudah karena unicode(MyException('\xe5'))meningkatkan UnicodeDecodeError. Apakah ini berarti bahwa jika saya tidak tahu sebelumnya apakah argumennya adalah stratau unicode, saya harus menggunakan .args[0]tempat yang sebelumnya saya gunakan .message?
kasperd
1
@kasperd Seperti hampir semua masalah unicode Python, ini bisa diselesaikan dengan sandwich unicode .
Ryan P
3
@RyanP Itu mengasumsikan saya benar-benar memiliki kendali atas apa yang masuk. Inilah fakta kehidupan yang saya hadapi. Saya harus menangani pengecualian dari beberapa perpustakaan pihak ketiga. Beberapa dari mereka lolos unicode ke pengecualian mereka dan beberapa lainnya lulus str. Salah satu perpustakaan bahkan memiliki kelas sendiri yang mewarisi dari unicode tetapi memiliki metode repr sendiri, yang mengembalikan unicode daripada str seperti yang dipersyaratkan oleh spec.
kasperd
1
Saya melakukan btw. ganti semua penggunaan .messagedalam proyek kami dengan .args[0], dan itu tidak menyebabkan masalah bagi kami.
kasperd
26

Ya, itu sudah tidak digunakan lagi di Python 2.6 karena akan hilang di Python 3.0

Kelas BaseException tidak menyediakan cara untuk menyimpan pesan kesalahan lagi. Anda harus menerapkannya sendiri. Anda dapat melakukan ini dengan subkelas yang menggunakan properti untuk menyimpan pesan.

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

Semoga ini membantu

Sahas
sumber
1
Bagaimana Anda menginisialisasi pesan selama kenaikan? Kode-nya menunjukkan pesan yang disetel dengan memanggil MyException ("some message")
eric.frederich
Metode dalam contoh saya hanya untuk mengimplementasikan properti pesan. Cara properti digunakan tergantung pada pembuat kode. Dalam hal ini OP menggunakan metode init dan str yang telah ia posting dalam kodenya.
Sahas
1
Pertimbangkan untuk menggunakan variabel publik alih-alih pengambil / penyetel, jika hanya membaca variabel lain. Anda selalu dapat memutakhirkannya nanti menjadi @propertysintaks ketika Anda benar-benar membutuhkan enkapsulasi.
vdboor
2
@vdboor: dia gunakan @propertyuntuk menonaktifkan peringatan penghentian.
bukzor
Adalah unpythonic dan tidak berguna untuk membuat properti yang tidak melakukan apa-apa selain mendapatkan dan mengatur nilai, tidak dicentang dan tidak berubah. Inti dari semua properti adalah untuk memungkinkan seseorang menggunakan atribut publik secara bebas, sampai waktu seperti pengambil atau penyetel benar-benar diperlukan. Kemudian Anda mengubah atribut publik menjadi properti, tanpa melanggar kode klien apa pun.
Luciano Ramalho
9
class MyException(Exception):

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

e = MyException('asdf')
print e

Ini adalah kelas Anda dengan gaya Python2.6. Pengecualian baru mengambil sejumlah argumen sewenang-wenang.

Maxim Sloyko
sumber
1
Kelas Exception yang lama juga mengambil sejumlah argumen. Anda sepenuhnya dapat menghindari properti pesan seperti apa yang Anda lakukan, tetapi jika itu akan merusak kode yang ada, Anda dapat memecahkan masalah dengan menerapkan properti pesan Anda sendiri.
Sahas
8

Cara meniru peringatan

Biarkan saya mengklarifikasi masalah, karena seseorang tidak dapat mereplikasi ini dengan kode sampel pertanyaan, ini akan mereplikasi peringatan dalam Python 2.6 dan 2.7, jika Anda memiliki peringatan yang diaktifkan (melalui -Wbendera , PYTHONWARNINGSvariabel lingkungan, atau modul peringatan ):

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

Berhenti menggunakan .message

Saya lebih suka repr(error), yang mengembalikan string yang berisi nama jenis kesalahan, repr dari pesan, jika ada, dan repr dari argumen yang tersisa.

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

Menghilangkan peringatan saat masih menggunakan .message

Dan cara Anda mendapatkan menyingkirkan dari DeprecationWarningadalah subclass pengecualian builtin sebagai desainer Python dimaksudkan:

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

hanya mendapatkan .messageatribut tanpaerror.message

Jika Anda tahu ada satu argumen, sebuah pesan, untuk Pengecualian dan itulah yang Anda inginkan, lebih baik untuk menghindari atribut pesan dan hanya mengambil strkesalahan. Katakan untuk subclass Exception:

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

Dan penggunaan:

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

Lihat juga jawaban ini:

Cara yang tepat untuk mendeklarasikan pengecualian khusus dalam Python modern?

Aaron Hall
sumber
4

Sejauh yang saya tahu, cukup menggunakan nama yang berbeda untuk atribut pesan menghindari konflik dengan kelas dasar, dan dengan demikian menghentikan peringatan penghentian:

class MyException(Exception):

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

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

Sepertinya hack bagi saya.

Mungkin seseorang dapat menjelaskan mengapa peringatan itu dikeluarkan bahkan ketika subclass mendefinisikan atribut pesan secara eksplisit. Jika kelas dasar tidak lagi memiliki atribut ini, seharusnya tidak ada masalah.

Hollister
sumber
4

Melanjutkan dari jawaban geekQ , penggantian kode yang disukai tergantung pada apa yang perlu Anda lakukan:

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

Terkadang pengecualian memiliki lebih dari satu argumen, jadi my.args[0]tidak dijamin untuk memberikan semua informasi yang relevan.

Misalnya:

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

Mencetak sebagai 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)

Namun itu merupakan trade off yang sensitif terhadap konteks, karena misalnya:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected
pzrq
sumber
1
Ya, jangan terlalu dipikirkan. Gunakan str(my)sebagai ganti my.message.
Christian Long
1

Saran untuk menggunakan str (myexception) mengarah ke masalah unicode di python 2.7, misalnya:

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

berfungsi seperti yang diharapkan, dan lebih disukai dalam kasus di mana beberapa konten dari string kesalahan termasuk input pengguna

jjc
sumber
1

Posting pzrq mengatakan untuk menggunakan:

str(e)

Inilah yang saya butuhkan.

(Jika Anda berada di lingkungan unicode, tampaknya:

unicode(e)

akan bekerja, dan tampaknya berfungsi dengan baik di lingkungan non-unicode)

Pzrq mengatakan banyak hal bagus lainnya, tetapi saya hampir melewatkan jawaban mereka karena semua hal baik itu. Karena saya tidak memiliki 50 poin, saya tidak dapat mengomentari jawaban mereka untuk mencoba menarik perhatian pada solusi sederhana yang berhasil, dan karena saya tidak memiliki 15 poin, saya tidak dapat memilih jawaban itu, tetapi saya dapat memposting (merasa terbelakang, tetapi oh well) - jadi di sini saya memposting - mungkin kehilangan poin untuk itu ...

Karena poin saya adalah untuk menarik perhatian pada jawaban pzrq, tolong jangan sayu dan lewatkan semuanya di bawah. beberapa baris pertama dari posting ini adalah yang paling penting.

Ceritaku:

Masalah yang saya datangi di sini adalah jika Anda ingin menangkap pengecualian dari kelas yang tidak dapat Anda kendalikan - lalu bagaimana ??? Saya tentu saja tidak akan mensubklasifikasikan semua kelas yang mungkin digunakan kode saya dalam upaya untuk bisa mendapatkan pesan dari semua kemungkinan pengecualian!

Saya menggunakan:

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

yang, seperti kita semua tahu, memberi peringatan yang ditanyakan OP (yang membawaku ke sini), dan ini, yang diberikan pzrq sebagai cara untuk melakukannya:

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

tidak.

Saya tidak berada dalam lingkungan unicode, tetapi jawaban jjc membuat saya bertanya-tanya, jadi saya harus mencobanya. Dalam konteks ini menjadi:

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

yang mengejutkan saya, bekerja persis seperti str (e) - jadi sekarang itulah yang saya gunakan.

Tidak tahu apakah 'str (e) / unicode (e)' adalah 'cara Python yang disetujui', dan saya mungkin akan mencari tahu mengapa itu tidak baik ketika saya sampai ke 3.0, tetapi orang berharap bahwa kemampuan untuk menangani suatu pengecualian tak terduga (*) tanpa sekarat dan masih mendapatkan beberapa informasi darinya tidak akan pernah hilang ...

(*) Hmm. "pengecualian tak terduga" - Saya pikir saya baru saja gagap!

rustycar
sumber