Manejo de errores de aprendizaje de Python

En el proceso de ejecución del programa, si ocurre un error. Puede aceptar devolver un código de error por adelantado, para que pueda saber si hay un error y el motivo del error. Es muy común devolver códigos de error en las llamadas provistas por el sistema operativo. Por ejemplo, la función open () para abrir un archivo devuelve un descriptor de archivo (es decir, un número entero) cuando tiene éxito, y devuelve -1 cuando ocurre un error.

Es inconveniente usar un código de error para indicar si hay un error, porque el resultado normal que la función en sí debería devolver se mezcla con el código de error, lo que hace que la persona que llama use mucho código para juzgar si hay un error: (escribió c, percepción profunda)

def foo():
	r = some_function()
	if r == -1:
		return -1
	return r

def bar():
	r = foo()
	if r == -1:
		print('Error')
	else:
		pass

Una vez que ocurre un error, se debe informar nivel por nivel hasta que una función pueda manejar el error (por ejemplo, enviar un mensaje de error al usuario).

Entonces, los lenguajes de alto nivel generalmente tienen un mecanismo de manejo de errores incorporado de try ... excepto ... finalmente ..., y Python no es una excepción.

tratar

Usemos un ejemplo para ver el mecanismo de prueba:

try:
	print('try...')
	r = 10 / 0
	print('result: ', r)
except ZeroDivisionError as e:
	print('except: ', e)
finally:
	print('finally...')
print('END')

Cuando pensamos que algún código puede ser incorrecto, podemos usar try para ejecutar este código. Si hay un error en la ejecución, el código posterior no continuará ejecutándose, sino que saltará directamente al código de error , es decir, la excepción Después de ejecutar la excepción Después de eso, si hay un bloque de instrucción finalmente, ejecute el bloque de instrucción final, hasta ahora, la ejecución está completa.

El código anterior generará un error de división al calcular 10/0:

try...
except: division by zero
finally...
END

Se puede ver en la salida que cuando ocurre un error, la instrucción subsiguiente print ('resultado:', r) no se ejecutará, excepto que se ejecuta porque detecta ZeroDivisionError . Finalmente, se ejecuta la sentencia finalmente. Luego, el programa continúa descendiendo según el proceso.

Si el divisor 0 se cambia a 2, el resultado de la ejecución es el siguiente:

try...
result: 5
finally...
END

Dado que no se produce ningún error, el bloque de instrucciones except no se ejecutará, pero finalmente, si lo hay, definitivamente se ejecutará (puede que no haya una instrucción final).

También puede adivinar que debe haber muchos tipos de errores, y si ocurren diferentes tipos de errores, deben ser manejados por diferentes bloques de instrucciones except. Sí, puede haber varias excepciones para detectar diferentes tipos de errores:

try:
	print('try...')
	r = 10 / int('a')
	print('result:', r)
except ValueError as e:
	print('ValueError:', e)
except ZeroDivisionError as e:
	print('ZeroDivisionError:', e)
finally:
	print('finally...')
print('END')

La función int () puede arrojar ValueError, por lo que usamos uno excepto para atrapar Valuerror y otro excepto para atrapar ZeroDivisionError.

Además, si no ocurre ningún error, puede agregar una instrucción else después del bloque de instrucción except . Cuando no ocurra ningún error, la instrucción else se ejecutará automáticamente:

try:
	print('try...')
	r = 10 / int('2')
	print('result:', r)
except ValueError as e:
	print('ValueError:', e)
except ZeroDivisionError as e:
	print('ZeroDivisionError:', e)
else:
	print('no error!')
finally:
	print('finally...')
print('END')

Los errores de Python son en realidad clases, y algunos tipos de error se heredan de BaseException, por lo que cuando se usa except, debe tenerse en cuenta que no solo detecta errores de este tipo, sino que también "captura todas sus subclases" . como:

try:
	foo()
except ValueError as e:
	print('ValueError')
except UnicodeError as e:
	print('UnicodeError')

El segundo excepto nunca detectará UnicodeError, porque UnicodeError es una subclase de ValueError. Si lo hay, también lo detectará el primero excepto.

Todos los errores de Python se derivan de la clase BaseException. Para conocer los tipos de errores comunes y las relaciones de herencia, consulte aquí (documentación oficial de Python)

Usar try ... excepto para detectar errores tiene una gran ventaja, es decir, se puede llamar a través de múltiples capas . Por ejemplo, la función main () llama a bar (), bar () llama a foo () y el resultado foo () es incorrecto. Esto es tan largo como main () Una vez capturado, puede procesar:

def foo(s):
	return 10 / int(s)

