User Tools

Site Tools


apuntes:python

Programación con Python

Este anexo no proporciona todo lo necesario para aprender a programar desde cero sino proporcionar algunas explicaciones y ejemplos para comprender las diferencias que puede haber con respecto a Java, que es el lenguaje que ya conocéis. Así, en los diferentes apartados se encuentran, sobre todo, ejemplos y estructuras muy utilizadas. Estos ejemplos y estructuras nos permitirán, con las explicaciones y ejercicios de clase, empezar a programar en este lenguaje para abordar más adelante el desarrollo de aplicaciones con el framework Django.

Si se busca algo más de detalle sobre algún aspecto del lenguaje se pueden consultas los materiales de referencia donde hay tutoriales y libros mucho más completos donde se explica como programar en Python con todo detalle.

Estructura de un programa en Python

Antes de comenzar a ver los diferentes aspectos del lenguaje Python vamos a analizar algunos fragmentos de código de forma que nos ayudarán a echar un vistazo rápido a todo el lenguaje, aunque luego nos adentremos con más detalle en cada parte.

Teniendo ya nociones de programacíón y además de un lenguaje Orientado a Objetos como Java será bastante sencillo hacerse una idea de como es Python puesto que también se trata de un lenguaje de Programación Orientado a Objetos y posee muchas características similares, por lo que la transición a este nuevo lenguaje será muy rápida.

Python dispone de una Guia de estilo que conviene leerse para adecuarse a las convenciones que todo el mundo tiene que adoptar.

# Programa de ejemplo que lee el contenido de un directorio 
# y lo imprime por pantalla
import os
 
PATH = '.'
 
def leer_ficheros(directorio=None):
    '''
    Lee un directorio y devuelve el listado de ficheros del mismo
    :return: El listado de ficheros
    '''
    if directorio:
        ruta = directorio
    else:
        ruta = PATH
 
    print("Leyendo los ficheros de %s" % ruta)
    ficheros = os.listdir(ruta)
    print("Se ha terminado de leer", ruta)
 
    return ficheros
 
def listar_ficheros(ficheros):
    '''
    Imprime por pantalla un listado de ficheros
    :param ficheros: 
    '''
    for fichero in ficheros:
        print(fichero)
 
def otro_metodo():
    pass        # Pendiente de implementar
 
listar_ficheros(leer_ficheros())

Viendo el ejemplo anterior podemos hacernos una idea de cómo es la sintaxis más básica de Python. Conviene además tener en cuenta los siguientes aspectos:

  • Sólo se permite una instrucción por línea y no hay que marcar el final con ningún caracter
  • La indentación del código marca el ámbito por lo que no es simple adorno
  • Puesto que la indentación del código marca el ámbito no hay caracter de inicio y fin del mismo
  • No hay que declarar las variable. Python decidirá el tipo de las mismas según el valor asignado
  • Se puede comprobar si existe una variable comprobando si está definida en el diccionario locals()
>>> nombre = 'Santi'
>>> 'nombre' in locals()
True 
  • Para los nombres compuestos de elementos del código se usa el estilo listar_ficheros, palabras en minúscula y separadas por un guión bajo
  • El objeto nulo viene representado por la palabra reservada None. Hay que tener en cuenta que no es lo mismo que una variable contenga el valor nulo a que esa variable no haya sido declarada
>>> nombre = None
>>> nombre
>>>

Asi, si la variable no se ha declarado y se intenta hacer referencia a ella este será el error que aparecerá

