Python 即使“\uuu init\uuu()”引发异常,也要使用对象吗?

Python 即使“\uuu init\uuu()”引发异常,也要使用对象吗?,python,python-3.x,python-3.6,Python,Python 3.x,Python 3.6,我所处的情况是,类\uuuu init\uuuu方法中一些微不足道的重要部分可能会引发异常。在这种情况下,我希望显示一条错误消息,但继续使用实例 一个非常基本的例子: class something(object): def __init__(self): do_something_important() raise IrrelevantException() def do_something_useful(self): pass

我所处的情况是,类
\uuuu init\uuuu
方法中一些微不足道的重要部分可能会引发异常。在这种情况下,我希望显示一条错误消息,但继续使用实例

一个非常基本的例子:

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()
但是,最后一行不起作用,因为没有定义。奇怪的是,我可以发誓我以前做过这样的事情,效果很好。我甚至想了一些方法来阻止人们使用这样一个未完成的实例,因为我发现它即使在出现错误的情况下也会被创建。现在我想用它,但它不起作用。嗯


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

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

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

注意,如果您只是在寻找一条消息,那么不必费心创建一个异常对象,只需在self上设置一个错误代码/消息,稍后再进行检查。

我假设您无法控制“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类不会运行其父构造函数,除非调用它

您可以通过调用
object.\uuu new\uuu()
来创建对象来完成此操作。然后在调用
\uuuu init\uuuu()
创建对象之后

这将执行所有可能的代码

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所指出的,编辑。此代码假定已定义了
\uuuu init\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

现在,如果可以假定
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

但是,如果
对象中有错误。
则无法创建实例,也无法对其应用
中的操作

这是因为
\uuuuuuuuuuuuuuuuuu()
返回实例,而
\uuuuuuuuuuuu init()
操作实例。(当您调用
something()
时,默认的
\uuuu new\uuuu()
函数实际上调用
\uuuu init\uuuuu()
,然后安静地返回实例。)

因此,该代码最健壮的版本是:

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()
因此,同时这两种方法都回答了这个问题,后一种方法在
\uuuuu new\uuuuu()
出现错误的情况下(公认罕见)也会有所帮助,但这并不会阻止
做一些有用的事()
的工作。

评论:

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

那么,答案是显而易见的:只需删除
raiseinterrelatexception()

当然,您真正的代码可能没有
引发不相关异常
,而是调用一些可能引发异常的
危险函数()。但那很好;您可以像在其他地方一样处理异常;您在
\uuuu init\uuu
方法中这一事实没有什么区别:

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()
就这些。您的
\uuuu init\uuuu
没有理由引发异常,因此如何处理该异常的问题从一开始就不会出现


如果您不能修改某个东西,但可以将其子类化,那么您不需要任何花哨的东西:

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()
现在调用了
dou\u something\u important
,一个
something
实例得到了返回,我可以保存并调用
dou something\u有用的
等等。正是你想要的


当然,您可以使用一些巧妙的重命名技巧将
某些东西
隐藏在
更好的东西
后面:

_something = something
class something(_something):
    # same code as above
…或者只是monkeypatch
某个东西。_init__
使用包装函数而不是包装类:

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

但是,除非有很好的理由说明你不能明确说明你正在向它添加一个包装器,否则最好是明确的。

在分配任务之前,必须成功地评估任务的右侧,因此,没有定义它也就不足为奇了。即使对象是创建的(在初始化之前),如果赋值失败,您也没有对它的引用,因此它将被垃圾收集。如果您确实在某处保留了引用,例如通过类或元类,那么您仍然可以对其调用方法。在构造函数中引发异常意味着无法构造该对象,因此您将无法使用它。如果在某种情况下,构造函数内部发生的事情可能会失败,但对象仍然会被构造,那么这些东西不应该是构造函数的一部分。@poke这有点误导
\uuuu new\uuuu
是构造函数
\uuuu init\uuuu
是初始值设定项。这种差异在这里是相关的,即使不是经常发生,因为你可以有一个返回有效对象的
\uuuuuu new\uuuuuu
,然后是一个执行“无关内容”的
\uuuuu init\uuuuuu
,你可以通过,例如,使用
\uuuuuuuu init\uuuuu
方法或元类或任何将
self
隐藏在某个地方来解决这个问题,但是在一个构造函数中,这是不起作用的。对于PS,这是一个很小的问题:只打印一个错误而不是引发一个异常,然后,如何处理其他异常的整个问题并不是首先出现的。即使我问了这个问题,我现在也会认为它是坏的风格。如果它能以这种方式工作,那将是一个难看的小故障,因为正如@jornsharpe所说的,在实际分配任务之前,必须成功地评估任务的右侧。没有rend可能会失败的代码