ylab color change python assert assertion raise

color - python plotly axis



Las mejores prácticas para la afirmación de Python (13)

Los cuatro propósitos de assert

Supongamos que trabaja en 200,000 líneas de código con cuatro colegas, Alice, Bernd, Carl y Daphne. Llaman a tu código, tú llamas a su código.

Luego assert tiene cuatro roles :

  1. Informa a Alice, Bernd, Carl y Daphne lo que tu código espera.
    Suponga que tiene un método que procesa una lista de tuplas y que la lógica del programa se puede romper si esas tuplas no son inmutables:

    def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples))

    Esto es más confiable que la información equivalente en la documentación y mucho más fácil de mantener.

  2. Informe a la computadora lo que su código espera.
    assert aplica el comportamiento adecuado de los llamadores de su código. Si su código llama a Alices y el código de Bernd llama al suyo, entonces sin el assert , si el programa falla en el código de Alices, Bernd podría asumir que fue culpa de Alice, Alice investiga y podría asumir que fue su culpa, usted investiga y le dice a Bernd que estaba allí. hecho su Mucho trabajo perdido.
    Con aseveraciones, quienquiera que reciba una llamada equivocada, rápidamente podrá ver que fue su culpa, no la suya. Alice, Bernd, y todos ustedes se benefician. Ahorra inmensas cantidades de tiempo.

  3. Informe a los lectores de su código (incluido usted mismo) lo que su código ha logrado en algún momento.
    Supongamos que tiene una lista de entradas y que cada una de ellas puede estar limpia (lo que es bueno) o puede ser smorsh, trale, gullup o twinkled (que no todas son aceptables). Si está mal, debe estar desaliñado; si es cierto debe ser baludoed; si es gullup, debe ser trotada (y luego, posiblemente, estimulada); si está centelleado, debe hacerlo otra vez, excepto los jueves. Entiendes la idea: son cosas complicadas. Pero el resultado final es (o debería ser) que todas las entradas están limpias. Lo que se debe hacer (TM) es resumir el efecto de su ciclo de limpieza como

    assert(all(entry.isClean() for entry in mylist))

    Estas declaraciones ahorran un dolor de cabeza para todos los que intentan comprender qué es exactamente lo que está logrando el maravilloso bucle. Y el más frecuente de estas personas probablemente sea usted mismo.

  4. Informe a la computadora lo que su código ha logrado en algún momento.
    Si alguna vez olvida marcar el ritmo de una entrada que la necesita después de trotar, la assert salvará su día y evitará que su código se rompa, querida Daphne, mucho después.

En mi opinión, los dos propósitos de la aseveración de la documentación (1 y 3) y la protección (2 y 4) son igualmente valiosos.
Informar a las personas puede ser incluso más valioso que informar a la computadora, ya que puede evitar los mismos errores que la assert pretende capturar (en el caso 1) y muchos errores subsiguientes en cualquier caso.

  1. ¿Existe un problema de rendimiento o de mantenimiento del código con el uso de assert como parte del código estándar en lugar de utilizarlo solo para fines de depuración?

    Es

    assert x >= 0, ''x is less than zero''

    mejor o peor que

    if x < 0: raise Exception, ''x is less than zero''

  2. Además, ¿hay alguna manera de establecer una regla de negocios como if x < 0 raise error que siempre se verifica sin try/except/finally , si en cualquier momento a lo largo del código x es menor que 0 se genera un error, como si ¿ assert x < 0 establecer assert x < 0 al inicio de una función, en cualquier lugar dentro de la función donde x sea ​​menor que 0, se genera una excepción?


¿ Hay un problema de rendimiento?

  • Por favor, recuerde "hacer que funcione primero antes de hacerlo rápido" .
    Muy poco por ciento de cualquier programa suele ser relevante por su velocidad. Siempre puede expulsar o simplificar una assert si alguna vez resulta ser un problema de rendimiento, y la mayoría de ellos nunca lo hará.

  • Sea pragmático :
    Suponga que tiene un método que procesa una lista no vacía de tuplas y la lógica del programa se romperá si esas tuplas no son inmutables. Deberías escribir:

    def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples))

    Probablemente esto esté bien si sus listas tienden a tener diez entradas, pero puede convertirse en un problema si tienen un millón de entradas. Pero en lugar de descartar por completo este valioso cheque, simplemente puede rebajarlo a

    def mymethod(listOfTuples): assert(type(listOfTuples[0])==tuple) # in fact _all_ must be tuples!

    lo cual es barato, pero probablemente detectará la mayoría de los errores reales del programa.


Además de las otras respuestas, se afirma que lanzan excepciones, pero solo AssertionErrors. Desde un punto de vista utilitario, las aserciones no son adecuadas para cuando se necesita un control de grano fino sobre qué excepciones se detectan.


Como se dijo anteriormente, las afirmaciones deben usarse cuando su código NO DEBE llegar a un punto, lo que significa que hay un error allí. Probablemente la razón más útil que puedo ver para usar una aserción es una invariante / pre / postcondición. Estos son algo que debe ser cierto al principio o al final de cada iteración de un bucle o una función.

Por ejemplo, una función recursiva (2 funciones separadas, de modo que 1 controla la entrada incorrecta y la otra el código incorrecto, porque es difícil de distinguir con la recursión). Esto haría que fuera obvio si me olvidaba de escribir la declaración if, lo que había salido mal.

def SumToN(n): if n <= 0: raise ValueError, "N must be greater than or equal to 0" else: return RecursiveSum(n) def RecursiveSum(n): #precondition: n >= 0 assert(n >= 0) if n == 0: return 0 return RecursiveSum(n - 1) + n #postcondition: returned sum of 1 to n