>>> nombre
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'nombre' is not defined
  • Técnicamente no hay diferencia entre comillas dobles () y simples (') pero normalmente las primeras se usan para hacer referencia a contenido que es visible para el usuario y las segundas para el resto de casos
cadena = 'Texto de la cadena'
print("La cadena es %s", cadena)
  • Podemos definir métodos con parámetros opcionales simplemente dándoles un valor por defecto en la propia definición del método
. . .
def sumar_numeros(a, b=0, c=0):
    return a + b + c
. . .
>>> sumar_numeros(1)
1
>>> sumar_numeros(1, 4)
5
>>> sumar_numeros(1, 5, 7)
13

Cómo ejecutar un programa

Puesto que Python es un lenguaje interpretado, por un lado no tenemos que compilar el código y por otro lado tampoco podemos ejecutarlos directamente. Es por eso que tenemos que utilizar el intérprete de comandos y para como argumento el fichero de código que queramos que se ejecute.

santi@zenbook:$ python3 programa.py

También es posible ejecutar los scripts de Python directamente desde la línea de comandos. Simplemente tenemos que poner la línea #!/usr/bin/python3 al inicio del script para que podamos ejecutarlo sin utilizar el intérprete explicitamente. Por ejemplo, si tenemos el siguiente script de ejemplo:

hello.py
#!/usr/bin/python3
 
print("Hello!")

Dándole permisos de ejecución (en el caso de sistemas Linux/OS X), podremos ejecutarlo directamente.

santi@zenbook:$ chmod u+x hello.py
santi@zenbook:$ ./hello.py
Hello!

Entrada por teclado

Podemos solicitar al usuario la entrada de información por teclado mediante la instrucción input(mensaje)

>>> nombre = input("Introduce tu nombre")
Introduce tu nombre Santiago
>>> apellidos = input("Introduce tu apellido")
Introduce tu apellido Faci
>>> print("Te llamas", nombre, apellidos)
Te llamas Santiago Faci

También es posible hacer que la entrada del usuario se evalúe como una expresión más y no tal cual la introduzca.

>>> suma = input("Introduce una suma")
Introduce una suma 3 + 5
>>> print(suma)
3 + 5
>>> print(eval(suma))
8

Operadores

Operador Descripción Ejemplo
+, - , Suma, Resta 3 + 5, 3 - 2
* ,/, % Multiplicación, División, Módulo 12 * 3, 5 / 6, 12 % 3
// División truncada (resultado entero) 9 // 2 = 4
** Exponente 5**2 = 25
+=, -= Suma/Resta y asignación x += 2 –> x = x + 2
*=, /= Multiplicación/División y asignación x *= 2 –> x = x * 2
**=, //= Exponente/División truncada y asignación x //= 2 –> x = x // 2
or, and, not Operadores lógicos if not terminado:
in Está en la lista if 2 in [1, 2, 3]:
<, ⇐, >, >=, !=, == Operadores de comparación if x != y:

Sentencias de control

if <condicion1>:
    <sentencias1>
elif <condicion2>
    <sentencias2>
. . .
else:
    <sentencias_else>
 
while <condicion>:
    <sentencias>
    # En cualquier momento podemos romper el bucle
    [break]
[else:
    # Sólo irá aqui si el bucle termina de forma natural
    <sentencias_finales>]
 
for <variable> in <lista_valores>:
    <sentencias>
    # Opcionalmente podemos romper el bucle
    [break]
    # Opcionalmente podemos continuar con la siguiente iteración
    [continue]
[else:
    <sentencias_finales>]
 

Tipos de Datos

Al no tener que declarar las variables tampoco tenemos que definir manualmente el tipo de las mismas ya que Python decidirá el tipo que convenga en función del valor que le asignemos. Incluso si reusamos una variable asignándole valores de diferentes tipos Python modificará el tipo de dicha variable a medida que sea necesario.

Aún asi, conviene conocer los tipos que existen en el lenguaje puesto que alguna vez nos será útil conocer los valores posibles que puedan tomar e incluso tener que convertir alguna variable a otro tipo para representarla más adelante.

También es muy importante conocer los tipos complejos con los que contamos en Python como listas, diccionarios y otros.

Booleano

El tipo booleano que ya conocemos y que podrá tomar los valores True o False

Número

Fechas

Para las operaciones con fechas es necesario importar los módulos correspondientes

from datetime import datetime, date, time, timedelta
import calendar
. . .
Tipo Descripción
date Almacena una fecha(año-mes-dia)
datetime Almacena una fecha con hora (hora-minuto-segundo-milésimas
time Almacena una hora
calendar

Tanto con tipo date como datetime y time podemos acceder a cada parte de la fecha a través de la propiedad correspondiente y a una serie de métodos que serán de utilidad para trabajar con fechas. A continuación se muestran algunos ejemplos:

# Este ejemplo es válido también para date y time
# siempre y cuando accedamos a una propiedad válida
from datetime import datetime
. . .
>>> fecha = date.today()
>>> fecha_con_hora = datetime.today()
>>> fecha_hora.day
6
>>> fecha_hora.month
9
>>> fecha_hora.year
2017
>>> fecha_hora.hour
18
>>> fecha_hora.minute
19
>>> fecha_hora.second
46
>>> fecha_hora.microsecond
211714
>>> from datetime import time, date, timedelta
>>> hora1 = time(14, 1, 0) # 14:01h
>>> hora2 = time(22, 34, 5) # 22:34h y 5 segundos
>>> hora1 < hora2)
True
>>> fecha1 = date.today()
>>> fecha1
datetime.date(2017, 9, 6)
>>> # Con timedelta podemos sumar days, seconds, hours, . . .
>>> fecha2 = fecha1 + timedelta(days=2) # Suma 2 dias a fecha1
>>> fecha2
datetime.date(2017, 9, 8)
>>> fecha1 < fecha2
True

Datos secuenciales

En Python se consideran datos secuenciales todos aquellos que están compuestos de una secuencia de elementos siempre y cuando éstos estén ordenados y puedan ser accedidos mediante indices.

  • Cadenas de texto
  • Listas
  • Tuplas
Cadenas de texto

Las cadenas de texto se consideran datos secuenciales porque pueden ser tratados como una lista de caracteres y, por tanto, es posible acceder a cada unos de los caracteres por separado

>>> cadena = 'Esto es una cadena de texto'
>>> cadena[1]
's'
>>> cadena[1:4]
'sto'
>>> 'cad' in cadena
True
>>> 'coche' in cadena
False
>>> 'hola' * 4
'holaholaholahola'
>>> 'hola' + 'hola'
'holahola'
Lista

Una lista en Python es un tipo de datos que almacena una serie de elementos bajo las siguientes condiciones:

  • Están ordenadas
  • Pueden contener objetos arbitrarios
  • Se accede a cada elemento por su posición
  • Un elemento podría ser a su vez otra lista
  • Son de tamaño variable
  • Son mutables, los elementos de la lista pueden cambiar

Las listas se representan en Python con una serie de valores entre corchetes [ ] separados por comas ([1, 2, 'Esto es una cadena', 4] es una lista).

Veamos algunos ejemplos de operaciones que podemos realizar con listas:

>>> lista = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> len(lista)
10
>>> lista[0]  # Primer elemento
1
>>> lista[-1]  # Último elemento
10
>>> lista[0:3]  # Tres primeros elementos
[1, 2, 3]
>>> lista[:3]  # Tres primeros elementos
[1, 2, 3]
>>> lista[:-3]  # Todos los elementos menos los 3 últimos
[1, 2, 3, 4, 5, 6, 7]
>>> lista[0:10:3]  # lista[inicio:fin:paso]
[1, 4, 7, 10]
>>> otra_lista = ['un texto', [1, 2, 3], 50]
>>> otra_lista[1][0]
[1]
>>> 2 in lista  # Comprueba si el elemento está en la lista
True
>>> 2 not in lista
False
>>> 2 * lista  # duplica los elementos de la lista
[1, 2, 'Esto es una cadena', 4, 1, 2, 'Esto es una cadena', 4]

Además, también es posible manipular las listas para añadir, eliminar y buscar elementos en la lista

  • append(elemento): Añade un elemento a una lista
  • pop([indice]): Elimina un elemento de una lista. No devuelve nada y lanza una excepción IndexError si la lista está vacía o no hay un elemento en la posición indicada. Si no se pasa ningún parámetro se elimina el último elemento de la lista
  • extend(lista): Añade los elementos de una lista a otra
  • remove(elemento): Elimina la primera aparición de un elemento dado. Devuelve una excepción ValueError en caso de que el elemento no exista
  • index(elemento, [indice_inicial]): Devuelve la posición de un elemento determinado
  • insert(indice, elemento): Inserta un elemento en una posición determinada
>>> lista = [1, 2, 3] 
>>> lista.append(4)  # No devuelve nada
>>> lista
[1, 2, 3, 4]
>>> lista.pop(0) # Elimina y devuelve el primer elemento
1
>>> lista
[2, 3, 4]
>>> lista.pop() # Elimina y devuelve el último elemento
4
>>> lista
[2, 3]
>>> lista2 = [4, 5, 6]
>>> lista.extend(lista2) # Concatena la segunda lista
>>> lista
[2, 3, 4, 5, 6]
>>> lista.extend("hola")
>>> lista
[2, 3, 4, 5, 6, 'h', 'o', 'l', 'a']
>>> lista.remove('h')
>>> lista
[2, 3, 4, 5, 6, 'o', 'l', 'a']
>>> lista.index('o')
5
>>> lista.insert(2, 3.5)
>>> lista
[2, 3, 3.5, 4, 5, 6, 'o', 'l', 'a']

También puede ser útil comenzar definiendo una lista vacía e ir añadiendo elementos durante la ejecución del programa.

>>> lista = []
>>> lista.append(1)
>>> lista
[1]
>>> lista.append(2)
>>> lista
[1, 2]
Tupla

Una tupla es una lista inmutable, es decir, una vez que se crea no se puede modificar y tampoco es posible añadir o eliminar elementos. Por ello presenta algunos beneficios:

  • Son más rapidas que las listas
  • También se utilizan para proteger los datos de cambios accidentales, siempre que estés seguro de que no te interesa que se puedan modificar sus elementos
  • A diferencia de la listas, las tuplas pueden utilizar como claves para los diccionarios

Se diferencia de la lista en que se utilizan los paréntesis (( )) para delimitar sus elementos.

>>> una_tupla = (1, 2, 3)
>>> una_tupla[0]
1
>>> una_tupla[0] = 23
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Diccionario

Un diccionario es una tipo de datos que contiene parejas clave-valor de forma que cada elemento (valor) tiene asociada una clave. Los elementos no están ordenados ya que la forma de hacer referencia a ellos es a través de su clave. Por eso, las claves no se pueden repetir.

Hay que tener en cuenta que cualquier valor puede ser utilizado como elemento pero para las claves sólo pueden ser utilizados tipos de datos inmutables, como por ejemplo las listas o diccionarios. Si que podría usarse, por ejemplo, una tupla como clave en un diccionario puesto que se trata de un tipo de datos inmutable.

En otros lenguajes, los diccionarios de Python, son conocidos como Arrays Asociativos o Mapas

>>> habitantes = {'Huesca':52000, 'Zaragoza':661000, 'Teruel':35000}
>>> habitantes['Huesca']
52000
>>> 'Huesca' in habitantes
True
>>> habitantes['Madrid']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Madrid'

Como ocurre con las lista, también es posible crear un diccionario vacío e ir añadiendo parejas clave-valor

>>> stock = {}
>>> stock
{}
>>> stock['patatas'] = 50
>>> stock
{'patatas':50}
>>> stock['carne'] = 340
>>> stock
{'patatas':50, 'carne':340}

A continuación, algunas de las operaciones que se pueden llevar a cabo sobre un diccionario:

  • len(diccionario): Devuelve el número de parejas clave-valor
  • del diccionario[clave]: Elimina el elemento (junto con su clave) cuya valor se para como parámetro
  • popitem(): Elimina y devuelve una pareja clave-valor aleatoriamente
>>> habitantes = {'Huesca':52000, 'Zaragoza':661000, 'Teruel':35000}
>>> len(habitantes)
3
>>> del habitantes['Huesca']
>>> habitantes
{'Zaragoza':661000, 'Teruel':35000}
>>> habitantes.popitem()
('Teruel', 35000)

También es posible recorrer secuencialmente todas las claves o todos los elementos del diccionario

>>> habitantes = {'Huesca':52000, 'Zaragoza':661000, 'Teruel':35000}
>>> # Recorre todas las claves del diccionario
>>> for ciudad in habitantes:
...     print(ciudad)
...
Huesca
Zaragoza
Teruel
>>> # Recorre todas las claves del diccionario
>>> for ciudad in habitantes.keys():
...     print(ciudad)
...
Huesca
Zaragoza
Teruel
>>> # Recorre todos los elementos del diccionario
>>> for numero in habitantes.values():
...     print(numero)
...
52000
661000
35000

Conversión de tipos

Función Descripción Ejemplo
bool()
float()
int()
long()

Cadenas de texto

Control de excepciones

La estructura del código para capturar excepciones en Python sigue un esquema similar al de Java. La principal diferencia es que dispondremos de una claúsula else para indicar qué queremos hacer en caso de que la excepción no salte.

try:
    # Código que puede producir la excepción
except <NombreException> [as objeto_excepcion]:
    # Código para ejecutar en caso de que se produzca
else:
    # Código que se ejecuta si no salta la excepcion
finally:
    # Código que se ejecuta en cualquier caso

Y a continuación un par de ejemplos de código:

. . .
while True:
    try:
        n = input("Introduce un número: ")
        n = int(n)
        break
    except ValueError:
        print("El número no es válido. Prueba otra vez")
print("El número es correcto")
. . .
. . .
try:
    fichero = open(nombre_fichero, 'r')
except IOError:
    print("No puedo abrir el fichero ", nombre_fichero)
else:
    contenido = fh.readlines()
    fichero.close()
. . .
. . .
if algopasa:
    raise Exception('Ha pasado algo')
. . .
class MiExcepcion(Exception):
    def __init__(self, valor):
        self.valor = valor
    def __str__(self):
        return repr(self.valor)
. . .
try:
    raise MiExcepcion('algopasa ha pasado')
except MiExcepcion as me:
    print("Ha ocurrido un error", e.valor)
. . .

Expresiones Regulares

Programación Orientada a Objetos

Python es un lenguaje de Programación Orientado a Objetos y, aunque sigue ese paradigma, puede presentar ligeras diferencias con respecto a otros lenguajes también orientados a objetos.

A continuación se exponen algunas diferencias con respecto a Java o aspectos a tener a cuenta a la hora de escribir programas utilizando las características de orientación a objetos de Python:

  • Se permita la herencia múltiple, es decir, una clase puede tener varias clases base
  • No existen los modificadores de accesibilidad (public, protected, private) aunque hay una convención de código ampliamente adoptada por la que cualquier atributo prefijado con un guión bajo debería tratarse como un parte no pública (_velocidad), sea un atributo o un método) de forma que:
    • Si un atributo comienza con un guión bajo (_velocidad) se considera como protegido (protected)
    • Si el atributo comienza con dos guiones bajos (__velocidad) se le considera como privado (private)
  • No existe la sobrecarga de métodos como tal por lo que tendremos que optar por crear un método con parámetros opcionales y decidir qué hacer dentro del mismo:
