使用一个对象,即使__init __()引发异常?

时间:2018-07-09 22:28:38

标签: python python-3.x python-3.6

我处于这样的情况下,类__init__方法的一些重要部分可能引发异常。在那种情况下,我想显示一条错误消息,但继续使用实例。

一个非常基本的例子:

class something(object):
    def __init__(self):
        do_something_important()
        raise IrrelevantException()

    def do_something_useful(self):
        pass

try:
    that_thing = something()
except IrrelevantException:
    print("Something less important failed.")

that_thing.do_something_useful()

但是,最后一行不起作用,因为未定义that_thing。奇怪的是,我可以发誓以前做过这样的事情,而且效果很好。我什至讨论如何阻止人们使用未完成的实例的方法,因为我发现即使在发生错误的情况下它也会被创建。现在我想使用它,它不起作用。嗯...?!?

PS:something是我自己写的,所以我可以控制一切。

4 个答案:

答案 0 :(得分:2)

您不能同时引发异常和返回值(不会被黑客攻击)。如果这是您控制的所有代码,那么我可以建议以下模式:

class something(object):
   Exception = None
   def __init__(self):
      ...
      if BadStuff:
         self.Exception = IrrelevantException()
      ...

that_thing = something()
if that_thing.Exception:
   print(that_thing.Exception)

# carry on

请注意,如果您只是在寻找一条消息,那么就不必费心创建Exception对象,而只需在自己身上设置错误代码/消息,然后再进行检查即可。

答案 1 :(得分:2)

您可以通过调用object.__new__()创建对象来完成此操作。然后,调用__init__()创建对象。

这将执行所有可能的代码。

class IrrelevantException(Exception):
    """This is not important, keep executing."""
    pass

class something(object):
    def __init__(self):
        print("Doing important stuff.")
        raise IrrelevantException()

    def do_something_useful(self):
       print("Now this is useful.")

that_thing = object.__new__(something) # Create the object, does not call __init__
try:
    that_thing.__init__() # Now run __init__
except IrrelevantException:
    print("Something less important failed.")

that_thing.do_something_useful() # And everything that __init__ could do is done.

编辑,如@abarnert所指出。该代码确实假定已定义__init__(),但未定义__new__()

现在,如果可以假设__new__()不会出错,则可以在代码中替换object.__new__()

但是,如果object.__new__()中存在错误,则无法创建实例,也无法对其应用__new__()中的操作。

这是因为__new__()返回实例,而__init__()返回实例。 (当您调用something()时,默认的__new__()函数实际上会调用__init__(),然后悄悄地返回实例。)

因此,此代码最可靠的版本是:

class IrrelevantException(Exception):
    """This is not important, keep executing."""
    pass

class something(object):
    def __init__(self):
        print("Doing important stuff.")
        raise IrrelevantException()

    def do_something_useful(self):
       print("Now this is useful.")

try:
    that_thing = something.__new__(something) # Create the object, does not call __init__
except IrrelevantException:
    # Well, just create the object without calling cls.__new__()
    that_thing = object.__new__(something)
try:
    that_thing.__init__() # Now run __init__
except IrrelevantException:
    print("Something less important failed.")

that_thing.do_something_useful()

因此,尽管这两个都回答了问题,但后一个问题也应在__new__()有错误但不会阻止do_something_useful()正常工作的情况下(在极少数情况下)提供帮助。 / p>

答案 2 :(得分:2)

来自评论:

  

PS:有些东西是我自己写的,所以我可以控制一切。

那么,答案很明显:只需删除raise IrrelevantException()

当然,您的实际代码可能没有raise IrrelevantException,而是调用了可能引发的某些dangerous_function()。但这很好;您可以像在其他任何地方一样处理异常。您在__init__方法内部的事实没有任何区别:

class something(object):
    def __init__(self):
        do_something_important()
        try:
            do_something_dangerous()
        except IrrelevantException as e:
            print(f'do_something_dangerous raised {e!r}')
        do_other_stuff_if_you_have_any()

仅此而已。您的__init__没有理由提出异常,因此一开始就不会出现如何处理该异常的问题。


如果您不能修改something,但是可以对其进行子类化,那么您就不需要花哨的东西了:

class IrrelevantException(Exception):
    pass

def do_something_important():
    pass

class something(object):
    def __init__(self):
        do_something_important()
        raise IrrelevantException()

    def do_something_useful(self):
        pass

class betterthing(something):
    def __init__(self):
        try:
            super().__init__() # use 2.x style if you're on 2.x of course
        except IrrelevantException:
            pass # or log it, or whatever
        # You can even do extra stuff after the exception

that_thing = betterthing()
that_thing.do_something_useful()

现在调用了do_something_important,并且返回了一个something实例,我可以保存并调用do_something_useful,依此类推。正是您要找的东西。


您当然可以通过一些巧妙的重命名技巧将something隐藏在betterthing后面:

_something = something
class something(_something):
    # same code as above

…或仅使用包装函数而不是包装类的Monkeypatch something.__init__

_init = something.__init__
def __init__(self):
    try:
        _init(self)
    except IrrelevantException:
        pass
something.__init__ = __init__

但是,除非有充分的理由不能明确说明要添加包装程序,否则最好是明确说明。

答案 3 :(得分:0)

我假设您无法控制“ something”类,因此在这种情况下,您可以直接调用该方法,前提是该类中不需要任何元素。不过,您传递的是self = None,因此它将无法访问该类的变量。

    class IrrelevantException(Exception):
        x = "i don't matter"

    class something(object):
        def __init__(self):
            raise IrrelevantException()

        def do_something_useful(self):
            print('hi')

    #this will fail
    try:
        that_thing = something()
    except IrrelevantException:
        print("Something less important failed.")

    #this will run
    something.do_something_useful(None)

或者您可以使用继承:

    class mega_something(something):
        def __init__(self):
            print("its alive!")


    that_other_thing = mega_something()
    that_other_thing.do_something_useful()

除非调用mega_something类,否则不会运行其父构造函数。