尝试/捕获或验证速度?

Ble*_*der 36 python performance exception-handling typechecking

我正在使用Python,每当我必须验证函数输入时,我认为输入有效,然后发现错误.

就我而言,我有一个通用Vector()类,我用它来做一些不同的事情,其中​​一个是补充.它既可以作为Color()类也可以作为a Vector(),因此当我向其添加标量时Color(),它应该将该常量添加到每个单独的组件中.Vector()Vector()添加所需的组分添加.

此代码用于光线跟踪器,因此任何速度提升都很棒.

这是我Vector()班级的简化版本:

class Vector:
  def __init__(self, x, y, z):
    self.x = x
    self.y = y
    self.z = z

  def __add__(self, other):
    try:
      return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
    except AttributeError:
      return Vector(self.x + other, self.y + other, self.z + other)
Run Code Online (Sandbox Code Playgroud)

我目前正在使用这种try...except方法.有人知道更快的方法吗?


编辑:由于答案,我尝试并测试了以下解决方案,在添加Vector()对象之前专门检查类名:

class Vector:
  def __init__(self, x, y, z):
    self.x = x
    self.y = y
    self.z = z

  def __add__(self, other):
    if type(self) == type(other):
      return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
    else:
      return Vector(self.x + other, self.y + other, self.z + other)
Run Code Online (Sandbox Code Playgroud)

我使用这两个代码块进行了速度测试,timeit结果非常重要:

 1.0528049469 usec/pass for Try...Except
 0.732456922531 usec/pass for If...Else
 Ratio (first / second): 1.43736090753
Run Code Online (Sandbox Code Playgroud)

我没有测试过没有任何输入验证的Vector()类(即将校验移出类和实际的代码),但我想它甚至比方法更快.if...else


延迟更新:回顾这段代码,这不是最佳解决方案.

OOP使这更快:

class Vector:
  def __init__(self, x, y, z):
    self.x = x
    self.y = y
    self.z = z

  def __add__(self, other):
    return Vector(self.x + other.x, self.y + other.y, self.z + other.z)

class Color(Vector):
  def __add__(self, other):
    if type(self) == type(other):
      return Color(self.x + other.x, self.y + other.y, self.z + other.z)
    else:
      return Color(self.x + other, self.y + other, self.z + other)
Run Code Online (Sandbox Code Playgroud)

nco*_*lan 80

我赞成了Matt Joiner的回答,但是想要包括一些额外的观察结果,以明确说明,除了其他几个因素之外,在预检条件之间进行选择时,有4次是重要的(称为LBYL或"Look Before You Leap") ")并且只处理异常(称为EAFP或"更容易请求宽恕而非权限").

那些时间是:

  • 使用LBYL 检查成功的时间
  • 使用LBYL 检查失败时的计时
  • 当异常定时与EAFP抛出
  • 当异常时序与EAFP抛出

其他因素是:

  • 检查成功/失败或异常抛出/未抛出案例的典型比率
  • 是否存在阻止使用LBYL的竞争条件

最后一点是需要首先解决的问题:如果存在竞争条件的可能性,那么您别无选择,必须使用异常处理.一个典型的例子是:

if <dir does not exist>:
    <create dir> # May still fail if another process creates the target dir
Run Code Online (Sandbox Code Playgroud)

由于LBYL不排除异常是这种情况,它没有提供真正的好处,也没有判断要求:EAFP是唯一能够正确处理竞争条件的方法.

但如果没有竞争条件,任何一种方法都是可行的.他们提供不同的权衡:

  • 如果没有异常,则EAFP接近免费
  • 但是,如果发生异常则相对昂贵,因为在展开堆栈,创建异常并将其与异常处理子句进行比较时涉及相当多的处理
  • 相比之下,LBYL会产生潜在的高固定成本:无论成功与否,都会始终执行额外检查

然后导致以下决策标准:

  • 这段代码是否已知对应用程序的速度至关重要?如果没有,那么不要担心哪两个更快,担心哪两个更容易阅读.
  • 提前检查是否比提高和捕获例外的成本更昂贵?如果是,那么EAFP总是更快,应该使用.
  • 如果答案是"不",事情会变得更有趣.在这种情况下,更快将取决于成功或错误情况是否更常见,以及预检和异常处理的相对速度.明确地回答这个问题需要实时定时测量.

作为一个粗略的经验法则:

  • 如果存在潜在的竞争条件,请使用EAFP
  • 如果速度不重要,只需使用您认为更容易阅读的
  • 如果预检费用昂贵,请使用EAFP
  • 如果您希望操作在大多数时间*成功,请使用EAFP
  • 如果您希望操作失败的时间超过一半,请使用LBYL
  • 如果有疑问,请测量它

*在这种情况下,人们会在"大部分时间"中考虑他们的想法.对于我来说,如果我期待操作成功一半以上的时间,我只想用EAFP作为理所当然的事,直到我有理由怀疑这段代码是一个实际的性能瓶颈.

  • 哇,*这*是我收到的最好的答案之一!我认为我一直过多地依赖这些课程来反复进行繁重的工作,因为现在我开始考虑它,我只执行一次Vector/Scalar,所以如果我要移动*all*type - 检查类外部和所需的代码区域,我可以删除代码中基本上所有与类相关的延迟. (8认同)
  • @Blender:"在课堂外移动所有类型检查".普遍真实.不只是为了性能,而是为了简化整体.类只是工作,他们不验证. (3认同)
  • @Jonathon:我相信他的观点是,除了明确的验证例程之外,通常最好遵循"Garbage In Garbage Out"规则,如果人们向你提供荒谬的论证,就让Python自己的异常逃脱.Python非常适合确保在这种情况下得到异常,而不是静默生成垃圾数据 - 这只是在(相对罕见的)后一种情况下以及错误令人困惑的情况,您需要将自己的验证嵌入到操作中. (3认同)
  • 最近对这件事很好奇,于是做了一些实验。结果:http://gerg.ca/blog/post/2015/try-except-speed/。简短版本:@ncoghlan 所说的,即只要错误很少发生,异常就会更便宜。 (2认同)

Mat*_*ner 5

在Python中,由于查找次数减少,异常通常更快.然而,一位朋友曾经说过(它应该适用于任何语言),假装每次发现异常时都会有一点延迟.避免在延迟可能成为问题的情况下使用例外.

在您给出的示例中,我将使用例外.