# Método con dos casos (simula la sobrecarga)
def anadir_temporada(self, cantidad=None):
    if cantidad:
        self.temporadas += 1
    else:
        self.temporadas += cantidad

Declarar clases

class Pelicula:
 
    # Atributo de clase
    productora = 'Netflix'
 
    # Constructor de la clase
    def __init__(self, titulo, director, presupuesto):
        # Atributos de instancia
        self.titulo = titulo
        self.director = director
        self.presupuesto = presupuesto
 
    # Obtiene su representación como cadena de texto
    def __str__(self):
        return self.titulo
 
    def aumentar_presupuesto(self, cantidad):
        self.presupuesto += cantidad
 
class Serie(Pelicula):
    # Esta clase hereda de la clase Pelicula
 
    def __init__(self, titulo, director, presupuesto, temporadas):
        # Invoca al constructor de la clase base
        super().__init__(titulo, director, presupuesto)
        self.temporadas = temporadas
 
    # Método con dos casos (simula la sobrecarga)
    def anadir_temporada(self, cantidad=None):
        if cantidad is None:
            self.temporadas += 1
        else:
            self.temporadas += cantidad
 
    # Método sobreescrito
    def aumentar_presupuesto(self, cantidad):
        # Invoca al método de la clase base con super()
        super().aumentar_presupuesto(cantidad * self.temporadas)

