【问题标题】:Nested try statements in python?python中的嵌套try语句?
【发布时间】:2010-10-16 15:11:24
【问题描述】:

有没有更好的方法来做以下事情:

try:
    a.method1()
except AttributeError:
    try:
        a.method2()
    except AttributeError:
        try:
            a.method3()
        except AttributeError:
            raise

它看起来很讨厌,我宁愿不这样做:

if hasattr(a, 'method1'):
    a.method1()
else if hasattr(a, 'method2'):
    a.method2()
else if hasattr(a, 'method3'):
    a.method3()
else:
    raise AttributeError

保持最大效率。

【问题讨论】:

  • 您是否测试过您的理论,即第二种选择效率低下?如果它不比第一个更有效,我会感到惊讶。
  • Oddthinking 可能是正确的。 hasattr 消除了引发异常的需要。
  • 其实hasattr()的实现本质上只是调用getattr(),如果抛出异常则返回False;见svn.python.org/view/python/tags/r254/Python/…
  • 但这并不意味着第二个不会更快。这取决于第一种方法是否可能存在。
  • -1:过早的优化。为什么要担心性能?第一个显然是你的意思——只要这样做,不要在“效率”上狡辩,直到你能证明例外是你的瓶颈。

标签: python try-catch


【解决方案1】:

一个紧凑的解决方案:

getattr(a, 'method1',
    getattr(a, 'method2',
        getattr(a, 'method3')))()

【讨论】:

  • 紧凑,但可能是错误的。如果amethod1没有method3,那么这将失败。 getattr 的第三个参数在调用 getattr 之前进行评估,这意味着此代码会在考虑 method1method2 之前尝试获取 method3。请参阅Ethan Furman's answer 以获得更安全的替代方案。
【解决方案2】:
method = (
        getattr(a, 'method1', None) or
        getattr(a, 'method2', None) or
        getattr(a, 'method3')
        )
method()

这将首先查找method1,然后是method2,然后是method3。一旦找到其中一个,搜索将立即停止。如果没有找到任何方法,最后一个getattr 将引发异常。

【讨论】:

    【解决方案3】:

    对第二个稍作改动看起来非常漂亮和简单。我真的怀疑你会注意到两者之间的任何性能差异,这比嵌套的 try/excepts 好一点

    def something(a):
        for methodname in ['method1', 'method2', 'method3']:
            try:
                m = getattr(a, methodname)
            except AttributeError:
                pass
            else:
                return m()
        raise AttributeError
    

    另一种非常易读的方法是......

    def something(a):
        try:
            return a.method1()
        except:
            pass
    
        try:
            return a.method2()
        except:
            pass
    
        try:
            return a.method3()
        except:
            pass
    
        raise AttributeError
    

    虽然很长,但函数的作用非常明显。性能确实不应该成为问题(如果一些 try/except 语句明显减慢了您的脚本速度,那么脚本结构可能存在更大的问题)

    【讨论】:

    • 我喜欢第二个,因为它非常易读且直截了当。如果性能确实是一个问题,那么原始发布者可能做错了什么。
    【解决方案4】:

    如果您使用的是新式对象:

    methods = ('method1','method2','method3')
    for method in methods:
        try:
            b = a.__getattribute__(method)
        except AttributeError:
            continue
        else:
            b()
            break
    else:
        # re-raise the AttributeError if nothing has worked
        raise AttributeError
    

    当然,如果您不使用新式对象,您可以尝试使用__dict__ 而不是__getattribute__

    编辑:这段代码可能被证明是一团糟。如果没有找到__getattribute____dict__,请猜测会引发什么样的错误。

    【讨论】:

    • 一定要用getattr()函数代替getattribute方法。
    • 我无法完全弄清楚 getattr 与 getattribute 的相对优势。存在其中一个会引发 AttributeError 而另一个会起作用的对象。
    【解决方案5】:

    也许你可以试试这样的:

    def call_attrs(obj, attrs_list, *args):
        for attr in attrs_list:
            if hasattr(obj, attr):
                bound_method = getattr(obj, attr)
                return bound_method(*args)
    
        raise AttributeError
    

    你可以这样称呼它:

    call_attrs(a, ['method1', 'method2', 'method3'])
    

    这将尝试按照它们在列表中的顺序调用方法。如果你想传递任何参数,你可以像这样在列表之后传递它们:

    call_attrs(a, ['method1', 'method2', 'method3'], arg1, arg2)
    

    【讨论】:

      【解决方案6】:

      将调用封装在一个函数中怎么样?

      def method_1_2_or_3():
          try:
              a.method1()
              return
          except AttributeError:
              pass
          try:
              a.method2()
              return
          except AttributeError:
              pass
          try:
              a.method3()
          except AttributeError:
              raise
      

      【讨论】:

      • 为什么是“封装”部分?在我看来,pass 是个好主意。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-14
      • 2013-11-21
      • 2010-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多