Su interés en el nuevo libro " Python Pro Secrets " nos ha convencido de que la historia de las peculiaridades de Python merece continuar. Hoy ofrecemos leer un pequeño tutorial sobre la creación de clases de excepción personalizadas (en el texto, las suyas propias). El autor lo entendió interesante, es difícil no estar de acuerdo con él en que la ventaja más importante de una excepción es la integridad y claridad del mensaje de error. Parte del código del original tiene forma de imágenes.
Bienvenido a cat.
Creando sus propias clases de error
Python ofrece la posibilidad de crear sus propias clases de excepción. Al crear dichas clases, puede diversificar el diseño de las clases en su aplicación. Una clase personalizada de errores podría registrar errores, inspeccionar el objeto. Somos nosotros quienes definimos lo que hace la clase de excepción, aunque normalmente una clase personalizada difícilmente puede hacer más que mostrar un mensaje.
Naturalmente, el tipo de error en sí es importante y, a menudo, creamos nuestros propios tipos de error para indicar una situación específica que normalmente no se cubre en el nivel de Python. De esta manera, los usuarios de la clase sabrán exactamente qué está sucediendo cuando encuentren este error.
Este artículo está dividido en dos partes. Primero, definiremos una clase de excepción por sí misma. Luego demostramos cómo podemos integrar nuestras propias clases de excepción en nuestros programas de Python y mostramos cómo podemos mejorar así la usabilidad de las clases que diseñamos.
Clase de excepción personalizada MyCustomError
Lanzar una excepción requiere los métodos
__init__()
y
__str__()
.
Al lanzar una excepción, ya creamos una instancia de la excepción y al mismo tiempo la mostramos en la pantalla. Echemos un vistazo más de cerca a nuestra clase de excepción personalizada que se muestra a continuación.
La clase MyCustomError anterior tiene dos métodos mágicos,
__init__
y
__str__
, que se llaman automáticamente durante el manejo de excepciones. Se
Init
llama al método cuando se crea la instancia y
str
se llama al método cuando se muestra la instancia . Por lo tanto, cuando se lanza una excepción, estos dos métodos se suelen llamar inmediatamente uno después del otro. La declaración de lanzamiento de excepción de Python pone un programa en un estado de error.
En la lista de argumentos del método
__init__
es
*args
. Un componente
*args
es un modo de coincidencia de patrones especial que se utiliza en funciones y métodos. Le permite pasar múltiples argumentos y almacena los argumentos pasados como una tupla, pero al mismo tiempo le permite no pasar argumentos en absoluto.
En nuestro caso, podemos decir que si
MyCustomError
se pasa algún argumento al constructor , entonces tomamos el primer argumento pasado y lo asignamos a un atributo
message
en el objeto. Si no se pasaron argumentos, al atributo
message
se le asignará un valor
None
.
En el primer ejemplo, la excepción
MyCustomError
se lanza sin ningún argumento, por lo que el atributo
message
a este objeto se le asigna un valor
None
. Se llamará a un método
str
y mostrará el mensaje 'Se ha generado el mensaje MyCustomError'.
La excepción
MyCustomError
se lanza sin ningún argumento (los paréntesis están vacíos). En otras palabras, este diseño de objeto no parece estándar. Pero esto es solo soporte sintáctico proporcionado en Python cuando se lanza una excepción.
En el segundo ejemplo, se
MyCustomError
pasa con un argumento de cadena 'Tenemos un problema'. Se establece como un atributo del
message
objeto y se muestra como un mensaje de error cuando se lanza una excepción.
El código para la clase de excepción MyCustomError está aquí...
class MyCustomError(Exception):
def __init__(self, *args):
if args:
self.message = args[0]
else:
self.message = None
def __str__(self):
print('calling str')
if self.message:
return 'MyCustomError, {0} '.format(self.message)
else:
return 'MyCustomError has been raised'
# MyCustomError
raise MyCustomError('We have a problem')
Clase CustomIntFloatDic
Creamos nuestro propio diccionario, cuyos valores solo pueden ser enteros y números de coma flotante.
Sigamos adelante y demostremos cómo inyectar clases de error de manera fácil y útil en nuestros propios programas. Para empezar, ofreceré un ejemplo ligeramente elaborado. En este ejemplo ficticio, crearé mi propio diccionario que solo puede aceptar números enteros o de coma flotante.
Si el usuario intenta especificar cualquier otro tipo de datos como valor en este diccionario, se lanzará una excepción. Esta excepción proporcionará información útil al usuario sobre cómo utilizar este diccionario. En nuestro caso, este mensaje informa directamente al usuario que solo los números enteros o de coma flotante se pueden especificar como valores en este diccionario.
Al crear su propio diccionario, tenga en cuenta que hay dos lugares donde se pueden agregar valores al diccionario. En primer lugar, esto puede suceder en el método init al crear un objeto (en esta etapa, al objeto ya se le pueden asignar claves y valores) y, en segundo lugar, al configurar claves y valores directamente en el diccionario. En ambos lugares, debe escribir código para asegurarse de que el valor solo pueda ser de tipo
int
o
float
.
Primero, definiré la clase CustomIntFloatDict que hereda de la clase incorporada
dict
.
dict
se pasa en una lista de argumentos, que están entre paréntesis y siguen al nombre de la clase
CustomIntFloatDict
.
Si se crea una instancia de una clase
CustomIntFloatDict
Además, no se pasan argumentos a los parámetros de clave y valor, se establecerán en
None
. La expresión se
if
interpreta de la siguiente manera: si la clave es igual
None
o el valor es igual
None
, se llamará a un método con el objeto
get_dict()
, que devolverá el atributo
empty_dict
; tal atributo en un objeto apunta a una lista vacía. Recuerde que los atributos de clase están disponibles en todas las instancias de la clase.
El propósito de esta clase es permitirle al usuario pasar una lista o tupla con las claves y valores dentro. Si el usuario ingresa a una lista o tupla buscando claves y valores, entonces estos dos conjuntos iterables se concatenarán usando la función
zip
el lenguaje Python. Una variable enganchada que apunta a un objeto
zip
es iterable y las tuplas no se pueden empaquetar. Al iterar sobre las tuplas, verifico si val es una instancia de la clase
int
o
float
. Si
val
no pertenece a ninguna de estas clases, lanzo
IntFloatValueError
mi propia excepción y le paso val como argumento.
Clase de excepción IntFloatValueError
Cuando se lanza una excepción,
IntFloatValueError
creamos una instancia de la clase
IntFloatValueError
y la mostramos simultáneamente en la pantalla. Esto significa que los métodos mágicos
init
y serán llamados
str
.
El valor que provocó la excepción lanzada se establece como un atributo que
value
acompaña a la clase
IntFloatValueError
. Al llamar al método magic str, el usuario recibe un mensaje de error que informa que el valor
init
de
CustomIntFloatDict
no es válido. El usuario sabe qué hacer para corregir este error.
Clases de excepción
IntFloatValueError
y
KeyValueConstructError
si no se lanza ninguna excepción, es decir, todas
val
del objeto encadenado son de tipos
int
o
float
, luego se establecerán usando
__setitem__()
, y el método de la clase padre hará todo por nosotros
dict
, como se muestra a continuación.
Clase KeyValueConstructError
¿Qué sucede si el usuario ingresa un tipo que no es una lista o una tupla de claves y valores?
Nuevamente, este ejemplo es un poco artificial, pero es útil para mostrarle cómo puede usar sus propias clases de excepción.
Si el usuario no especifica las claves y valores como una lista o una tupla, se lanzará una excepción
KeyValueConstructError
. El propósito de esta excepción es informar al usuario que para escribir claves y valores en un objeto
CustomIntFloatDict
, se debe especificar una lista o tupla en el constructor de la
init
clase
CustomIntFloatDict
.
En el ejemplo anterior, como segundo argumento del constructor
init
se pasó mucho y se lanzó una excepción debido a esto
KeyValueConstructError
. La ventaja del mensaje de error que se muestra es que el mensaje de error que se muestra informa al usuario que las claves y los valores que se deben insertar deben informarse como una lista o una tupla.
Nuevamente, cuando se lanza una excepción, se crea una instancia de KeyValueConstructError y la clave y los valores se pasan como argumentos al constructor KeyValueConstructError. Se establecen como los valores de los atributos de clave y valor de KeyValueConstructError y se utilizan en el método __str__ para generar un mensaje de error significativo cuando el mensaje se muestra en la pantalla.
Además, incluso incluyo los tipos de datos inherentes a los objetos agregados al constructor
init
- Hago esto por claridad.
Configuración de clave y valor en CustomIntFloatDict
CustomIntFloatDict
hereda de
dict
. Esto significa que funcionará exactamente como un diccionario, excepto en los lugares que elijamos para cambiar selectivamente su comportamiento.
__setitem__
Es un método mágico llamado al establecer una clave y un valor en un diccionario. En nuestra implementación,
setitem
verificamos que el valor sea de tipo
int
o
float
, y solo después de una verificación exitosa se puede establecer en el diccionario. Si la comprobación falla, puede volver a utilizar la clase de excepción
IntFloatValueError
. Aquí podemos asegurarnos de que obtendremos una excepción al intentar establecer una cadena
‘bad_value’
como valor en el diccionario
test_4
.
Todo el código de este tutorial se muestra a continuación y se publica en Github .
# , int float
class IntFloatValueError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return '{} is invalid input, CustomIntFloatDict can only accept ' \
'integers and floats as its values'.format(self.value)
class KeyValueContructError(Exception):
def __init__(self, key, value):
self.key = key
self.value = value
def __str__(self):
return 'keys and values need to be passed as either list or tuple' + '\n' + \
' {} is of type: '.format(self.key) + str(type(self.key)) + '\n' + \
' {} is of type: '.format(self.value) + str(type(self.value))
class CustomIntFloatDict(dict):
empty_dict = {}
def __init__(self, key=None, value=None):
if key is None or value is None:
self.get_dict()
elif not isinstance(key, (tuple, list,)) or not isinstance(value, (tuple, list)):
raise KeyValueContructError(key, value)
else:
zipped = zip(key, value)
for k, val in zipped:
if not isinstance(val, (int, float)):
raise IntFloatValueError(val)
dict.__setitem__(self, k, val)
def get_dict(self):
return self.empty_dict
def __setitem__(self, key, value):
if not isinstance(value, (int, float)):
raise IntFloatValueError(value)
return dict.__setitem__(self, key, value)
#
# test_1 = CustomIntFloatDict()
# print(test_1)
# test_2 = CustomIntFloatDict({'a', 'b'}, [1, 2])
# print(test_2)
# test_3 = CustomIntFloatDict(('x', 'y', 'z'), (10, 'twenty', 30))
# print(test_3)
# test_4 = CustomIntFloatDict(('x', 'y', 'z'), (10, 20, 30))
# print(test_4)
# test_4['r'] = 1.3
# print(test_4)
# test_4['key'] = 'bad_value'
Conclusión
Si crea sus propias excepciones, trabajar con la clase se vuelve mucho más conveniente. La clase de excepción debe tener métodos mágicos
init
y
str
que se invoquen automáticamente durante el manejo de excepciones. Depende completamente de usted lo que hará su propia clase de excepción. Entre los métodos mostrados se encuentran los que se encargan de inspeccionar un objeto y mostrar un mensaje de error informativo en la pantalla.
Sea como sea, las clases de excepción hacen que sea mucho más fácil manejar cualquier error que surja.