Instanciar objetos

A continuación se muestra un breve ejemplo de cómo instanciar objetos, cómo acceder a sus atributos o métodos y cómo tratar con las referencias entre objetos (en este caso es similar a como ocurre en Java)

>>> serie = Serie('Breaking Bad', 'Ni idea', 5)
>>> miniserie = MiniSerie('El coche fantastico', 'Tampoco', 1250000)
>>> miniserie.incrementar_presupuesto(100)
>>> serie.titulo
'Breaking Bad'
>>> miniserie.presupuesto
12500100
>>> otraserie = serie
>>> otraserie.titulo = 'Dexter'
>>> serie.titulo
'Dexter'

Métodos Estáticos vs Métodos de Clase

De haber trabajado con Java ya sabemos que los métodos que no se asocian a las instancias de una clase (a sus objetos) se llaman método estáticos (static) pero aqui en Python nos encontramos dos tipos de estos métodos, los conocidos como métodos estáticos (staticmethod) y lo que se conoce como método de clase (classmethod).

En Python la idea es la siguiente, los métodos estáticos no quedan vinculados ni a una instancia ni a una clase mientras que los métodos de clase siguen sin vincularse a ninguna instancia pero si a la clase.

Para comprender mejor la diferencia veamos un ejemplo de código con el mismo método declarado como estático y como de clase actuando sobre un atributo de la misma.

