BaseException.message เลิกใช้ใน Python 2.6


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) เราควรจะสืบทอดคลาสยกเว้นที่กำหนดเองของเราจากข้อยกเว้นซึ่ง ( เริ่มจาก 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__ถูกนำไปใช้ในทางที่มีความหมายโดยเฉพาะอย่างยิ่งสำหรับกรณีของ arg เพียงอันเดียว (ซึ่งสามารถใช้เป็นข้อความ)

คุณไม่จำเป็นต้องทำซ้ำ__str__หรือ__init__ปรับใช้หรือสร้าง_get_messageตามที่ผู้อื่นแนะนำ


5
เปลี่ยน BaseException เป็น Exception ตามที่แนะนำโดย
@Matt

8
โดยใช้strการแบ่งถ้าข้อยกเว้นที่ถูกสร้างด้วยอาร์กิวเมนต์ Unicode: การยกstr(MyException(u'\xe5')) UnicodeEncodeErrorการใช้งานunicodeแทนการใช้งานstrนั้นไม่สามารถป้องกันความผิดพลาดได้ทั้งหมดเนื่องจากการunicode(MyException('\xe5'))เพิ่ม UnicodeDecodeError นี้ไม่ได้หมายความว่าถ้าฉันไม่ได้ทราบล่วงหน้าหากอาร์กิวเมนต์เป็นstrหรือunicodeฉันต้องใช้.args[0]ที่ผมใช้ก่อนหน้านี้.message?
kasperd

1
@kasperd ชอบแทบทุกปัญหา Unicode งูใหญ่นี้จะสามารถแก้ไขได้ด้วยแซนวิช Unicode
Ryan P

3
@ RyanP ถือว่าฉันมีอำนาจควบคุมสิ่งที่เกิดขึ้นจริงนี่คือความจริงของชีวิตที่ฉันต้องเผชิญ ฉันต้องจัดการข้อยกเว้นจากห้องสมุดบุคคลที่สามหลายแห่ง บางส่วนของ Pass เหล่านั้นจะ unicode ไปยังข้อยกเว้น หนึ่งในห้องสมุดนั้นมีคลาสของตัวเองซึ่งสืบทอดมาจากยูนิโค้ด แต่มีวิธีการตอบสนองของตัวเองซึ่งส่งคืน 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 ("some message")
eric.frederich

วิธีการในตัวอย่างของฉันมีไว้สำหรับการนำคุณสมบัติข้อความไปใช้เท่านั้น วิธีใช้คุณสมบัตินั้นเกิน coder ในกรณีนี้ OP ใช้วิธีการเริ่มต้นและstrที่เขาได้โพสต์ในรหัสของเขา
Sahas

1
พิจารณาใช้ตัวแปรสาธารณะแทน getter / setter หากอ่านเฉพาะตัวแปรอื่น คุณสามารถอัปเกรดนั้นในภายหลังเป็น@propertyไวยากรณ์เมื่อคุณต้องการ encapsulation
vdboor

2
@vdboor: เขาใช้@propertyเพื่อปิดการเตือนการคัดค้าน
bukzor

มันเป็นแบบไม่มีเสียงและไร้ประโยชน์ในการสร้างคุณสมบัติที่ไม่ทำอะไรเลยยกเว้นการรับและการตั้งค่าไม่ถูกตรวจสอบและไม่เปลี่ยนแปลง จุดรวมของคุณสมบัติคือการอนุญาตให้หนึ่งใช้คุณลักษณะสาธารณะได้อย่างอิสระจนกว่าจะถึงเวลาที่จำเป็นต้องมีตัวรับหรือตัวตั้งค่า จากนั้นคุณเปลี่ยนแอททริบิวต์สาธารณะให้เป็นคุณสมบัติโดยไม่ทำลายรหัสลูกค้าใด ๆ
Luciano Ramalho

9
class MyException(Exception):

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

e = MyException('asdf')
print e

นี่คือคลาสของคุณในสไตล์ Python2.6 ข้อยกเว้นใหม่ใช้จำนวนอาร์กิวเมนต์โดยพลการ


1
คลาส Exception ระดับเก่ายังใช้อาร์กิวเมนต์จำนวนเท่าใดก็ได้ คุณสามารถหลีกเลี่ยงคุณสมบัติข้อความทั้งหมดเช่นเดียวกับสิ่งที่คุณทำ แต่ถ้านั่นจะทำลายรหัสที่มีอยู่ของคุณคุณสามารถแก้ปัญหาได้โดยการใช้คุณสมบัติข้อความของคุณเอง
Sahas

8

วิธีการทำซ้ำคำเตือน

ผมขอชี้แจงปัญหาที่เกิดขึ้นเป็นหนึ่งไม่สามารถทำซ้ำนี้กับโค้ดตัวอย่างของคำถามนี้จะทำซ้ำคำเตือนในหลาม 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เป็นประเภทรองข้อยกเว้นในตัวเป็นนักออกแบบหลามตั้งใจ:

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ข้อผิดพลาดนั้น พูดเพื่อ subclassed 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
Christian Long

1

คำแนะนำในการใช้ str (myexception) ทำให้เกิด unicode problems ใน 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))

ซึ่งตามที่เราทุกคนรู้แล้วให้เตือน OP ถามเกี่ยวกับ (ซึ่งนำฉันมาที่นี่) และสิ่งนี้ซึ่ง pzrq ให้เป็นวิธีที่จะทำ:

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

ไม่ได้.

ฉันไม่ได้อยู่ในสภาพแวดล้อมเดียว แต่คำตอบของ jjc ทำให้ฉันประหลาดใจดังนั้นฉันต้องลอง ในบริบทนี้สิ่งนี้จะกลายเป็น:

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

ที่แปลกใจของฉันทำงานเหมือน str (e) - ดังนั้นตอนนี้นั่นคือสิ่งที่ฉันใช้

ไม่ทราบว่า 'str (e) / unicode (e)' เป็น 'วิธี Python ที่ได้รับการอนุมัติ' และฉันอาจจะหาสาเหตุที่ไม่ดีเมื่อฉันได้รับถึง 3.0 แต่หวังว่าความสามารถในการจัดการ ข้อยกเว้นที่ไม่คาดคิด (*) โดยไม่ตายและยังได้รับข้อมูลบางอย่างจากมันจะไม่หายไป ...

(*) อืม "ข้อยกเว้นที่ไม่คาดคิด" - ฉันคิดว่าฉันแค่พูดติดอ่าง!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.