Tabla de Contenidos
Ejemplos prácticos Python
Bloque perteneciente al curso Introducción a la programación con Python.
Tratamiento de ficheros CSV
Un fichero CSV es un fichero de texto cuyo contenido está separado por comas (CSV - Comma Separated Values).
Lectura fichero CSV
index,Organization id,Name,Website,Country, Description,Founded,Industry,Number of employees 1,abcd,Ferrell LLC, https://web.net,Papua New Guinea,Horizontal empowering knowledgebase,1990,Plastics,3498
Dentro de la biblioteca estándar de Python, hay un apartado para la lectura y escritura de ficheros CSV.
import csv # Para ver las variantes de CSV que admite esta biblioteca csv.list_dialects() # Lista vacía en la que se irán añadiendo todas las filas organizaciones = [] with open('fichero.csv', newline='') as csvfile: reader1 = csv.reader(csvfile, delimiter=',', quotechar='"') for row in reader1: organizaciones.append(row) orgs_paises = {} # Nos saltamos la primera línea porque son los encabezados for r in organizaciones[1:]: pais = r[4] if pais in orgs_paises: orgs_paises[pais] += 1 else: orgs_paises[pais] = 1 # Mostramos los países y cuántas veces se repiten orgs_paises
Escritura fichero CSV
import csv with open('paises.csv', 'w', newline='') as csvfile: writer1 = csv.writer(csvfile) writer1.writerow(['Pais', 'Num organizaciones']) for t in orgs_paises.items() writer1.writerow(t)
Manipulación de CSV mediante diccionarios
Forma más cómoda de manejar ficheros CSV.
import csv with open('organizations.csv', newline='') as csvfile: reader = csv.DictReader(csvfile) for d in reader: print(f"Empresa {d["Name"]}, con {d["Number of employees"]} empleados")
Pandas
Biblioteca para el análisis de datos. Más información.
# Importamos y creamos un alias import pandas as pd df1 = pd.read_csv("people.csv") # Muestra una serie de operaciones y conteos básicos: df1.describe() # Mostrando los valores de cierto campo: df1["First Name"]
KML
Un KML es un fichero XML con información cartográfica.
Entre los módulos de Python que podemos usar generar ficheros KML, está simplekml
import simplekml kml = simplekml.Kml() kml.newpoint(name="Secret place", coords=[{-8.77, 42.32}]) # longitud, latitud mkl.save("secret_place.kml")
Entornos virtuales de desarrollo (venv)
pip freeze > requirements.txt
Instalamos los módulos necesarios para el proyecto:
pip install -r requirements.txt
Lanzar el entorno virtual en Windows:
.venv\Scripts\activate
Lanzar el entorno virtual en Linux:
source .venv/bin/activate
Cuando copiemos lo que hayamos hecho en un entorno virtual, deberíamos excluir .venv ya que es propio de la máquina en la que se ejecuta.
Desarrollo web
Alguna opción más:
- Tornado: para comunicaciones asíncronas (incorpora su propio servidor web).
Flask
Ejemplo básico usando el módulo Flask
pip install Flask
from flask import Flask app = Flask(__name__) @app.route("/hola") def hello_world(): return "Hola, mundo"
Django
Django es un Framework para desarrollar aplicaciones web de forma rápida. Es un “todo en uno”.
pip install Django
Usa ORM (Object Relational Mapping) para que el usuario no tenga que crear las tablas, se encarga el framework
Bases de datos
Bases de datos DBM
DBM son bases de datos de Unix. Información sobre el módulo ''dbm'' de Python
Escritura:
import dbm with dbm.open("ejemplo-dbm", "c") as db: db["nombre"] = "Pepito" db["apellido"] = "Grillo" db["edad"] = str(31)
En este tipo de bases de datos, los datos deben guardarse como strings
Lectura:
import dbm with dbm.open("ejemplo-dbm", "c") as db: for k in db.keys(): print(f"Clave {k} -> valor {db[k]}")
Bases de datos SQLite
Información del módulo ''sqlite3'' de Python
Creación de base de datos y una tabla:
import sqlite3 con = sqlite3.connect("tutorial.db") cur = con.cursor() cur.execute("CREATE TABLE movie(title, year, score)") con.close
Inserción de datos en la tabla previamente creada:
con = sqlite3.connect("tutorial.db") cur = con.cursor() cur.execute(""" INSERT INTO movie VALUES ('Monty Python and the Holy Grail', 1975, 8.2), ('And Now for Something Completely Different', 1971, 7.5) """) con.commit() con.close()
Consulta de los datos:
con = sqlite3.connect("tutorial.db") cur = con.cursor() cur.execute(""" SELECT title, score FROM movie """) entradas = res.fetchall() for t in entradas: print(f"Película: {t[0]}, calificación: {t[1]}") con.commit() con.close()
Para poder interactuar con un fichero sqlite podemos usar el cliente para línea de comandos sqlite3.
SQLAlchemy
Biblioteca de abstracción de bases de datos. Es un ORM. Web oficial
Necesitamos igualmente el soporte para la base de datos que vayamos a utilizar.
pip install SQLAlchemy
form sqlalchemy import create_engine, text # Conexión SQLite engine = create_engine("sqlite+pysqlite:///tutorial.db") with engine.connect() as conn: result = conn.execute(text("SELECT * FROM movie")) print(result.all())
Para trabajar con bases de datos MySQL, primero instalamos la dependencia:
pip install mysql
form sqlalchemy import create_engine, text # Conexión MySQL engine = create_engine("mysql+pymysql:///usuario:contraseña@servidor:puerto/base_datos") with engine.connect() as conn: result = conn.execute(text("SELECT * FROM movie")) print(result.all())
Interfaz gráfica
Usando PyQt
Aplicación cronómetro
requirements.txt:
altgraph==0.17.3 macholib==1.16.2 pyinstaller==5.10.1 pyinstaller-hooks-contrib==2023.2 PyQt6==6.5.0 PyQt6-Qt6==6.5.0 PyQt6-sip==13.5.1
app.py:
import sys from PyQt6.QtWidgets import QApplication, QWidget, QHBoxLayout, QPushButton if __name__ == '__main__': app = QApplication(sys.argv) w = QWidget() layout = QHBoxLayout() btn1 = QPushButton("Púlsame") btn2 = QPushButton("Púlsame también") layout.addWidget(btn1) layout.addWidget(btn2) w.setLayout(layout) w.resize(250, 150) w.move(300, 300) w.setWindowTitle('Hola Mundo') w.show() sys.exit(app.exec())
cronometro.py:
from datetime import datetime import sys from PyQt6.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton, QLabel, QMessageBox from PyQt6.QtCore import Qt, QTimer from PyQt6 import QtGui class Cronometro(QWidget): def __init__(self): super(Cronometro, self).__init__() self.enMarcha = False self.tiempoInicial = None self.tiempoTranscurrido = None self.iniciarGUI() # Construcción de la interfaz gráfica def iniciarGUI(self): self.setWindowTitle("Cronómetro") self.setWindowIcon(QtGui.QIcon('icon.png')) self.setGeometry(100, 100, 300, 300) # Posición y tamaño iniciales vbox = QVBoxLayout() # Disposición vertical de los elementos # Etiqueta para mostrar el tiempo self.timeLabel = QLabel("00:00:00") self.timeLabel.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignCenter) # Centrar el texto de la etiqueta self.timeLabel.setStyleSheet("font-size: 50px; font-weight: bold") # Estilo de la etiqueta (tamaño de la fuente) # Botón para iniciar/parar el cronómetro self.startStopButton = QPushButton("Start") self.startStopButton.setStyleSheet("background-color: green; color: white; font-size: 20px") self.startStopButton.clicked.connect(self.startStop) # Conectar la señal clicked del botón con el método startStop self.startStopButton.setShortcut("Space") vbox.addWidget(self.timeLabel) vbox.addWidget(self.startStopButton) self.setLayout(vbox) def startStop(self): self.enMarcha = not self.enMarcha # Invierte el estado del cronómetro if self.enMarcha: if self.tiempoTranscurrido: self.tiempoInicial = datetime.now() - self.tiempoTranscurrido else: self.tiempoInicial = datetime.now() self.temporizador = QTimer() self.temporizador.timeout.connect(self.actualizarTiempo) self.temporizador.start(1000) # cada 1000 ms genera un evento timeout self.startStopButton.setText("Stop") self.startStopButton.setStyleSheet("background-color: red; color: white; font-size: 20px") else: self.temporizador.stop() self.startStopButton.setText("Start") self.startStopButton.setStyleSheet("background-color: green; color: white; font-size: 20px") self.startStopButton.setShortcut("Space") def actualizarTiempo(self): tiempoActual = datetime.now() self.tiempoTranscurrido = tiempoActual - self.tiempoInicial horas, resto = divmod(self.tiempoTranscurrido.seconds, 3600) minutos, segundos = divmod(resto, 60) self.timeLabel.setText(f"{horas:02}:{minutos:02}:{segundos:02}") def closeEvent(self, event): reply = QMessageBox.question(self, 'Salir', '¿Está usted seguro de que quiere salir?', QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) if reply == QMessageBox.StandardButton.Yes: event.accept() # el evento de salida se acepta y la aplicación termina else: event.ignore() # como si nada hubiera pasado if __name__ == '__main__': # Creación de la aplicación, encargada de gestionar su ciclo de vida app = QApplication(sys.argv) app.setApplicationName("Cronómetro") app.setApplicationDisplayName("Cronómetro") # Creación de la instancia de la ventana del cronómetro cronometro = Cronometro() # Mostrar ventana cronometro.show() # Proceso de eventos de la aplicación y terminación de la misma sys.exit(app.exec())
Testing: Pruebas unitarias
Usaremos el módulo unittest para las pruebas unitarias en Python.
Los tests unitarios están pensados para probar módulos aislados.
Partimos de un programa:
# Programa para resolver ecuaciones de segundo grado con soluciones reales from math import sqrt # Cálculo de las raíces def segundo_grado(a: float, b: float, c: float) -> tuple: """ Calcula las soluciones reales de una ecuación de segundo grado. Devuelve una tupla con las 2 soluciones reales o lanza una excepción en caso de no tenerlas. """ d = b ** 2 - 4 * a *c if d > 0: # Ahora podemos ejecutar las siguientes operaciones sin temor a que dé error de dominio x1 = (-b + sqrt(d)) / (2 * a) x2 = (-b - sqrt(d)) / (2 * a) return x1, x2 else: # Para el caso de la raíz cuadrada negativa, mostramos un aviso raise ValueError("La ecuación {a}x^2 + {b}x + {c} = 0 no tiene soluciones reales") # Punto de entrada al programa: se ejecuta si el programa se llama directamente if __name__ == '__main__': # Toma de datos print("ax^2 + bx + c = 0") a = float(input("Coeficiente a: ")) b = float(input("Coeficiente b: ")) c = float(input("Coeficiente c: ")) x1, x2 = segundo_grado(a, b, c) # Tercer paso: mostrar resultados print(f"Soluciones: {x1} y {x2}")
Si ahora queremos probar el anterior código, crearemos un fichero nuevo test_segundo_grado.py:
import unittest from segundo_grado import segundo_grado class TestSegundoGrado(unittest.TestCase): def test_ecuacion1(self): self.assertEqual(segundo_grado(1, 0, -1), (1, -1)) def test_ecuacion2(self): self.assertEqual(segundo_grado(1, 2, 1), (-1, -1)) def test_ecuacion3(self): self.assertEqual(segundo_grado(1, 2, -3), (1, -3)) def test_ecuacion4(self): self.assertAlmostEqual(segundo_grado(1, -2, 0), (2, 0)) # Para probar algo que falle: def test_ecuacion5(self): with self.assertRaises(ValueError) as err: segundo_grado(1, 0, 1) self.assertTrue(str(err).endswith("no tiene soluciones reales")) def test_ecuacion6(self): self.assertAlmostEqual(segundo_grado(2, 4, -6), (1, -3)) def test_ecuacion7(self): self.assertAlmostEqual(segundo_grado(1, -1, -1), (1.618033988749895, -0.6180339887498949)) def test_ecuacion8(self): if __name__ == '__main__': unittest.main()
Si hacemos modificaciones en el código, los tests nos aseguran que todo siga funcionando correctamente o que el cambio que hemos hecho hace que no pasen los test. De esa manera, podremos detectar si algo hemos hecho mal.
Además, al hacer tests, estamos dejando por escrito las pruebas que realizaremos en nuestro código.
Para lanzar la batería de tests desde la línea de comandos:
python -m unittest test_segundo_grado.py
Conversión a números romanos
Esta vez crearemos primero los tests y luego crearemos el código :
TDD (Test Driven Development): técnica de desarrollo en la que primero se define el test y luego el código que debe satisfacer el test.
Fichero test_romanos.py:
import unittest from romanos import roman class TestRomanos(unittest.TestCase): def test_1(self): self.assertEqual(roman(1), "I") def test_2(self): self.assertEqual(roman(2), "II") def test_2(self): self.assertEqual(roman(2), "II") def test_3(self): self.assertEqual(roman(3), "III") def test_4(self): self.assertEqual(roman(4), "IV") def test_5(self): self.assertEqual(roman(5), "V") def test_6(self): self.assertEqual(roman(6), "VI") def test_7(self): self.assertEqual(roman(7), "VII") def test_9(self): self.assertEqual(roman(9), "IX") def test_10(self): self.assertEqual(roman(10), "X") def test_13(self): self.assertEqual(roman(13), "XIII") def test_14(self): self.assertEqual(roman(14), "XIV") def test_17(self): self.assertEqual(roman(17), "XVII") def test_19(self): self.assertEqual(roman(19), "XIX") def test_20(self): self.assertEqual(roman(20), "XX") def test_27(self): self.assertEqual(roman(27), "XVII") def test_33(self): self.assertEqual(roman(33), "XXXIII") def test_40(self): self.assertEqual(roman(40), "XL") def test_49(self): self.assertEqual(roman(49), "XLIX") def test_50(self): self.assertEqual(roman(50), "L") def test_62(self): self.assertEqual(roman(62), "LXII") def test_90(self): self.assertEqual(roman(90), "XC") def test_97(self): self.assertEqual(roman(90), "XCVII") def test_100(self): self.assertEqual(roman(100), "C") def test_333(self): self.assertEqual(roman(333), "CCCXXXIII") def test_400(self): self.assertEqual(roman(400), "CD") def test_500(self): self.assertEqual(roman(500), "D") def test_666(self): self.assertEqual(roman(666), "DCLXVI") def test_900(self): self.assertEqual(roman(900), "CM") def test_1000(self): self.assertEqual(roman(1000), "M") def test_1666(self): self.assertEqual(roman(1666), "MDCLXVI") def test_3999(self): self.assertEqual(roman(3999), "MMMCMXCIX") if __name__ == '__main__': unittest.main()
Creamos el programa romanos.py:
""" Romani ite domum! TDD - Test Driven Development 1. Escribir un test (que inicialmente falle) 2. Escribir el código que haga pasar el test 3. Refactorizar el código: optimización, limpieza, etc (Y vuelta a empezar...) """ def roman(n: int) -> str: cantidades = [ (1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"), (50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I") ] conv = "" for cant, letras in cantidades: while n >= cant. n -= cant conv += letras return conv
Opción con recursividad:
def roman2(n: int) -> str: if n >= 1000: return "M" + roman2(n - 1000) elif n >= 900: return "CM" + roman2(n - 900) elif n >= 500: return "D" + roman2(n - 500) elif n >= 400: return "CD" + roman2(n - 400) elif n >= 100: return "C" + roman2(n - 100) elif n >= 90: return "XC" + roman2(n - 90) elif n >= 50: return "L" + roman2(n - 50) elif n >= 40: return "XL" + roman2(n - 40) elif n >= 10: return "XL" + roman2(n - 10) elif n >= 9: return "XL" + roman2(n - 9) elif n >= 5: return "V" + roman2(n - 5) elif n >= 4: return "IV" + roman2(n - 4) elif n >= 1: return "I" + roman2(n - 1) else: return ""
- codewars: retos de programación
JSON
JSON (JavaScript Object Notation) formato para el intercambio de información.
JSON no son más que objetos (que son equivalentes a los diccionarios de Python) y listas.
Se trata de texto.
JSON en Python
Aspecto de un JSON, que lo representamos como string:
datos_json = ''' { "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": { "GlossEntry": { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": { "para": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": [ "GML", "XML" ] }, "GlossSee": "markup" } } } } } '''
# Biblioteca de Python para el tratamiento de JSON import json # Cargamos a partir de un string un objeto JSON/diccionario datos = json.loads(datos_json) type(datos) # dict datos.keys() # dict_keys(['glossary']) glossary = datos['glossary'] # dict glossary.keys() # dict_keys(['title', 'GlossDiv']) paises_capitales = { "España": "Madrid", "Portugal": "Lisboa", "Italia": "Roma", "Francia": "Paris", "Alemania": "Berlin" } type(paises_capitales) # dict # Si queremos pasar de un diccionario a string en formato JSON json.dumps(paises_capitales)
Si procesamos un JSON inválido:
mal_json = ''' {"mal": "json" ''' try: json.loads(mal_json) except json.JSONDecodeError as error: print("Error cargando JSON")
disculpa, Jairo, sabes hasta qué día tenemos disponible el acceso al campus virtual?