Primero el ejemplo de código donde definimos el método de la clase como estático:

personajes.py
class Personaje:
    tipo = 'personaje'
 
    @staticmethod
    def descripcion(self):
        print("Este objeto es de tipo ", Personaje.tipo);
 
class SuperHeroe(Personaje):
    tipo = 'superheroe'
 
class Malo(Personaje):
    tipo = "malo"
 
personaje = Personaje()
personaje.descripcion()
 
superheroe = SuperHeroe()
superheroe.descripcion()
 
malo = Malo()
malo.descripcion()

Si ejecutamos el código anterior esta es la salida que obtenemos

santi@zenbook:$ python personajes.py
Este objeto es de tipo personaje
Este objeto es de tipo personaje
Este objeto es de tipo personaje

No queda vinculado a ninguna clase y muestra siempre el valor que inicialmente se le ha dado sin importar desde cual de las subclases se está ejecutando realmente.

Si ahora reescribimos el mismo código pero hacemos que el método estático sea un método de clase (classmethod) veremos que al ejecutar hay una ligera diferencia y es que el método queda vinculado a cada clase (subclases en este caso)

personajes.py
class Personaje:
    tipo = 'personaje'
 
    @classmethod
    def descripcion(cls):
        print("Este objeto es de tipo", cls.tipo)
 
class SuperHeroe(Personaje):
    tipo = 'superheroe'
 
 
class Malo(Personaje):
    tipo = "malo"
 
personaje = Personaje()
personaje.descripcion()
 
superheroe = SuperHeroe()
superheroe.descripcion()
 
malo = Malo()
malo.descripcion()

Y al ejecutar:

santi@zenbook:$ python personajes.py
Este objeto es de tipo personaje
Este objeto es de tipo superheroe
Este objeto es de tipo malo

Podemos observar como el valor de la variable de clase varía en función de la clase que ejecuta el método puesto que éste mantiene un vínculo con la clase que lo invoca.

Definir propiedades

En lenguajes como Java, tras definir algún atributo como privado o protegido, es habitual programar los habituales métodos conocidos como getters y setters para definir cómo puede el programador acceder a dichos atributos o bien para definir que no puede (si no implementamos el correspondiente get). En Python existe lo que se conoce como propiedades (que también existen en otras plataformas como .NET) que permite crear los métodos de acceso a dichos atributos y al utilizarlos hacerlo como si de atributos se trataran. Con ello conseguimos que el código quede algo más limpio sin prescindir de algo tan importante en la POO como es la encapsulación.

Veamos un ejemplo:

class Pelicula:
    def __init__(self, titulo):
        # Hemos definido el atributo como privado
        self.__titulo = titulo
 
    # getter
    @property
    def titulo(self):
        return self.__titulo
 
    # setter
    @titulo.setter
    def titulo(self, titulo)
        self.__titulo = titulo
 
    . . .

De forma que podemos hacer uso de las propiedades como si estuvieramos accediendo a los propios atributos directamente, y lo que estamos haciendo realmente es ejecutar los métodos que hemos definido como tal:

>>> pelicula = Pelicula('Gran Torino')
>>> pelicula.titulo = 'El lobo de Wall Street'
>>> pelicula.titulo
'El lobo de Wall Street'

Clases Abstractas

