Ver índice de contenidos del libro

10.1. Pre y Postcondiciones

Cuando hablamos de contratos o programación por contratos, nos referimos a la necesidad de estipular tanto lo que necesita como lo que devuelve nuestro código.

Las condiciones que deben estar dadas para que el código funcione las llamamos precondiciones y las condiciones sobre el estado en que quedan las variables y él o los valores de retorno, las llamamos postcondiciones.

En definitiva, este concepto es similar al ya mencionado con respecto a la documentación de funciones, es decir que se debe documentar cómo deben ser los parámetros recibidos, cómo va a ser lo que se devuelve, y qué sucede con los parámetros en caso de ser modificados.

Esta estipulación es mayormente para que la utilicen otros programadores, por lo que es particularmente útil cuando se encuentra dentro de la documentación. En ciertos casos, además, puede quererse que el programa revise si las condiciones realmente se cumplen y de no ser así, actúe en consecuencia.

Existen herramientas en algunos lenguajes de programación que facilitan estas acciones, en el caso de Python, es posible utilizar la instrucción assert.

10.1.1. Precondiciones

Las precondiciones son las condiciones que deben cumplir los parámetros que una función recibe, para que esta se comporte correctamente.

Por ejemplo, en una función división las precondiciones son que los parámetros son números, y que el divisor sea distinto de 0. Tener una precondición permite asumir desde el código que no es necesario lidiar con los casos en que las precondiciones no se cumplen.

10.1.2. Postcondiciones

Las postcodiciones son las condiciones que cumplirá el valor de retorno, y los parámetros recibidos, en caso de que hayan sido alterados, siempre que se hayan cumplido las precondiciones.

En el ejemplo anterior, la función división con las precondiciones asignadas, puede asegurar que devolverá un número correspondiente al cociente solicitado.

10.1.3. Aseveraciones

Tanto las precondiciones como las postcondiciones son aseveraciones (en inglés assert). Es decir, afirmaciones realizadas en un momento particular de la ejecución sobre el estado computacional. Si llegaran a ser falsas significaría que hay algún error en el diseño o utilización del algoritmo.

Para comprobar estas afirmaciones desde el código en algunos casos podemos utilizar la instrucción assert, está instrucción recibe una condición a verificar y, opcionalmente, un mensaje de error que devolverá en caso que la condición no se cumpla.

>>> n=0
>>> assert n!=0, "El divisor no puede ser 0"
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
AssertionError: El divisor no puede ser 0

Advertencia Es importante tener en cuenta que assert está pensado para ser usado en la etapa de desarrollo. Un programa terminado nunca debería dejar de funcionar por este tipo de errores.

10.1.4. Ejemplos

Usando los ejemplos anteriores, la función division nos quedaría de la siguiente forma:

def division(dividendo, divisor):
    """ Calculo de la división
    Pre: Recibe dos números, divisor debe ser distinto de 0.
    Post: Devuelve un número real, con el cociente de ambos.
    """
    assert divisor != 0, "El divisor no puede ser 0"
    return dividendo / ( divisor * 1.0 )

Otro ejemplo, tal vez más interesante, puede ser una función que implemente una sumatoria. En este caso hay que analizar cuáles van a ser los parámetros que recibirá la función, y las precondiciones que estos parámetros deberán cumplir.

La función sumatoria a escribir, necesita de un valor inicial, un valor final, y una función a la cual llamar en cada paso. Es decir que recibe tres parámetros.

def sumatoria(inicial, final, f):

Tanto inicial como final deben ser números enteros, y dependiendo de la implementación a realizar o de la especificación previa, puede ser necesario que final deba ser mayor o igual a inicial.

Con respecto a f, se trata de una función que será llamada con un parámetro en cada paso y se requiere poder sumar el resultado, por lo que debe ser una función que reciba un número y devuelva un número.

La declaración de la función queda, entonces, de la siguiente manera.

def sumatoria(inicial, final, f):
    """Calcula la sumatoria desde i=inicial hasta final de f(i)
    Pre: inicial y final son números enteros, f es una función que
         recibe un entero y devuelve un número.
    Post: Se devuelve el valor de la sumatoria de aplicar f a cada
          número comprendido entre inicial y final.
    """

Ejercicio 10.1.1. Realizar la implementación correspondiente a la función sumatoria.

En definitiva, la documentación de pre y postcondiciones dentro de la documentación de las funciones es una forma de especificar claramente el comportamiento del código de forma que quienes lo vayan a utilizar no requieran conocer cómo está implementado para poder aprovecharlo.

Esto es útil incluso en los casos en los que el programador de las funciones es el mismo que el que las va a utilizar, ya que permite separar responsabilidades. Las pre y postcondiciones son, en efecto, un contrato entre el código invocante y el invocado.

Copyright (c) 2011-2014 Rosita Wachenchauzer, Margarita Manterola, Maximiliano Curia, Marcos Medrano, Nicolás Paez. La copia y redistribución de esta página se permite bajo los términos de la licencia Creative Commons Atribución - Compartir Obras Derivadas Igual 3.0 siempre que se conserve esta nota de copyright.