6

estoy introduciéndome en el mundo de Python y tengo una pregunta que me ha surgido, he estado mirando por ahí pero no he encontrado nada que me convenza y espero que aquí se me aclare la dura.

La pregunta es sobre el tema de las excepciones en Python y como lanzarlas y capturarlas después.

Pongo un ejemplo en Java y quiero saber como se podría hacer en Python:

public void setEdad(int edad) {
if (edad <= 0)
    throw new Exception("La edad debe ser positiva.");
this.edad = edad;
}

Y luego antes de asignar un valor la capturo:

try {
    Persona persona = new Persona();
    persona.setEdad(-10);
} catch (Exception e) {
    System.out.println(e.getMessage());
}

Es un ejemplo de prueba para mostrar que es lo que quiero conseguir.

¿Como podría hacer eso en Python y poder lanzar excepciones como se hace en java?

Un saludo y gracias.

RodriKing
  • 649
  • 4
  • 10
  • 22

3 Answers3

5

Para este tipo de excepciones debidas a pasar un argumento inválido a un método/función se suele usar ValueError o una sublclase de él. Lo más simple es:

class Persona:
    def setEdad(self, edad):
        if (edad <= 0):
            raise ValueError("La edad debe ser positiva.")
        self.edad = edad

try:
    persona =  Persona()
    persona.setEdad(-10)
except ValueError as e:
    print(e)

Normalmemte prefiero crear una clase personalizada para esto, lo cual te da mucha más libertad y claridad al código:

class MyAppValueError(ValueError):
    def __init__(self, message, *args):         
        super(MyAppValueError, self).__init__(message, *args)


class Persona:
    def setEdad(self, edad):
        if (edad <= 0):
            raise MyAppValueError("La edad debe ser positiva.")
        self.edad = edad


try:
    persona =  Persona()
    persona.setEdad(-10)
except MyAppValueError as e:
    print(e)

En el except puedes capturar todas las excepciones y no solo la personalizada usando Exception:

except Exception as e:
    print(e)
FJSevilla
  • 55,603
  • 7
  • 35
  • 58
  • Muchas gracias, era el ejemplo que buscaba. Y con el método def __init__(self): vendría a ser el constructor de la clase, no? – RodriKing May 28 '17 at 18:51
  • @RodriKing se podria llamar así, hize una respuesta en su dia sobre eso: https://es.stackoverflow.com/a/63451/15089. A rasgos generales podemos decir que es el "equivalente" a lo que entendemos por constructor en otros lenguajes. – FJSevilla May 28 '17 at 18:57
  • Gracias muy buena esa respuesta también, la verdad es que de esa información es la que necesito. Puedes recomendarme algún manual, o algo (a parte de la documentación oficial) que trate esos temas. Como los constructores, que las listas se pasar por referencia, que las tuplas son inmutables, etc. Un saludo. – RodriKing May 30 '17 at 17:26
  • @RodriKing la verdad es que la documentación es muy buena incluyendo ejemplos en casi todos los casos. Aparte de esto hay muchas otras fuentes de información, unas más amplias que otras dependiendo de cada tema concreto. Te puedes mirar el libro ['Python para todos'](https://launchpadlibrarian.net/18980633/Python%20para%20todos.pdf) disponible con descarga gratuita. Si entras en la información de la etiqueta [tag:python] tienes otras fuentes. Por otro lado, se puede decir que todos los parámetros se pasan por referencia en Python , lo que difiere es si el objeto es mutable o inmutable.Saludos – FJSevilla May 30 '17 at 17:45
4

Te han dado ya respuestas correctas a tu pregunta. Aunque la pregunta era sobre cómo lanzar excepciones y cómo interceptarlas, para casos como tu ejemplo sería mejor usar "asserts":

class Persona:
    def setEdad(self, edad):
        assert edad > 0, "La edad debe ser positiva."
        self.edad = edad

Si se incumple la condición, se laza una excepción AssertionError que podría interceptarse. Pero interceptar excepciones sólo tiene sentido si lo que quieres es corregir el error y conseguir que programa continúe. Si ése fuera el caso, lo mejor es hacer la corrección y evitar lanzar excepciones.

La ventaja de usar asserts es que "desaparecen" cuando se optimiza el código (parámetro -O). Puede usarlos tranquilamente para asegurar las pruebas en desarrollo sin temor a que ralentice la versión final optimizada cuando la despliegues en producción.

ChemaCortes
  • 8,325
  • 18
  • 36
3

Una Excepción la puedes lanzar mediante raise, te comentó también por si no lo sabías, que en python no son necesarios los métodos "getters" o "setters" de Java, obviamente si, si requieres hacer una validación sobre un valor, pero de todas formas no deja de ser una convención y no existe restricción del lenguaje al respecto, siempre podrías estar haciendo objeto.edad = -1 y sería totalmente válido. Para acercarse a la mecánica de Java, es conveniente usar los decoradores @Property y @Prop.setter

class Persona():

  def __init__(self):
    self._edad = None

  @property
  def edad(self):
    """getter"""
    return self._edad

  @edad.setter
  def edad(self, edad):
    """setter"""
    if edad <= 0:
       raise ValueError("La edad debe ser positiva.")

    self._edad = edad


p = Persona()
print(p.edad)

try:
  p.edad = -1
except ValueError as e:
  print(e)

print(p.edad)
Patricio Moracho
  • 54,367
  • 12
  • 35
  • 68
  • Muchas gracias, era el ejemplo que buscaba. Y tendré en cuenta lo de @ Property y @ Prop.setter. Un saludo. – RodriKing May 28 '17 at 18:54
  • Hola buenas, me he estado informando más sobre el tema que comentas. Y tengo unas preguntas que espero que no te importe en responder. 1.- ¿Los decoradores que comentas, al hacer p.edad = "un valor" se llaman solos sin hacer nada, osea que capturaría la excepción al cambiarlo? 2.- ¿Es lo mismo utilizar decoradores @ que utilizar property()? ¿Qué sería mejor? @Patricio Moracho – RodriKing Jun 01 '17 at 19:35
  • 1. El "decorador simplemente modifica el comportamiento de la función y permite hacer p.edad = -1", en el ejemplo (solo se controla valores inferiores a 1) lanzaría la excepción. Si vos queres pasale un String debes controlar también en la función que el valor recibido en edad sea numérico, caso contrario lanzas otra excepción "El valor de edad debe ser númerico. La captura de estas excepciones las haces afuera. 2. @property es el decorador para el "getter", sirve para poder hacer edad = p.edad, por ejemplo, son dos cosas distintas. Saludos – Patricio Moracho Jun 01 '17 at 21:14