Como ocurre en la POO, una clase abstracta es una clase que cumple una serie de características:

  • No se pueden instanciar objetos de ella
  • Puede definir métodos abstractos, que son aquellos que pueden dejarse en blanco (en Python con la palabra reservada pass, para que sean definidos por las clases derivadas.

En Python no existe una palabra reservada para definir una clase abstracta sino que tenemos que importar el paquete abc (Abstract Base Class) y hacer que la clase herede de ABC, y definir como @abstractmethod los métodos de esa clase que también queremos que sean abstractos.

from abc import ABC, abstractmethod
 
class Inmueble(ABC):
    def __init__(self, direccion, salario):
        self._nombre = nombre
        self._salario = salario
        super().__init__()
 
    @abstractmethod
    def obtener_salario(self):
        pass
 
     # También se pueden definir métodos 
     # que no sean abstractos
 
     . . .

Asi, si queremos crear una clase derivada de la anterio clase abtracta, tenemos que tener en cuenta que deberemos implementar los métodos declarados como abstractos en ella (si queremos que la clase derivada deje de considerarse abstracta).

class Vendedor(Empleado):
 
    def __init__(self, direccion, salario, comision):
        super().__init__(direccion, salario, comision)
        self._comision = comision
 
    # Si no definimos todos los métodos abstractos
    # la clase también será abstracta
    def obtener_salario(self):
        return self._salario + self._comision

También podemos encontrarnos con el caso de métodos abstractos que se hayan implementado en la clase base. En ese caso, en las clases que hereden de esta clase abstractos estaremos obligados a sobreescribir dichos métodos.

from abc import ABC, abstractmethod
 
class Empleado(ABC):
    def __init__(self, nombre, salario):
        self.__nombre = nombre
        self.__salario = salario
        super().__init__()
 
    @abstractmethod
    def obtener_salario(self):
        return self.__salario
 
class Vendedor(Empleado):
    def __init__(self, nombre, salario, comision):
        super().__init__(nombre, salario)
        self.__comision = comision
 
    def obtener_salario(self):
        return super().obtener_salario() + self.__comision

Ficheros

Leer ficheros de texto

En este apartado se muestran algunos ejemplos de operaciones habituales sobre ficheros de texto.

Extraer algo de información útil sobre el propio fichero (no el contenido):

>>> # Muestra información útil sobre el fichero abierto
>>> fichero = open('fichero.txt')
>>> print(fichero.name)
'estructura.py'
>>> print(fichero.encoding)
'UTF-8'
>>> print(fichero.mode)
'r'
>>> fichero.close()

Cómo leer un fichero de texto completo, con una sola línea de texto:

>>> # Lee un fichero de texto completo
>>> fichero = open('fichero.txt')
>>> contenido = fichero.read()
>>> fichero.close()
>>> contenido
'Este es el contenido del fichero'

En este caso podemos ver cómo es posible (y recomendable) utilizar la instrucción with para trabajar con ficheros, puesto que se encarga de cerrarlo automáticamente cuando terminamos de leerlo:

# Lee el fichero y lo cierra automáticamente
with open('fichero.txt') as fichero:
    contenido = fichero.read()
 
print(contenido)

También podemos, si la ocasión lo requiere, leer el fichero de texto línea a línea:

# Lee el fichero línea a línea
with open('fichero.txt') as fichero:
    for linea in fichero:
        print(linea.rstrip())

Escribir ficheros de texto

Aqui encontramos un par de ejemplos a la hora de escribir ficheros de texto.

Cómo escribir un fichero en modo escritura (writable), de forma que el nuevo contenido sobrescribe completamente todo el contenido anterior del fichero:

with open('fichero.txt', mode='w') as fichero:
    fichero.write('este texto sobreescribe el fichero')

En este otro caso en modo añadir (append), el contenido escrito se añadirá justamente al final del contenido que ya tuviera el fichero:

with open('fichero.txt', mode='a') as fichero:
    fichero.write('este texto se añade al fichero')

XML

Conexión con Bases de Datos

MySQL

mysql.jpg

MySQL es un gestor de Bases de Datos Relacional diseñado según la arquitectura cliente-servidor. Actualmente es uno de los 3 motores que más se utilizan (y con mucha diferencia con respecto al resto) 1) (los otros 2 son Oracle y Microsoft SQL Server)

A continuación veremos como realizar las 4 operaciones básicas (CRUD2)) sobre una Base de Datos MySQL.

En este caso hemos utilizado el módulo pymysql

Lanzar una consulta

# Ejemplo que muestra como conectar con una Base de Datos MySQL
# Usa el módulo pymysql que hay que instalar
# Lista todos los valores de un campo determinado por la posición
# También comprueba los errores de conexión más comunes
import pymysql
 
try:
    conexion = pymysql.connect(host='localhost', port=3306, user='santi', 
                               passwd='supercontraseña', db='basededatos',
                               cursorclass=pymysql.cursors.DictCursor)
    sql = "SELECT * FROM prueba"
    cursor = conexion.cursor()
    cursor.execute(sql)
 
    for fila in cursor:
        print(fila[texto])  # Se debe indicar el campo a mostrar
        # print(fila[0])  # La posición determina el campo a mostrar
 
except pymysql.err.OperationalError:
    print("Se ha producido un error al conectar con la Base de Datos")
    print("Comprueba que MySQL está iniciado o que los datos de conexión son correctos")
except pymysql.err.InternalError:
    print("Se ha producido un error al conectar con la Base de Datos")
    print("Comprueba que la Base de Datos existe")
