BaseException.message không dùng nữa trong Python 2.6


177

Tôi nhận được một cảnh báo rằng BaseException.message không được dùng trong Python 2.6 khi tôi sử dụng ngoại lệ do người dùng xác định sau:

class MyException(Exception):

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

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

Đây là cảnh báo:

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

Có chuyện gì với cái này vậy? Tôi phải thay đổi gì để thoát khỏi cảnh báo khấu hao?


9
Xem PEP 352 để biết lý do: python.org/dev/peps/pep-0352/#retracted-ideas
balpha

Câu trả lời:


154

Giải pháp - hầu như không cần mã hóa

Chỉ cần kế thừa lớp ngoại lệ của bạn từ Exceptionvà truyền thông điệp làm tham số đầu tiên cho hàm tạo

Thí dụ:

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

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

Bạn có thể sử dụng str(my)hoặc (kém thanh lịch) my.args[0]để truy cập tin nhắn tùy chỉnh.

Lý lịch

Trong các phiên bản mới hơn của Python (từ 2.6), chúng tôi có nghĩa vụ kế thừa các lớp ngoại lệ tùy chỉnh của chúng tôi từ Exception, bắt đầu từ Python 2.5 ) kế thừa từ BaseException. Nền được mô tả chi tiết trong 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__đã được thực hiện theo một cách có ý nghĩa, đặc biệt đối với trường hợp chỉ có một đối số (có thể được sử dụng làm thông điệp).

Bạn không cần phải lặp lại __str__hoặc __init__thực hiện hoặc tạo _get_messagetheo đề xuất của người khác.


5
Đã thay đổi BaseException thành Exception theo đề xuất của
@Matt

8
Sử dụng dấu strngắt nếu ngoại lệ được xây dựng với đối số unicode: str(MyException(u'\xe5'))tăng UnicodeEncodeError. Việc sử dụng unicodethay vì strkhông thể đánh lừa được vì unicode(MyException('\xe5'))làm tăng UnicodeDecodeError. Điều này có nghĩa là nếu tôi không biết trước nếu đối số là strhoặc unicode, tôi phải sử dụng .args[0]nơi tôi đã sử dụng trước đây .message?
kasperd

1
@kasperd Giống như hầu hết các vấn đề về unicode của Python, điều này có thể được giải quyết bằng một bánh sandwich unicode .
Ryan P

3
@RyanP Giả sử tôi thực sự có quyền kiểm soát những gì diễn ra. Đây là một thực tế của cuộc sống mà tôi phải đối mặt. Tôi phải xử lý các trường hợp ngoại lệ từ nhiều thư viện bên thứ ba. Một số trong số đó vượt qua unicode đến ngoại lệ của họ và một số vượt qua str. Một trong những thư viện thậm chí có lớp riêng của nó kế thừa từ unicode nhưng có phương thức repr riêng, trả về unicode chứ không phải str theo yêu cầu của spec.
kasperd

1
Tôi đã làm btw. thay thế tất cả việc sử dụng .messagetrong dự án của chúng tôi bằng .args[0], và điều đó không gây ra bất kỳ vấn đề nào cho chúng tôi.
kasperd

26

Có, nó không được dùng trong Python 2.6 vì nó sẽ biến mất trong Python 3.0

Lớp BaseException không cung cấp cách lưu trữ thông báo lỗi nữa. Bạn sẽ phải tự thực hiện nó. Bạn có thể làm điều này với một lớp con sử dụng một thuộc tính để lưu trữ tin nhắn.

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

Hi vọng điêu nay co ich


1
Làm thế nào bạn sẽ khởi tạo tin nhắn trong quá trình tăng? Mã của anh ấy cho thấy tin nhắn đang được đặt bằng cách gọi MyException ("một số tin nhắn")
eric.frederich

Các phương thức trong ví dụ của tôi chỉ để thực hiện thuộc tính thông báo. Làm thế nào tài sản được sử dụng là tùy thuộc vào coder. Trong trường hợp này, OP sử dụng các phương thức initstr mà anh ấy đã đăng trong mã của mình.
Sahas

1
Cân nhắc sử dụng biến công khai thay vì getter / setter, nếu nó chỉ đọc một biến khác. Bạn luôn có thể nâng cấp nó sau này thành một @propertycú pháp khi bạn thực sự cần đóng gói.
vdboor

2
@vdboor: anh ấy đang sử dụng @propertyđể vô hiệu hóa cảnh báo không dùng nữa.
bukzor

Nó là unpythonic và vô dụng để tạo ra các thuộc tính không làm gì ngoài việc nhận và thiết lập giá trị, không được kiểm tra và không thay đổi. Toàn bộ quan điểm của các thuộc tính là cho phép một người sử dụng các thuộc tính công khai một cách tự do, cho đến khi thời gian như một getter hoặc setter thực sự cần thiết. Sau đó, bạn biến thuộc tính công cộng thành một thuộc tính, mà không phá vỡ bất kỳ mã khách hàng nào.
Luciano Ramalho

9
class MyException(Exception):

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

e = MyException('asdf')
print e

Đây là lớp học của bạn theo phong cách Python2.6. Ngoại lệ mới có số lượng đối số tùy ý.


1
Lớp Exception cũ cũng có bất kỳ số lượng đối số. Bạn hoàn toàn có thể tránh thuộc tính thư như những gì bạn đang làm, nhưng nếu điều đó sẽ phá vỡ mã hiện tại của bạn, bạn có thể giải quyết vấn đề bằng cách triển khai thuộc tính thư của riêng bạn.
Sahas