def bar(s):
	return foo(s) * 2

def main():
	try:
		bar('0')
	except Exception as e:
		print('Error:', e)
	finally:
		print('finally...')

En otras palabras, no hay necesidad de detectar errores en todos los lugares posibles, siempre que se detecten en el nivel apropiado. De esta manera, la molestia de intentar escribir ... excepto ... finalmente se reduce enormemente.

Pila de llamadas

Si no se detecta el error, se lanzará hacia arriba y, finalmente, el intérprete de Python lo detectará, se imprimirá un mensaje de error y el programa se cerrará. Eche un vistazo a err.py:

# err.py
def foo(s):
	return 10 / int(s)

def bar(s):
	return foo(s) * 2

def main():
	bar('0')

main()

El resultado de la ejecución es el siguiente
, se lanza de abajo hacia arriba, al lugar equivocado

$ python3 err.py
Traceback (most recent call last):
  File "err.py", line 11, in <module>
    main()
  File "err.py", line 9, in main
    bar('0')
  File "err.py", line 6, in bar
    return foo(s) * 2
  File "err.py", line 3, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero

No es terrible cometer errores, lo terrible es que no sabes qué salió mal. Interpretar la información errónea es la clave para localizar el error. Podemos ver toda la cadena de funciones de llamada incorrecta de arriba a abajo :

Línea de mensaje de error 1:

Traceback (most recent call last):

El retroceso
nos dice que se trata de información de seguimiento incorrecta.
No veo el mensaje de error. Cuando vea inglés, sus ojos se oscurecerán y comenzará a pensar, Baidu o algo así, tómese su tiempo.

Líneas 2 ~ 3:

  File "err.py", line 11, in <module>
    main()

Se produjo un error al llamar a la función man (). El código está en la línea 11 del archivo de código err.py, pero el motivo es la línea 9:

File "err.py", line 9, in main
    bar('0')

Se produjo un error al llamar a bar ('0'). El código está en la línea 9 del archivo de código err.py, pero la razón es la línea 6:

File "err.py", line 6, in bar
    return foo(s) * 2

La razón es que la declaración "return foo (s) * 2" es incorrecta, pero esta no es la razón final, así que continúe mirando hacia abajo:

  File "err.py", line 3, in foo
    return 10 / int(s)

La razón es que la declaración return 10 / int (s) es incorrecta. Esta es la fuente del error, porque se imprime lo siguiente:

ZeroDivisionError: integer division or modulo by zero

De acuerdo con el tipo de error ZeroDivisionError, juzgamos que no hay ningún error en int (s) en sí, pero int (s) devuelve 0, y se produjo un error al calcular 10/0. Hasta ahora, se ha encontrado la fuente del error. .

Recuerde recordar :
Cuando ocurre un error, se debe analizar la información de la pila de llamadas incorrecta para ubicar la ubicación del error.

Error de registro

Si no detecta el error, naturalmente puede dejar que el intérprete de Python imprima la pila de errores, pero el programa también finaliza. Ahora que podemos detectar el error, podemos imprimir la pila de errores, analizar la causa del error y dejar que el programa continúe ejecutándose.

El módulo de registro incorporado de Python puede registrar mensajes de error muy fácilmente:

# err_logging.py

import logging

def foo(s):
	return 10 / int(s)

def bar(s):
	return foo(s) * 2

def main():
	try:
		bar('0')
	except Exception as e:
		logging.exception(e)

main()
print('END')

Se produce el mismo error, pero el programa continuará ejecutándose después de imprimir el mensaje de error y saldrá normalmente:

$ python3 err_logging.py
ERROR:root:division by zero
Traceback (most recent call last):
  File "err_logging.py", line 13, in main
    bar('0')
  File "err_logging.py", line 9, in bar
    return foo(s) * 2
  File "err_logging.py", line 6, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero
END

Mediante la configuración, el registro también puede registrar errores en un archivo de registro para facilitar la resolución de problemas posteriormente.

Error de lanzamiento

Debido a que el error es una clase, capturar un error significa capturar una instancia de la clase. Por lo tanto, los errores no se crean de la nada, sino que se crean y lanzan intencionalmente . Las funciones integradas de Python pueden generar muchos tipos de errores, y las funciones escritas por nosotros mismos también pueden generar errores.

Si desea generar un error, primero puede definir una clase de error de acuerdo con sus necesidades, elegir una buena relación de herencia y luego usar la declaración de aumento para generar una instancia de error:

class FooError(ValueError):
	pass

def foo(s):
	n = int(s)
	if n == 0:
		raise FooError('invalid value: %s' % s)
	return 10 / n