finally:
    if 'cursor' in locals():
        cursor.close()
    if 'conexion' in locals():
        conexion.close()

Insertar un registro

. . .
sql = "INSERT INTO prueba (texto) VALUES (%s)"
cursor = conexion.cursor()
cursor.execute(sql, ('Texto de prueba'))
conexion.commit()
. . .

Modificar un registro

. . .
sql = "UPDATE prueba SET texto = %s WHERE id = %s"
cursor = conexion.cursor()
cursor.execute(sql, ('nuevo texto', 1))
conexion.commit()
. . .

Eliminar un registro

. . .
sql = "DELETE FROM prueba WHERE id = %s"
cursor = conexion.cursor()
cursor.execute(sql, (2))
conexion.commit()
. . .

PostgreSQL

Realizar una consulta

 

Insertar un registro

 

Modificar un registro

 

Eliminar un registro

 

SQLite

SQLite es un motor de Bases de Datos Relacional que, a diferencia de los anteriores que hemos visto, no sigue la arquitectura cliente-servidor sino que la Base de Datos se almacena en un sólo fichero y es la propia librería la que hace de motor.

SQLite se usa principalmente como almacenamiento local de cualquier tipo de aplicación siempre que necesitemos almacenar una cantidad no demasiado grande de información. Presenta como ventajas que siempre será mucho más sencillo de implantar la solución desarrollada ya que no tenemos que instalar ni configurar ningún software adicional. Actualmente es uno de los motores más usados ya que, por ejemplo, Android lo utiliza (viene integrado como parte de Android) para el almacenamiento local de las aplicaciones instaladas.

Conviene tener en cuenta que al tratarse de un motor tan sencillo apenas hay aplicaciones de gestión por lo que lo más habitual es incluir el código de creación de la Base de Datos como parte del código de la aplicación teniendo en cuenta que sólo deberá ejecutarse la primera vez. Es por eso que en este caso se incluye el ejemplo de cómo crear las tablas de la Base de Datos desde Python. Hay que pensar que nunca encontraremos Bases de Datos complejas para ser gestionadas por SQLite, por lo que esta práctica tampoco supone ningún problema. En el resto de ejemplos siempre se asume que la Base de Datos ya ha sido diseñada y creada con las aplicaciones correspondientes.

Para trabajar con Python en este caso hemos utilizado el módulo pysqlite que además dispone de amplia documentación

Conectar y crear la Base de Datos

En el siguiente ejemplo se muestra el código de como crear una Base de Datos 3)

# Ejemplo que muestra como conectar y crear 
# una Base de Datos SQLite
# Usa el módulo pysqlite que hay que instalar
import sqlite3
 
try:
    # Este primer bloque sólo habría que ejecutarlo
    # la primera vez
    conexion = sqlite3.connect("datos.db")
    sql_create = "CREATE TABLE prueba" \
                 "(id INTEGER PRIMARY KEY, texto VARCHAR(255))"
    cursor = conexion.cursor()
    cursor.execute(sql_create)
    conexion.commit()
 
except sqlite3.OperationalError:
    print("Se ha producido un error al conectar con la Base de Datos")
except sqlite3.InternalError:
    print("Se ha producido un error al conectar con la Base de Datos")
    print("Comprueba que la Base de Datos existe")
finally:
    if 'cursor' in locals():
        cursor.close()
    if 'conexion' in locals():
        conexion.close()

Realizar una consulta

. . .
sql_select = "SELECT * FROM prueba"
cursor.execute(sql_select)
 
for fila in cursor:
    print(fila[1])  # La posición determina el campo a mostrar
. . .

Insertar un registro

. . .
sql_insert = "INSERT INTO prueba (texto) VALUES (?)"
parametros = ('texto de prueba',)
cursor.execute(sql_insert, parametros)
conexion.commit()
. . .

Modificar un registro

 

Eliminar un registro

 

MongoDB

Realizar una consulta

 

Insertar un registro

 

Modificar un registro

 

Eliminar un registro

 

Cómo organizar un proyecto

Módulos

La programación modular o basada en módulos consiste en dividir una aplicación en varias partes independientes (módulos), capaces de funcionar por separados, pero que juntas funcionan como una sola. La principal idea sobre la programación modular es la de poder crear un código legible, ordenado y que se puedan mantener.

Además, disponer de nuestra aplicación programada en módulos, permite que podamos reutilizar esos módulos para construir otras, reutilizando de esa manera nuestro código. Incluso podemos desarrollar librerías simplemente para que otros puedan apoyarse (importando los módulos) en ellas para construir sus aplicaciones.

En Python, un módulo es cualquer fichero con extensión .py (fichero de código) con código que pueda ser reutilizado (clases, funciones, . . .).

La forma de utilizar esos módulos es utilizando la palabra reservada import acompañada del nombre del módulo que queremos utilizar en nuestra aplicación.

Por ejemplo, supongamos que tengo el módulo 'operaciones.py':

operaciones.py
def siguiente(numero):
    return numero + 1
 
def anterior(numero):
    if (numero > 0):
        numero -= 1
    return numero