Estos invariantes de bucle a menudo se pueden representar con una aserción.


En IDE, como PTVS, PyCharm, Wing assert isinstance() declaraciones pueden usarse para habilitar la finalización de código para algunos objetos poco claros.


Existe un marco denominado JBoss Drools para java que realiza el monitoreo en tiempo de ejecución para hacer valer las reglas comerciales, que responde a la segunda parte de su pregunta. Sin embargo, no estoy seguro de si existe un marco para python.


La palabra en inglés aseverar aquí se usa en el sentido de juro , afirmar , reconocer . No significa "cheque" o "debería ser" . Significa que usted, como programador, está haciendo una declaración jurada aquí:

# I solemnly swear that here I will tell the truth, the whole truth, # and nothing but the truth, under pains and penalties of perjury, so help me FSM assert answer == 42

Si el código es correcto, con la excepción de alteraciones de eventos únicos , fallas de hardware y demás, ninguna afirmación nunca fallará . Es por eso que el comportamiento del programa para un usuario final no debe verse afectado. Especialmente, una afirmación no puede fallar incluso bajo condiciones programáticas excepcionales . Simplemente nunca sucede. Si sucede, el programador debe ser zapped para ello.


Lo único que realmente está mal con este enfoque es que es difícil hacer una excepción muy descriptiva con declaraciones de afirmación. Si está buscando una sintaxis más simple, recuerde que también puede hacer algo como esto:

class XLessThanZeroException(Exception): pass def CheckX(x): if x < 0: raise XLessThanZeroException() def foo(x): CheckX(x) #do stuff here

Otro problema es que el uso de asertar para la verificación de condición normal es que dificulta la inhabilitación de las aserciones de depuración utilizando el indicador -O.


Para poder lanzar automáticamente un error cuando x sea menor que cero en toda la función. Puede utilizar descriptores de clase . Aquí hay un ejemplo:

class LessThanZeroException(Exception): pass class variable(object): def __init__(self, value=0): self.__x = value def __set__(self, obj, value): if value < 0: raise LessThanZeroException(''x is less than zero'') self.__x = value def __get__(self, obj, objType): return self.__x class MyClass(object): x = variable() >>> m = MyClass() >>> m.x = 10 >>> m.x -= 20 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "my.py", line 7, in __set__ raise LessThanZeroException(''x is less than zero'') LessThanZeroException: x is less than zero


Se deben usar aserciones para probar condiciones que nunca deberían suceder . El propósito es bloquearse temprano en el caso de un estado de programa corrupto.

Deben usarse excepciones para los errores que posiblemente puedan ocurrir, y casi siempre debe crear sus propias clases de excepción .

Por ejemplo, si está escribiendo una función para leer desde un archivo de configuración en un dict , el formato incorrecto en el archivo debería generar un ConfigurationSyntaxError , mientras que puede assert que no está a punto de devolver None .

En su ejemplo, si x es un valor establecido a través de una interfaz de usuario o de una fuente externa, lo mejor es una excepción.

Si x solo está establecido por su propio código en el mismo programa, vaya con una aserción.


Si está tratando con un código heredado que depende de assert para funcionar correctamente, aunque no debería , entonces agregar el siguiente código es una solución rápida hasta que encuentre tiempo para refactorizar:

try: assert False raise Exception(''Python Assertions are not working. This tool relies on Python Assertions to do its job. Possible causes are running with the "-O" flag or running a precompiled (".pyo" or ".pyc") module.'') except AssertionError: pass


Una afirmación es verificar
1. la condición válida,
2. la declaración válida,
3. verdadera lógica;
de código fuente. En lugar de fallar todo el proyecto, da la alarma de que algo no es apropiado en su archivo fuente.

En el ejemplo 1, ya que la variable ''str'' no es nula. Así que no hay ninguna afirmación o excepción se plantean.

Ejemplo 1:

#!/usr/bin/python str = ''hello Pyhton!'' strNull = ''string is Null'' if __debug__: if not str: raise AssertionError(strNull) print str if __debug__: print ''FileName ''.ljust(30,''.''),(__name__) print ''FilePath ''.ljust(30,''.''),(__file__) ------------------------------------------------------ Output: hello Pyhton! FileName ..................... hello FilePath ..................... C:/Python/hello.py

En el ejemplo 2, var ''str'' es nul. Así que estamos evitando que el usuario vaya por delante del programa defectuoso mediante una declaración de afirmación.

Ejemplo 2:

#!/usr/bin/python str = '''' strNull = ''NULL String'' if __debug__: if not str: raise AssertionError(strNull) print str if __debug__: print ''FileName ''.ljust(30,''.''),(__name__) print ''FilePath ''.ljust(30,''.''),(__file__) ------------------------------------------------------ Output: AssertionError: NULL String

En el momento en que no queremos depurar y nos dimos cuenta del problema de aserción en el código fuente. Deshabilitar la bandera de optimización

python -O assertStatement.py
nada se imprimirá


Las declaraciones "assert" se eliminan cuando se optimiza la compilación . Entonces, sí, hay diferencias funcionales y de rendimiento.

El generador de código actual no emite ningún código para una declaración de afirmación cuando se solicita la optimización en el momento de la compilación. - Python 2.6.4 Docs

Si usa assert para implementar la funcionalidad de la aplicación, luego optimice la implementación a la producción, estará plagado de defectos "pero-funciona-en-dev".

Ver PYTHONOPTIMIZE y -O -OO