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 :
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.
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 elassert
, 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.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 comoassert(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.
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, laassert
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.
¿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''
Además, ¿hay alguna manera de establecer una regla de negocios como
if x < 0 raise error
que siempre se verifica sintry/except/finally
, si en cualquier momento a lo largo del códigox
es menor que 0 se genera un error, como si ¿assert x < 0
establecerassert x < 0
al inicio de una función, en cualquier lugar dentro de la función dondex
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 unaassert
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