8

Cách nhân rộng cảnh báo

Hãy để tôi làm rõ vấn đề, vì người ta không thể sao chép điều này bằng mã mẫu của câu hỏi, điều này sẽ sao chép cảnh báo trong Python 2.6 và 2.7, nếu bạn đã bật cảnh báo (thông qua -Wcờ , PYTHONWARNINGSbiến môi trường hoặc mô-đun cảnh báo ):

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

Ngừng sử dụng .message

Tôi thích repr(error), trả về một chuỗi chứa tên của loại lỗi, repr của thông báo, nếu có một và repr của các đối số còn lại.

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

Loại bỏ cảnh báo trong khi vẫn sử dụng .message

Và cách bạn nhận được thoát khỏi của DeprecationWarninglà để phân lớp một ngoại lệ được xây dựng trong khi các nhà thiết kế Python dự định:

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

chỉ nhận được .messagethuộc tính mà không cóerror.message

Nếu bạn biết có một đối số, một thông báo, cho Ngoại lệ và đó là những gì bạn muốn, tốt nhất là tránh thuộc tính thông báo và chỉ nhận strlỗi. Nói cho một lớp con Exception:

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

Và cách sử dụng:

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

Xem thêm câu trả lời này:

Cách thích hợp để khai báo các ngoại lệ tùy chỉnh trong Python hiện đại?


4

Theo như tôi có thể nói, chỉ cần sử dụng một tên khác cho thuộc tính thông báo sẽ tránh xung đột với lớp cơ sở và do đó dừng cảnh báo không dùng nữa:

class MyException(Exception):

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

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

Có vẻ như một hack cho tôi.

Có lẽ ai đó có thể giải thích tại sao cảnh báo được đưa ra ngay cả khi lớp con xác định rõ ràng một thuộc tính thông báo. Nếu lớp cơ sở không còn thuộc tính này nữa, thì không có vấn đề gì.


4

Tiếp tục từ câu trả lời của geekQ , việc thay thế mã ưu tiên phụ thuộc vào những gì bạn cần làm:

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

Đôi khi các ngoại lệ có nhiều hơn một đối số, do đó my.args[0]không được đảm bảo để cung cấp tất cả các thông tin liên quan.

Ví dụ:

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

In dưới dạng đầu ra:

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)

Tuy nhiên, đó là một sự đánh đổi nhạy cảm bối cảnh, vì ví dụ:

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

1
Vâng, đừng lật đổ nó. Sử dụng str(my)thay vì my.message.
Christian Long

1

Lời khuyên để sử dụng str (myexception) dẫn đến các vấn đề về unicode trong python 2.7, ví dụ:

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

hoạt động như mong đợi và được ưu tiên trong trường hợp một số nội dung của chuỗi lỗi bao gồm đầu vào của người dùng


1

bài viết của pzrq nói để sử dụng:

str(e)

Đây chính xác là những gì tôi cần.

(Nếu bạn đang ở trong một môi trường unicode, có vẻ như:

unicode(e)

sẽ hoạt động và có vẻ như nó hoạt động tốt trong môi trường không unicode)

Pzrq nói rất nhiều thứ hay ho khác, nhưng tôi gần như đã bỏ lỡ câu trả lời của họ do tất cả những thứ tốt. Vì tôi không có 50 điểm nên tôi không thể nhận xét về câu trả lời của họ để cố gắng thu hút sự chú ý vào giải pháp đơn giản hiệu quả và vì tôi không có 15 điểm nên tôi không thể bỏ phiếu trả lời, nhưng tôi có thể đăng (cảm thấy lạc hậu, nhưng ồ) - vì vậy ở đây tôi đang đăng bài - có lẽ mất điểm vì điều đó ...

Vì quan điểm của tôi là thu hút sự chú ý vào câu trả lời của pzrq, xin vui lòng đừng trừng mắt và bỏ lỡ nó trong tất cả những điều dưới đây. vài dòng đầu tiên của bài này là quan trọng nhất.

Câu chuyện của tôi:

Vấn đề tôi đến đây là nếu bạn muốn bắt một ngoại lệ từ một lớp mà bạn không có quyền kiểm soát - thì sao ??? Tôi chắc chắn sẽ không phân lớp tất cả các lớp có thể mà mã của tôi sử dụng trong một nỗ lực để có thể nhận được một thông báo trong số tất cả các ngoại lệ có thể!

Tôi đang sử dụng:

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

mà, như tất cả chúng ta đều biết, đưa ra cảnh báo mà OP đã hỏi về (điều này đã đưa tôi đến đây), và điều này, mà pzrq đưa ra như một cách để làm điều đó:

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

đã không.

Tôi không ở trong một môi trường unicode, nhưng câu trả lời của jjc khiến tôi tự hỏi, vì vậy tôi phải thử nó. Trong bối cảnh này, điều này trở thành:

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

điều mà tôi ngạc nhiên, hoạt động chính xác như str (e) - vì vậy bây giờ đó là những gì tôi đang sử dụng.

Không biết liệu 'str (e) / unicode (e)' có phải là 'cách Python được phê duyệt' hay không và có lẽ tôi sẽ tìm hiểu tại sao điều đó không tốt khi tôi lên 3.0, nhưng người ta hy vọng rằng khả năng xử lý một ngoại lệ bất ngờ (*) mà không chết và vẫn nhận được một số thông tin từ nó sẽ không bao giờ biến mất ...

(*) Hừm. "ngoại lệ bất ngờ" - Tôi nghĩ rằng tôi chỉ nói lắp!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.