foo('0')

Ejecución, finalmente podemos rastrear el error definido por nosotros mismos:

$ python3 err_raise.py 
Traceback (most recent call last):
  File "err_throw.py", line 11, in <module>
    foo('0')
  File "err_throw.py", line 8, in foo
    raise FooError('invalid value: %s' % s)
__main__.FooError: invalid value: 0

Solo defina nuestros propios tipos de error cuando sea necesario. Si puede elegir los tipos de error incorporados existentes de Python (como ValueError, TypeError), intente utilizar los tipos de error incorporados de Python.

Finalmente, veamos otra forma de manejo de errores:

# err_reraise.py

def foo(s):
	n = int(s)
	if n == 0:
		raise ValueError('invalid value: %s' % s)
	return 10 / n

def bar():
	try:
		foo('0')
	except ValueError as e:
		print('ValueError!')
		raise

bar()

En la función bar (), claramente hemos detectado el error, ¡pero imprimimos un ValueError! Más tarde, el error se eliminó a través de la declaración de aumento. ¿No es una enfermedad?

De hecho, este método de manejo de errores no solo está libre de enfermedades, sino que también es bastante común. El propósito de capturar errores es simplemente registrar para seguimiento de seguimiento. Sin embargo, dado que la función actual no sabe cómo manejar el error, la forma más apropiada es continuar lanzándolo y dejar que la persona que más llama lo maneje. Por ejemplo, cuando un empleado no puede manejar un problema, se lo arroja a su jefe. Si su jefe no puede manejarlo, simplemente lo sigue vomitando y finalmente se lo envía al CEO.

Si la declaración de aumento no tiene parámetros, arrojará el error actual tal como está. Además, generar un error en el ejercicio también puede convertir un tipo de error en otro tipo:

try:
	10 / 0
except ZeroDivisionError:
	raise ValueError('input error')

Siempre que sea una lógica de conversión razonable, nunca debe convertir un IOError en un ValueError irrelevante.

Ejercicio

Ejecute el siguiente código para analizar de acuerdo con la información anormal, localice la fuente del error y corríjalo:

from functools import reduce

def str2num(s):
    return int(s)

def calc(exp):
    ss = exp.split('+')
    ns = map(str2num, ss)
    return reduce(lambda acc, x: acc + x, ns)

def main():
    r = calc('100 + 200 + 345')
    print('100 + 200 + 345 =', r)
    r = calc('99 + 88 + 7.6')
    print('99 + 88 + 7.6 =', r)

main()

Ejecute directamente aquí para ver el error

100 + 200 + 345 = 645
Traceback (most recent call last):
  File "E:\Program Files\pythonProject\test\main.py", line 17, in <module>
    main()
  File "E:\Program Files\pythonProject\test\main.py", line 14, in main
    r = calc('99 + 88 + 7.6')
  File "E:\Program Files\pythonProject\test\main.py", line 9, in calc
    return reduce(lambda acc, x: acc + x, ns)
  File "E:\Program Files\pythonProject\test\main.py", line 4, in str2num
    return int(s)
ValueError: invalid literal for int() with base 10: ' 7.6'

Análisis: arrojar párrafos. Finalmente, el tipo int solo se puede convertir a números enteros por defecto, y no se puede convertir a cadenas con decimales. Por lo tanto, es necesario determinar si la cadena s solo está compuesta de números. De lo contrario, convierta que flote.

from functools import reduce


def str2num(s):
    if s.isdigit():
        return int(s)
    return float(s)


def calc(exp):
    # 通过+号来将数字分开来,并获得一个数字列表
    ss = exp.split('+')
    # 将列表中的字符串用map函数映射str2num方法,将字符串转为int
    # 但是int()不能转带小数的字符串比如7.6
    ns = map(str2num, ss)
    return reduce(lambda acc, x: acc + x, ns)


def main():
    r = calc('100 + 200 + 345')
    print('100 + 200 + 345 =', r)
    r = calc('99 + 88 + 7.6')
    print('99 + 88 + 7.6 =', r)

*

main()


resumen

El intento incorporado de Python ... excepto ... finalmente es muy conveniente para manejar errores. Cuando ocurre un error, lo más importante es analizar el mensaje de error y ubicar la ubicación del código donde ocurrió el error.

El programa también puede lanzar errores de forma activa y dejar que la persona que llama maneje los errores correspondientes. Sin embargo, debe estar escrito claramente en el documento qué errores pueden arrojarse y las razones de los errores.

Supongo que te gusta

Origin blog.csdn.net/qq_44787943/article/details/112563111
Recomendado
Clasificación