¿No hay switch en Python?

Esto es algo que nos ha sorprendido a todos los que alguna vez nos hemos enfrentado al Python. ¿Por qué demonios no hay un switch? ¿Y ahora qué hago yo? ¿Tengo que anidar un montón de if-else?

Pues no, en Python no existe la estructura switch. Sin embargo no es necesario empezar a anidar if-else, podemos usar diccionarios.

Un diccionario en Python es parecido a lo que en otros lenguajes se llama array asociativo y se define así:

edades = { 'Paco': 20, 'Luis': 25, 'Lucas': 30 }

y la forma de usarlo es:

edades['Paco']

por ejemplo:

print ("La edad de Paco es "+str(edades['Paco'])+".")

Que mostrará:

La edad de Paco es 20.

Entonces, si quisiéramos algo como esto (ojo, recuerda que este código no funciona en Python):

nombre = "Luis"
switch(nombre):
   case 'Paco':
     edad = 20
     break
   case 'Luis':
     edad = 25
     break
   case 'Lucas':
     edad = 30
     break
print edad

tendríamos que hacer:

edades = { 'Paco': 20, 'Luis': 25, 'Lucas': 30 }
nombre = 'Luis'
edad = edades[nombre]
print edad

Bastante sencillo ¿no? Pero, podrías pensar, esto está muy bien para seleccionar valores como en este caso pero ¿y si lo que quiero es llamar a una función o ejecutar un bloque de código? Pues tranquilo, porque se puede. Mira el siguiente ejemplo:

def sumar(a, b):
    return a + b
 
def restar(a, b):
    return a - b
 
def multiplicar(a, b):
    return a * b;
 
num1 = raw_input("Num1: ")
num2 = raw_input("Num2: ")
 
print("Opciones\n1.- Sumar\n2.- Restar\n3.- Multiplicar")
 
operaciones = { '1': sumar, '2': restar, '3': multiplicar}
 
seleccion = raw_input('Escoge una: ')
try:
    resultado = operaciones[seleccion](int(num1), int(num2))
    print resultado
except:
    print("Esa no vale")

Como en este caso no sabemos la opción que va a elegir el usuario (podría teclear un 4 o cualquier otra cosa) tenemos que usar try-except para evitar errores (algo similar al default de los switch-case).

Problemas con el paquete python-chardet en Ubuntu

Desde hace un tiempo he tenido algunos problemas al actualizar mi Ubuntu. El problema estaba relacionado con el paquete python-chardet 1.0.1-1.1. Según he descubierto al ir a informar del fallo en bugs.launchpad.net el problema se puede corregir editando el fichero: /usr/share/python/debian_defaults:

sudo gedit /usr/share/python/debian_defaults

y modificando la línea:

# all supported python versions
supported-versions = python2.5, python2.6

por:

# all supported python versions
supported-versions = python2.5, python2.6, python3.0, python3.1

Python: Trabajar con MySQL

En esta nueva entrega de artículos sobre Python le ha tocado el turno al MySQL.

Lo primero, si no tenemos instalado el soporte MySQL para Python debemos añadirlo al sistema. En Ubuntu podemos hacerlo así:

sudo apt-get install python-mysqldb

Existen dos formas de trabajar con MySQL, una es a través del módulo _mysql y la otra es a través de MySQLdb. _mysql es la implementación en Python de la API de C para MySQL. Se suele recomendar usar MySQLdb para trabajar, pero, cabezota que es uno yo suelo trabajar con _mysql :-P.

Para el ejemplo voy a suponer que existe una tabla llamada clientes que contiene los campos nombre y apellido.

#!/usr/bin/env python
 
import _mysql
 
# Datos para la conexión a MySQL
mysql_servidor = 'localhost'
mysql_usuario  = 'usuario'
mysql_clave    = 'clave'
mysql_bd       = 'base_datos'
 
conexion = _mysql.connect(host=mysql_servidor, user=mysql_usuario, passwd=mysql_clave, db=mysql_bd)
sql = "select * from clientes limit 10"
conexion.query(sql)
query = conexion.store_result()
if query.num_rows():
	print "Resultados\n=========="
	fila = query.fetch_row(how=1)
	while fila:
		print fila[0]["nombre"], " ", fila[0]["apellido"]
		fila = query.fetch_row(how=1)
conexion.close()

Aquí empezamos a usar conceptos algo más complicados como los diccionarios y las tuplas. Se recomienda buscar información al respecto (o igual me animo y escribo algún post sobre el tema).

Algunos comentarios:

1) fetch_row() tiene un parámetro opcional llamado how que indica cómo se devuelven los resultados. Si se especifica el valor ‘1’ los devolverá como un dicconario donde la clave es el nombre del campo.

2) Uso fila[0] ya que fetch_row devuelve cada fila como un diccionario dentro de una tupla.

3) store_result(). Si se usa esta función el servidor nos envía todos los datos “de golpe”, lo que puede ser un problema si hay muchos datos. Sin embargo podemos limitar el número de resultados usando LIMIT en la SQL. También podemos usar use_result() y el servidor nos devolvera las filas una a una. Esta opción tiene la pega de que consumimos más recursos del servidor.