Y ahora quiero importar ese módulo para utilizar algunas de sus funciones. Hay varias posibilidades:

Importar el módulo sin más, de forma que tendremos que escribir su nombre cuando queramos utilizar sus funciones:

>>> import operaciones
>>> operaciones.siguiente(3)
4

Podemos importarlo y renombrarlo, de forma que podremos utilizar el nuevo nombre cuando queramos utilizar sus funciones:

>>> import operaciones as ops
>>> ops.siguiente(3)
4

También podemos importar todas las funciones del módulo y evitar tener que utilizar su nombre como prefjo antes de invocarlas:

>>> from operaciones import *
>>> anterior(0)
0

También podemos importar sólo algunas de sus funciones, y utilizarlas entonces sin usar el nombre del módulo como prefijo:

>>> from operaciones import siguiente
>>> siguiente(3)
4
>>> anterior(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'anterior' is not defined

Realmente la opción mas aconsejable es importar el paquete tal cual aunque ello nos obligue a escribir su nombre a modo de prefijo cada vez que invoquemos a alguna de sus funciones. En proyectos muy grandes a veces interesa saber de donde salen las funciones y de esa manera podremos distinguir claramente nuestro código del de los módulos que hayamos importado. En ocasiones puede ser recomendable un renombrado del módulo para aquellos que tengan un nombre largo o incómodo de escribir, pero siempre utilizando un segundo nombre que sea lo suficientemente descriptivo como para que sepamos a que módulo se refiere.

Paquetes

En el punto anterior hemos visto que podemos organizar el código en módulos. En Python un módulo es un fichero que contiene código que podremos reutilizar importándolo. En el caso de que tengamos varios módulos y queramos organizarlos todos juntos estaremos hablando de un paquete.

Utilizar paquetes es la forma en la que podremos organizar nuestros proyectos más grandes en Python. Puesto que podemos almacenar varios módulos en cada paquete, podemos estructurar más todavía nuestro código evitando asi tener módulos demasiado grandes.

En Python un paquete es cualquier carpeta que contenga en su interior un fichero vacío con el nombre __init__.py. Todos los módulos que aparezcan en esa carpeta formarán parte de ese paquete, cuyo nombre será el nombre de la carpeta.

Al final, la forma de importar los módulos de cada paquete es la misma que la que hemos usado en el apartado anterior a la hora de importar módulos por separado. Ahora, simplemente tendremos que utiliar el nombre del paquete delante del módulo de la forma import paquete.modulo.

Imaginemos que tenemos el siguiente paquete, compuesto a su vez de dos paquetes. Cada uno de estos dos paquetes tiene un módulo con varias funciones cada uno (podrían contener más de un módulo y la forma de trabajar con ellos sería la misma).

Figure 1: Estructura del paquete calculadora

Cada uno de los ficheros operaciones.py (se llaman igual pero no es necesario) contiene una serie de funciones, basicas por un lado:

operaciones.py
def sumar(numero1, numero2):
    return numero1 + numero2
 
def restar(numero1, numero2):
    return numero1 - numero2
 
def multiplicar(numero1, numero2):
    return numero1 * numero2
 
def dividir(numero1, numero2):
    return numero1 / numero2
 
def potencia(numero, exponente):
    return numero ** exponente

y de funciones por otro:

operaciones.py
import math
 
def seno(valor):
    return math.sin(valor)
 
def coseno(valor):
    return math.cos(valor)
 
def tangente(valor):
    return math.tan(valor)

Así, si desde fuera del paquete principal calculadora, queremos importar cualquiera de los módulos, procederemos de la siguiente forma:

>>> import calculadora.basicas.operaciones as operaciones
>>> print(operaciones.sumar(3, 4))
7
>>> |

Y a partir de ahi podemos importar los módulos de las diferentes formas que hemos visto en el apartado anterior sobre los mismos.

GUI

Tkinter es la librería estándar para el desarrollo de GUIs en Python y viene incluida en la instalación de Python, por lo que para usarla sólo es necesario importar el paquete tkinter.

A continuación se muestra un ejemplo muy sencillo creado con esta librería. Para más información, en la sección Referencias hay un par de enlaces interesantes.

#!/usr/bin/python3
import tkinter as tk
 
class Ventana(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        self.grid()
        self.__crearVentana()
 
    def __crearVentana(self):
        self.__tfTexto = tk.Text(self, width=20, height=1)
        self.__tfTexto.grid()
 
        self.__btBorrar = tk.Button(self, text='Borrar', command=self.__borrar)
        self.__btBorrar.grid(row=0, column=1)
 
        self.__btSalir = tk.Button(self, text='Salir', command=self.quit)
        self.__btSalir.grid(row=1, column=0)
 
    def __borrar(self):
        self.__tfTexto.delete(1.0, tk.END)
 
if __name__ == '__main__':
    ventana = Ventana()
    ventana.master.title('¡Hola GUI!')
    ventana.mainloop()
Figure 2: Ejemplo GUI creada con tkinter
apuntes/python.txt · Last modified: 2019/06/11 20:40 by Santiago Faci