【问题标题】:Python: How to handle method calls on a class that could not initialize correctly?Python:如何处理无法正确初始化的类的方法调用?
【发布时间】:2011-12-08 21:18:40
【问题描述】:

如果一个对象依赖于 Python 未包含的模块(如 win32api、gstreamer、gui 工具包等),并且该模块中的类/函数/方法可能会失败,那么该对象应该做什么?

这是一个例子:

import guimodule  # Just an example; could be anything

class RandomWindow(object):

    def __init__(self):
        try:
            self.dialog = guimodule.Dialog()  # I might fail
        except: guimodule.DialogError:
            self.dialog = None  # This can't be right

    def update(self):
        self.dialog.prepare()
        self.dialog.paint()
        self.dialog.update()

    # ~30 more methods

这个类只是一个更大程序的一小部分(并且是不必要的,但很有用)。

假设我们有一个名为guimodule 的假想模块和一个名为Dialog 的类,它可能无法实例化。如果我们的 RandomWindow 类有 30 个操作这个窗口的方法,检查 if self.dialog is not None 会很痛苦,并且在经常使用的方法(如上面示例中的 update 方法)中实现时会减慢程序的速度。在 NoneType 上调用 .paint()(当 Dialog 加载失败时)会引发错误,并且使用原始的所有方法和属性创建一个虚拟的 Dialog 类将是荒谬的。

如何修改我的类以处理 Dialog 类的失败创建?

【问题讨论】:

    标签: python oop coding-style module refactoring


    【解决方案1】:

    您应该允许__init__ 中引发的异常传播出去,以便以适当的方式处理错误,而不是创建无效对象。或者您可以提出不同的异常。

    另见Python: is it bad form to raise exceptions within __init__?

    【讨论】:

    • 为什么这比让类在内部处理错误更好/更简单?
    • @DK,直接知道该对象不能使用比每次使用该对象都失败更简单。
    【解决方案2】:

    您可能会发现拥有它的两个子类很有用;一个使用该模块,一个不使用。 “工厂”方法可以确定哪个子类是合适的,并返回该子类的一个实例。

    通过子类化,您允许他们共享与该模块是否可用无关的代码。

    【讨论】:

    • 这个问题是我每次修改原始文件时都必须修改虚拟/子类(让它们保持同步会很烦人)。
    【解决方案3】:

    我同意“检查 self.dialog 是否不是 None 会很痛苦”,但我不同意它会减慢速度,因为如果存在 self.dialog 会更慢。所以暂时忘记缓慢。所以一种处理方法是创建一个对函数调用不执行任何操作的 MockDialog,例如

    class RandomWindow(object):
    
        def __init__(self):
            try:
                self.dialog = guimodule.Dialog()  # I might fail
            except: guimodule.DialogError:
                self.dialog = DummyDialog() # create a placeholder
    
    class DummyDialog(object):
        # either list all methods or override __getattr__ to create a mock object
    

    【讨论】:

      【解决方案4】:

      如果您考虑使用 Python 的 __getattr__ 功能,则创建一个虚拟的 Dialog 类并不像您想象的那么荒谬。以下虚拟实现将完全满足您的需求:

      class DummyDialog:
        def __getattr__(self, name):
          def fct(*args, **kwargs):
            pass
          return fct 
      

      【讨论】:

      • 如果有返回方法呢?
      • 你是对的,不能像这样模拟具有返回值的对话框方法。您可以在 DummyDialog 中单独定义它们并返回任何虚拟值,或者您必须选择一些完全不同的方法,如 Aaron 提出的涉及子类化的方法。
      猜你喜欢
      • 1970-01-01
      • 2015-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-24
      • 2019-10-03
      • 2022-01-17
      • 1970-01-01
      相关资源
      最近更新 更多