Python 2.6中已弃用BaseException.message

185

当我使用以下用户定义的异常时,我收到一个警告,指出在Python 2.6中BaseException.message已被弃用:

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

这是哪里出了问题? 我需要改变什么才能消除这个弃用警告?


11
请参阅 PEP 352,了解原因:http://www.python.org/dev/peps/pep-0352/#retracted-ideas。 - balpha
8个回答

160

解决方案 - 几乎不需要编码

只需从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开始),我们应该从Exception继承自定义异常类,Exception(从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__已经以有意义的方式实现了,特别是对于仅有一个参数(可以用作消息)的情况。

您不需要重复实现__str____init__方法,也不需要创建像其他人建议的_get_message方法。


5
根据@Matt的建议,将BaseException更改为Exception。 - geekQ
8
如果异常对象是用unicode参数构造的,使用str将会出错:str(MyException(u'\xe5')) 会抛出 UnicodeEncodeError 异常。但是使用 unicode 也不是万无一失的,因为 unicode(MyException('\xe5')) 将会抛出 UnicodeDecodeError 异常。这是否意味着如果我事先不知道参数是 str 还是 unicode,那么在之前使用 .message 的地方,我必须使用 .args[0] - kasperd
1
@kasperd 像几乎所有的Python Unicode问题一样,这可以通过使用Unicode三明治来解决。 - kitti
4
@RyanP 假设我真的能控制插入内容。这里是我所面临的生活事实:我必须处理来自多个第三方库的异常情况。其中一些库将unicode传递给它们的异常,而另一些则传递str。其中一个库甚至有自己的类,它继承自unicode但具有自己的repr方法,该方法返回unicode而不是规范要求的str类型。 - kasperd
1
@kasperd 我也是这样..随着BaseException.message的弃用,现在是否有一个不使用Unicode作为错误字符串的未公开规则? - Erik van Zijst
显示剩余2条评论

25

是的,在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
我的示例中的方法仅用于实现消息属性。属性的使用取决于编程人员。在这种情况下,OP使用了他在代码中发布的__init__和__str__方法。 - Sahas
1
如果只是读取另一个变量,考虑使用公共变量而不是getter/setter。当您真正需要封装时,您总是可以将其升级为@property语法。 - vdboor
2
@vdboor:他正在使用@property来禁用过时警告。 - bukzor
创建仅用于获取和设置值的属性,而不进行任何检查和更改是不符合Python风格且无用的。属性的整个意义在于允许使用公共属性,直到实际需要getter或setter为止。然后,您可以将公共属性转换为属性,而不会破坏任何客户端代码。 - Luciano Ramalho

10

如何重现这个警告

让我澄清一下问题,由于在问题的示例代码中无法重现此问题,因此如果您已经打开了警告(通过-W 标志PYTHONWARNINGS 环境变量或warnings 模块),则可以在 Python 2.6 和 2.7 中重现此警告:

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

停止使用.message

我更喜欢使用repr(error),它返回一个包含错误类型名称、消息的repr(如果有),以及其余参数的repr的字符串。

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

在仍使用.message的情况下消除警告

而消除DeprecationWarning的方法是按照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',)"

仅获取.message属性,而不是error.message

如果您知道异常只有一个参数——即一个消息,并且这正是您想要的,最好避免使用消息属性,而是直接获取错误的str。例如,对于一个子类化的Exception

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

并且用法:

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

请参见此答案:

Proper way to declare custom exceptions in modern Python?


9
class MyException(Exception):

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

e = MyException('asdf')
print e

这是Python2.6风格的类。新的异常可以接受任意数量的参数。


1
旧的 Exception 类也可以接受任意数量的参数。你完全可以像现在这样避免使用 message 属性,但如果那会破坏你现有的代码,你可以通过实现自己的 message 属性来解决问题。 - Sahas

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

4
据我所知,只需为消息属性使用不同的名称即可避免与基类发生冲突,从而停止弃用警告:
class MyException(Exception):

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

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

这对我来说似乎是一个黑客攻击。

也许有人可以解释为什么即使子类明确定义了消息属性,警告也会被发出。如果基类不再具有此属性,则不应该存在问题。


1

在Python 2.7中,建议使用str(myexception)会导致Unicode问题,例如:

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)

这段代码可以工作,在非Unicode环境下似乎也能正常工作。

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

没有。

我不在一个Unicode环境中,但是jjc的回答让我想试一下。在这个上下文中,它变成了:

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

令我惊讶的是,它的工作方式与str(e)完全相同 - 所以现在这就是我正在使用的。

不知道'str(e)/unicode(e)'是否是“批准的Python方式”,当我到3.0时,我可能会发现为什么这不好,但希望能够处理意外异常(*)而不死机并仍然从中获取一些信息的能力永远不会消失...

(*) 嗯。“意外异常”-我想我刚刚口吃了!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接