¿Probar / atrapar o validar la velocidad?

Estoy trabajando con Python y siempre que tuve que validar la entrada de la función, asumí que la entrada funcionó y luego detecté errores.

En mi caso, tuve una clase de Vector() universal que usé para algunas cosas diferentes, una de las cuales es la sum. Funcionó como una clase Color() y como Vector() , así que cuando agrego un escalar al Color() , debería agregar esa constante a cada componente individual. Vector() adición de Vector() y de Vector() requirió la adición de componentes.

Este código se usa para un trazador de rayos, por lo que cualquier aumento de velocidad es excelente.

Aquí hay una versión simplificada de mi clase 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) 

Actualmente estoy usando el método try...except . ¿Alguien sabe de un método más rápido?


EDITAR: Gracias a las respuestas, probé y probé la siguiente solución, que comprueba específicamente el nombre de una clase antes de agregar los objetos 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) 

Hice una prueba de velocidad con estos dos bloques de código usando timeit , y los resultados fueron bastante significativos:

  1.0528049469 usec/pass for Try...Except 0.732456922531 usec/pass for If...Else Ratio (first / second): 1.43736090753 

No he probado la clase Vector() sin ninguna validación de entrada (es decir, moviendo la comprobación de la clase al código real), pero me imagino que es incluso más rápido que el método if...else .


Actualización tardía : Mirando hacia atrás en este código, esta no es una solución óptima.

OOP hace esto aún más rápido:

 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) 

Aprecié la respuesta de Matt Joiner, pero quería incluir algunas observaciones adicionales para aclarar que, junto con un par de otros factores, hay 4 veces más importantes al elegir entre las condiciones de verificación previa (conocidas como LBYL o “Mire antes de saltar” “) y solo manejar las excepciones (conocidas como EAFP o” Más fácil de pedir perdón que el permiso “).

Esos tiempos son:

  • Momento en que la comprobación se realiza correctamente con LBYL
  • Momento en que falla la comprobación con LBYL
  • Tiempo cuando no se lanza una excepción con EAFP
  • Momento cuando se lanza una excepción con EAFP

Los factores adicionales son:

  • La proporción típica de casos de éxito / fracaso de verificación o de excepción lanzada / no lanzada
  • Si hay o no una condición de carrera que impide el uso de LBYL

Ese último punto es el que debe abordarse primero: si existe un potencial para una condición de carrera, entonces no tiene otra opción, debe usar el manejo de excepciones. Un ejemplo clásico es:

 if :  # May still fail if another process creates the target dir 

Dado que LBYL no descarta que la excepción sea en tales casos, no ofrece un beneficio real y no hay que hacer un juicio: EAFP es el único enfoque que manejará la condición de la carrera correctamente.

Pero si no hay una condición de raza, cualquiera de los dos enfoques es potencialmente viable. Ofrecen diferentes compensaciones:

  • Si no se produce ninguna excepción, entonces EAFP está cerca de ser libre.
  • sin embargo, es comparativamente costoso si se produce una excepción, ya que hay mucho procesamiento involucrado en el desenrollado de la stack, creando la excepción y comparándola con las cláusulas de manejo de excepciones
  • LBYL, por el contrario, incurre en un costo fijo potencialmente alto: la verificación adicional siempre se realiza, independientemente del éxito o el fracaso

Eso lleva a los siguientes criterios de decisión:

  • ¿Se sabe que este código es crítico para la velocidad de la aplicación? Si no, no te preocupes por cuál de los dos es más rápido, preocupate por cuál de los dos es más fácil de leer.
  • ¿La verificación previa es más costosa que el costo de criar y atrapar una excepción? Si es así, entonces EAFP siempre es más rápido y debe usarse.
  • Las cosas se ponen más interesantes si la respuesta es “no”. En ese caso, lo que es más rápido dependerá de si el caso de éxito o error es más común, y las velocidades relativas de la verificación previa y el manejo de excepciones. Responder esto definitivamente requiere mediciones de tiempo reales.

Como regla general:

  • Si hay una condición potencial de carrera, use EAFP
  • Si la velocidad no es crítica, simplemente use lo que considere más fácil de leer.
  • Si la comprobación previa es costosa, use EAFP
  • Si espera que la operación tenga éxito la mayor parte del tiempo *, use EAFP
  • Si espera que la operación falle más de la mitad del tiempo, use LBYL
  • En caso de duda, mídelo.

* Las personas variarán en cuanto a lo que consideran “la mayor parte del tiempo” en este contexto. Para mí, si espero que la operación tenga éxito más de la mitad del tiempo, simplemente usaría EAFP como una cuestión de rutina, hasta que tuviera razones para sospechar que este fragmento de código era un cuello de botella real en el rendimiento.

En Python, las excepciones suelen ser más rápidas debido a la cantidad reducida de búsquedas. Sin embargo, un amigo dijo una vez (y debería aplicarse a cualquier idioma), pretender que cada vez que se detecta una excepción, hay un pequeño retraso. Evite usar excepciones donde un retraso pueda ser un problema.

En el ejemplo que has dado, me gustaría ir con la excepción.