Python: Conectar a un servidor SSH

Y seguimos con la racha de artículos sobre Python (sí, me estoy quitando esa espina clavada). Esta vez vamos a ver un ejemplo de cómo enviar comandos a un servidor mediante SSH.

La magia esta vez se consigue gracias a la librería Paramiko. Para instalarla en Ubuntu:

sudo apt-get install python-paramiko

En este ejemplo no hay apenas control de errores, que sería importante añadirlo (eso queda como deberes para el lector 🙂 ).

#!/usr/bin/env python
 
# Librerías necesarias
import paramiko
import os
 
# Datos para la conexión SSH
ssh_servidor = 'midominio.com'
ssh_usuario  = 'usuario'
ssh_clave    = 'clave'
ssh_puerto   = 22 # O el puerto SSH que use nuestro servidor
comando      = 'ls' # el comando que vamos a ejecutar en el servidor
 
# Conectamos al servidor
conexion = paramiko.Transport((ssh_servidor, ssh_puerto))
conexion.connect(username = ssh_usuario, password = ssh_clave)
 
# Abrimos una sesión en el servidor
canal = conexion.open_session()
# Ejecutamos el comando, en este caso un sencillo 'ls' para ver
# el listado de archivos y directorios
canal.exec_command(comando)
 
# Y vamos a ver la salida
salida = canal.makefile('rb', -1).readlines()
if salida:
	# Si ha ido todo bien mostramos el listado de directorios
	print salida
else:
	# Si se ha producido algún error lo mostramos
	print canal.makefile_stderr('rb', -1).readlines()
conexion.close()

Python: Subir archivos a un FTP

En esta segunda entrega de Python os propongo un ejemplo que permite subir un fichero a un servidor FTP.

#!/usr/bin/env python
# -*- coding: cp1252 -*-
 
import ftplib
import os
 
# Datos FTP
ftp_servidor = 'ftp.servidor.com'
ftp_usuario  = 'miusuario'
ftp_clave    = 'miclave'
ftp_raiz     = '/public_html' # Carpeta del servidor donde queremos subir el fichero
 
# Datos del fichero a subir
fichero_origen = '/home/gorka/mifichero.zip' # Ruta al fichero que vamos a subir
fichero_destino = 'mifichero.zip' # Nombre que tendrá el fichero en el servidor
 
# Conectamos con el servidor
try:
	s = ftplib.FTP(ftp_servidor, ftp_usuario, ftp_clave)
	try:
		f = open(fichero_origen, 'rb')
		s.cwd(ftp_raiz)
		s.storbinary('STOR ' + fichero_destino, f)
		f.close()
		s.quit()
	except:
		print "No se ha podido encontrar el fichero " + fichero_origen
except:
	print "No se ha podido conectar al servidor " + ftp_servidor

Python: descargar imágenes de una web

Hoy me he dado cuenta de algo terrible: ¡Aún no he escrito ninguna entrada acerca de Python, uno de de mis lenguajes de programación favoritos!

Voy a remediarlo comenzando con un sencillo script que permite descargarse imágenes de una web. Este script lo he usado como parte de un proyecto para un cliente que necesitaba descargarse automáticamente imágenes que le iban subiendo sus usuarios.

Ya que estamos en fiestas de Bilbao voy a poner un ejemplo que se descarga de la web del Ayuntamiento el cartel de las fiestas.

#!/usr/bin/env python
# -*- coding: cp1252 -*-
 
import httplib # Necesaria para hacer peticiones HTTP
 
# Configuración
dominio = 'www.bilbao.net'
ruta_imagen = '/astenagusia2008/cartel2008.jpg'
dir_downloads = '/home/gorka/'
imagen_local = 'cartel_aste_nagusia.jpg'
 
# Descargamos la imagen. Usamos try/except por si hay algún
# error en la conexión
try:
	# conectamos con el servidor
	conn = httplib.HTTPConnection(dominio)
	# hacemos la petición a la imagen
	conn.request ("GET", '/' + ruta_imagen)
	r = conn.getresponse()
	# abrimos o creamos el fichero donde vamos a guardar la imagen
	fichero = file( dir_downloads + '/' + imagen_local, "wb" )
	# guardamos la imagen en el fichero
	fichero.write(r.read())
	# y cerramos el fichero
	fichero.close()
except:
	print "No se ha podido descargar la imagen"

Puedes hacer las pruebas cambiando los parámetros de configuración:

dominio: En este ejemplo es el del ayuntamiento (sin ‘http://’), sólo el dominio, en este caso bilbao.net.

ruta_imagen: es la ruta completa a la imagen, en este caso ‘astenagusia2008/cartel2008.jpg’.

(La URL completa de la imagen es: http://www.bilbao.net/astenagusia2008/cartel2008.jpg)

dir_downloads: directorio de nuestro ordenador donde queremos guardar la imagen (debe tener permiso de escritura).

imagen_local: nombre del archivo con el que queremos guardar la imagen.

Es posible que el servidor de donde intentes bajar la imagen no lo permita. Para saber si hay algún problema de ese tipo puedes imprimir el resultado de r.read() para ver si aparece algún mensaje de error (como ‘imagen no disponible’ o algo similar).