jueves, 30 de junio de 2011

auque un poco ausente de python ... solo por cuestiones labores y envuelto en el mundo web con java pronto volvere actualizar todos los temas de python!!!!

lunes, 24 de agosto de 2009

INTRODUCCION A MYSQL


Este artículo es el primero de toda una serie que ofrecerá MySQL AB con el fin de proporcionar al lectorinformación valiosa sobre el servidor de bases de datos MySQL. Aunque en artículos futuros se tratarán temas más avanzados tales como replicación, ODBC y optimización, se pensó que era prudente si el primer tutorial comenzaba por los conceptos básicos. Por lo tanto, el objetivo de este artículo es informar al lector sobre varios temas relacionados con el funcionamiento básico de MySQL. Una sinopsis de los temas que serán cubiertos se muestra a continuación en la Tabla de contenidos.

Conectándose a MySQL por primera vez
El programa cliente de MySQL, también conocido como el monitor de MySQL, es una interfase que le permite a un usuario conectarse a un servidor MySQL, crear y modificar bases de datos, y ejecutar consultas y ver los resultados. Este programa es iniciado ejecutando el comando mysql en el prompt del sistema. En general, la sintaxis de este comando es:

shell> mysql [opciones] [base de datos]
En donde [opciones] pueden ser una o más opciones usadas en conjunción con el programa mysql, y [base de datos] es el nombre de la base de datos sobre la que se va a trabajar. Puesto que se asume que es la primera vez que se usa el monitor MySQL, debe tomarse un momento para revisar todas las opciones permitidas al ejecutar el siguiente
comando:
shell> mysql --help
Éste produce una larga lista de opciones que se pueden usar en conjunción con el programa mysql. Por el momento, sin embargo, la meta principal es simplemente conectarse al servidor de bases de datos. Por lo tanto, hay que ejecutar el siguiente comando:

shell> mysql -u root

Deberá de aparecer algo como los siguiente: Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8 to server version: 3.23.28-gamma-log Type 'help;' or '\h' for help. Type '\c' to clear the buffer
mysql>
Felicidades, ahora se encuentra conectado al monitor de MySQL como el usuario root (eltodopoderoso). La primera acción oficial como el líder supremo del servidor de bases de datos MySQL es asegurarse que nadie más pueda declararse en esta posición, haciendo esto posible cuando alguién quiera conectarse como root al servidor deba de proporcionar una contraseña. Hay que cambiar la contraseña de su valor actual (un valor nulo), a algo difícil de adivinar usando el siguiente comando:

mysql>SET PASSWORD FOR 'root'@'localhost' = PASSWORD('la_palabra_secreta');
El "root", que es el nombre de usuario, y "localhost", que es el nombre del host, constituyen un usuario único en MySQL. Para aquellos lectores no familiarizado con la terminología de redes, 'localhost' es un nombre usado para referirse al servidor local; en este caso, el servidor sobre el cuál reside MySQL. Por lo tanto, al escribir 'root'@'localhost', este comando le dice al servidor MySQL que ponga la contraseña para un usuario denominado 'root' que se conectará específicamente del servidor local('localhost').

Más específicamente, este comando cambiará la contraseña actualizando lo que se conocecomúnmente como la tabla de privilegios de MySQL. Estas tablas, que se encuentran en la base de datos llamada mysql, contienen información con respecto a las capacidades de conexión y uso de todos los usuarios que intentan usar el servidor de base de datos MySQL. Más específicamente, estecomando actualiza la tabla user, actualizando el campo password de la fila en la cual el valor de campo user es root. El campo password se actualizará con el valor encriptado de la cadena que se pasa como parámetro a la función PASSWORD().
Por supuesto, no olvidar esta contraseña. Puesto que ésta es almacena en un texto encriptado dentro de la base de datos, no puede ser recuperada fácilmente en el caso de que sea olvidada.Hay también un método alternativo para actualizar la contraseña:

shell> mysqladmin -u root password 'la_palabra_secreta'

Este comando tiene el mismo efecto que el comando introducido previamente. Salir y conectarse de nuevo al monitor MySQLPara verificar que la nueva contraseña funciona, salir del monitor MySQL usando el siguiente comando:
mysql> \q
Esto nos regresa al shell del sistema. Ahora hay que volver al monitor, pero esta vez usando el siguiente comando:
shell> mysql -u root -p

El hacer esto resulta en un prompt para proporcionar la contraseña del usuario root, como se muestra a continuación:
Enter password:
En este punto, debe de suministrarse la contraseña que se asignó al usuario root previamente. Asumiendo que ésta ha sido proporcionada correctamente, aparecerá el saludo estándar de MySQL, y root será conectado al servidor MySQL una vez más.
¡Cuidado con esa contraseña!
Muchos lectores pueden estar tentados a incluir la contraseña en la misma línea de comandos, como sigue:
shell> mysql -u root -pla_palabra_secreta

¡No hacer esto!. ¡No sólo es un método sumamente inseguro para proporcionar la contraseña, sino que no producirá los resultados esperados!. Es inseguro porque permitirá a cualquier persona observar la contraseña en este formato de texto plano, pero también porque cualquier usuario puede usar el comando 'ps' de Unix para ver los comandos que se están ejecutando y ver allí la contraseña en su formato de texto plano. Quizás sea una buena idea almacenar la contraseña en nuestro archivo de configuración my.cnf,localizado en~/.my.cnf . Si usted no sabe lo que es este archivo, lea por favor la sección previa titulada El archivo de configuración de MySQL.
Seleccionado una base de datos Por supuesto, simplemente conectarse al servidor MySQL no servirá de mucho. Seguramente se querrá seleccionar una base de datos para trabajar sobre con ella. Esto se puede hacer de dos
maneras:
Una de ellas es incluir el nombre de la base de datos al ejecutar el comando mysql. Por ejemplo, para conectarse al servidor MySQL y seleccionar al mismo tiempo la base de datos es:

shell> mysql -u root -p mibasededatos

Tal vez cause algo de confusión en algunos lectores, ya que parece que se proporciona la palabra "mibasededatos" como la contraseña del usuario root. Esto no es correcto. Hay que tomarse un momento para revisar la sintaxis que se describe en la salida del comando mysql --help, puesto que debe de ser evidente que "-u root -p" se refiere a la parte de [opciones] y "mibasededatos" a la parte de [base de datos].

La otra manera de seleccionar una base de datos, es cuando uno ya está conectado al servidor MySQL. Para ello se debe usar el siguiente comando:
mysql> use mibasededatos
Una vez ejecutado cualquiera de los dos comandos, todas las consultas se dirigirán hacia la base dedatos hipotética mibasededatos.mysqladmin El programa mysqladmin se usa para administrar varios aspectos del servidor de bases de datos MySQL. Al usarlo, el administrador puede realizar las tareas tales como: crear y eliminar bases de datos, dar de baja el servidor MySQL, actualizar las tablas de privilegios, y ver los procesos que se están corriendo (ejecutando) dentro de MySQL. La sintaxis general es:
shell> mysqladmin [opciones] comando(s)

Dónde [opciones] puede ser toda una serie de opciones usadas en conjunción con el programa mysqladmin. Puesto que se asume que es la primera vez que se usa el programa mysqladmin, se recomienda que se revisen con detalle todas las opciones que se tienen permitidas con este programa.Usar el siguiente comando:
shell> mysqladmin --help
Esto produce una larga lista de las opciones que se pueden usar en conjunción con el programa de mysqladmin. Para demostrar como son usadas estas opciones, a continuación vamos a decirle a mysqladmin que necesitamos crear una base de datos llamada widgets, la cuál será usada a través del resto de este artículo para ver otras funciones útiles de MySQL.Una base de datos se crea como sigue:
shell> mysqladmin -u root -p create widgets
Enter Password:
Después de la ejecución, mysqladmin creará la base de datos y regresará el shell del sistema. Típicamente, el siguiente paso consiste en asegurar la nueva base de datos modificando las tablas de privilegios. Los detalles de como se hace esto es el objetivo de la siguiente sección. Lecturas adicionales en la documentación de MySQL (en inglés): 14.5 Administering a MySQL Server Asegurando una base de datos La seguridad debe ser lo primero que debe venir a la mente de un administrador de MySQL después de crear una base de datos. Como se discutió en la sección "Las tablas de privilegios", asegurar una base de datos consiste en hacer modificaciones a las tablas que se encuentran en la base de datos llamada mysql. En esta sección, el lector aprenderá a asegurar la base de datos widgets que se creó anteriormente. Antes de hacer esto, se va a realizar un breve resumen de cómo son modificadas las tablas de privilegios. Hay dos métodos usados para modificar las tablas de privilegios. El primero es a través del uso de las sentencias típicas de SQL tales como INSERT, UPDATE, y DELETE. Sin embargo el uso de este método ha sido depreciado para introducir el segundo método, el cual involucra el uso de los comandos especiales GRANT yREVOKE. Por lo tanto, sólo este método se discutirá a continuación.
El comando GRANT
La función del comando GRANT es crear nuevos usuarios, y asignarle sus privilegios. Su sintaxis es:
GRANT priv_type [(column_list)] [, priv_type [(column_list)] ...]
ON {tbl_name * *.* db_name.*}
TO user_name [IDENTIFIED BY 'password']
[, user_name [IDENTIFIED BY 'password'] ...]
[WITH GRANT OPTION]

Una comprensión de cómo trabaja el comando GRANT se obtiene mejor a través de ejemplos. En el ejemplo siguiente, el comando GRANT es usado para agregar un nuevo usuario a la base de datos. Este usuario será usado para accesar a la base de datos widgets:

mysql>GRANT usage ON *.* TO widgetAdmin@localhost
->IDENTIFIED BY 'ilovewidgets';

Esto creará un nuevo usuario denominado widgetAdmin, capaz de conectarse al servidor MySQL vía el localhost usando la contraseña ilovewidgets. Hay que tener presente que sólo se otorgan privilegios de conexión, no se permitirá que el usuario haga nada en el servidor MySQL. Vamos a proseguir cambiándonos a la base de datos mysql y ejecutando la siguiente consulta:

mysql> SELECT * FROM user;
Nótese que la fila conteniendo el usuario widgetAdmin tiene valores N para todos los demás privilegios.Esto es bueno, puesto que la tabla user contiene privilegios de carácter global. Para clarificar esto, si un valor "Y" se pone para cualquier privilegio en la tabla user, ese usuario puede aplicar ese privilegio a cualquier base de datos de MySQL. Por lo tanto, casi siempre será mejor si todos los privilegios están en "N" dentro de esta tabla. ¿Bien, entonces como se asignan los privilegios a un usuario para una base de datos en particular?. Esto se hace fácilmente modificando ligeramente el comando GRANT del ejemplo previo. Por ejemplo, si asumimos que el administrador quiere otorgar los privilegios SELECT, INSERT, UPDATE y DELETE al usuario widgetAdmin sobre la base de datos widgets, se tendría que usar el comando GRANT de la siguiente manera:
mysql>GRANT SELECT, INSERT, UPDATE, DELETE
->ON widgets.* TO widgetAdmin@localhost;
Después de la ejecución de este comando, el usuario widgetAdmin puede inmediatamente hacer uso de estos privilegios. Los privilegios introducidos aquí no son los únicos disponibles para el administrador. La tabla 1-proporciona un lista de todos los privilegios disponibles.
Tabla 1-1: privilegios disponibles para usar con el comando GRANT y REVOKE

ALL PRIVILEGES FILE RELOAD
ALTER INDEX SELECT
CREATE INSERT SHUTDOWN
DELETE PROCESS UPDATE
DROP REFERENCES USAGE

Para ver los nuevos privilegios que han sido asignados, se puede ejecutar la siguiente consulta:
mysql> SELECT * FROM db;
Nótese que una fila ha sido agregada a la tabla "db" para el registro del usuario widgetAdmin, con los
valores "Y" asignados a los campos SELECT, INSERT, UPDATE y DELETE.
Aunque en este ejemplo se mostró como crear un usuario, y posteriormente como asignarle los
privilegios, es posible hacer estos dos pasos en uno solo, al ejecutar el comando GRANT visto
anteriormente, con una ligera variante.
mysql>GRANT SELECT, INSERT, UPDATE, DELETE
->ON widgets.* TO widgetAdmin@localhost
->IDENTIFIED BY 'ilovewidgets';
Asumiendo que el usuario widgetAdmin aún no existe cuando se ejecute esta consulta, tanto la tabla
"user", como la tabla "db" serán actualizadas con las registros necesarios.
Por supuesto, el administrador puede revocar los privilegios otorgados en cualquier momento. Éste es el
objetivo que se cubre en la siguiente sección.
La sentencia REVOKE
La sentencia REVOKE es usada para rescindir los privilegios que han sido previamente otorgados a un
usuario. Su sintaxis es la siguiente:
REVOKE priv_type [(column_list)] [, priv_type [(column_list)] ...]
ON {tbl_name * *.* db_name.*}
FROM user_name [, user_name ...]
Como en el caso del comando GRANT, quizás la mejor manera de entender realmente cómo funciona
este comando es experimentado con varios ejemplos. Asumiendo que el administrador tiene la
necesidad de revocar el privilegio DELETE que había sido otorgado al usuario widgetAdmin, se tiene
que ejecutar el comando REVOKE de la siguiente manera:
mysql>REVOKE DELETE ON widgets.*
->FROM widgetAdmin@localhost;
Un punto que se debe tener presente es que mientras REVOKE puede quitar todos los privilegios
otorgados a un usuario (inclusive los privilegios de conexión), éste no quita al usuario de la tabla de
privilegios. Para ilustrar esto, hay que considerar el siguiente comando:
mysql>REVOKE ALL PRIVILEGES ON widgets.*
->FROM widgetAdmin@localhost;

¡Mientras esto tendría como resultado que sean revocados todos los privilegios del usuariowidgetAdmin, éste no borraría registros trascendentes de las tablas de privilegios!. Si lo que se desea es borrar completamente a un usuario de la base de datos se tiene que hacer uso de la sentencia DELETE como sigue:
mysql>DELETE FROM user WHERE user = 'widgetAdmin';
Query OK, 1 row affected (0.00 sec)
mysql>FLUSH PRIVILEGES;

Cuando ha sido ejecutada esta sentencia, se elimina a un usuario de manera definitiva de la tabla de privilegios. Lecturas adicionales en la documentación de MySQL (en inglés): Grant and Revoke Syntax Respaldos de bases de datos El concepto final discutido en este tutorial es uno verdaderamente importante: respaldos de datos. En esta sección se discuten dos métodos de hacer respaldos de datos y de las estructuras de las bases de datos MySQL, los comandos mysqldump y mysqlhotcopy.mysqldump El comando mysqldump proporciona una manera conveniente para respaldar datos y estructuras de tablas. Hay que notar que mientras el comando mysqldump no es el método más eficiente para crear respaldos (mysqlhotcopy se describe a continuación), éste ofrece un medio conveniente para copiar datos y estructuras de tablas que puede ser usado para "poblar" otro servidor SQL, no importando si se trata, o no de un servidor MySQL. El comando mysqldump puede ser usado para crear respaldos de todas las bases de datos, algunas bases de datos, sólo una de ellas, o incluso ciertas tablas de una base de datos dada. En esta sección se ilustra la sintaxis involucrada con varios posibles escenarios, seguida con unos pocos ejemplos. Usando el comando mysqldump para respaldar sólo una base de datos:
shell> mysqldump [opciones] nombre_base_datos
Usando el comando mysqldump para respaldar varias tablas de una base de datos:
shell> mysqldump [opciones] nombre_base_datos tabla1 tabla2. . . tablaN
Usando mysqldump para respaldar varias bases de datos:
shell> mysqldump [opciones] --databases [opciones] nombre_bd1 nombre_bd2...

Usando mysqldump para respaldar todas las bases de datos:
shell> mysqldump [opciones] --all-databases [opciones]

Las opciones pueden ser vistas ejecutando el siguiente comando:

shell> mysqldump --help
- Ejemplos -
Respaldar ambos, la estructura y los datos encontrados dentro de la base de datos widgets puede ser
realizado como sigue:

shell> mysqldump -u root -p --opt widgets

Alternativamente, quizás se requiera respaldar únicamente los datos, esto es logrado al incluir la opción --no-create-info, lo que significa que no se creen los datos relativos a la creación de las tablas.
shell>mysqldump -u root -p --no-create-info widgets
Otra variación es respaldar únicamente la estructura de las tablas, esto es logrado al incluir la opción --nodata, que significa la no creación de los datos de las tablas.

shell>mysqldump -u root -p --no-data widgets
Si se está planeando usar mysqldump con el fin de respaldar datos para que puedan ser movidos a otro servidor MySQL, es recomendado que se use la opción "--opt". Esto nos dará un respaldo optimizado de los datos que tendrá como resultado un tiempo más rápido de lectura cuando se quieran cargar los datos en otro servidor MySQL. Mientras mysqldump proporciona un método conveniente para respaldar datos, hay un segundo método, el cuales más rápido, y más eficiente. Esto se describe en la siguiente sección. mysqlhotcopy El comando mysqlhotcopy es un script de Perl que usa varios comandos SQL y del sistema para respaldar una base de datos. Más específicamente éste bloquea las tablas, limpia las tablas, hace una copia, y desbloquea las tablas. Aunque este sea el método disponible más rápido para respaldar una base de datos MySQL, éste se limita a respaldar sólo las bases de datos que residen en la misma
máquina en la cual está siendo ejecutado el comando mysqlhotcopy.

El comando mysqlhotcopy puede ser ejecutado para respaldar una base de datos, varias bases de datos, o sólo las bases de datos cuyo nombre coincida con una expresión regular. En esta sección, se muestra la sintaxis involucrada con cada posible escenario seguida de unos pocos ejemplos. Usando mysqlhotcopy para respaldar sólo una base de datos:
shell> mysqlhotcopy [opciones] nombre_bd /ruta/nuevo/directorio Usando mysqlhotcopy para respaldar varias
bases de datos:
shell> mysqlhotcopy [opciones] nombre_bd1.. nombre_bdN /ruta/nuevo/directorio
Usando mysqlhotcopy para respaldar sólo las tablas de una base de datos cuyo nombre coincida con
una expresión regular:
shell> mysqlhotcopy [opciones] nombre_bd./expresionregular/
Las opciones completas del comando mysqlhotcopy pueden ser vistas ejecutando el siguiente comando:
shell> mysqlhotcopy --help
- Ejemplos -
Para usar mysqlhotcopy para respaldar la base de datos widgets al directorio "/usr/mysql/backups/" setiene que ejecutar el siguiente comando:

shell>mysqlhotcopy -u root -p widgets /usr/mysql/backups
Un segundo ejemplo asume que la base de datos widgets contiene las tablas "productos2000", "productos2001", "clientes2000", y "clientes2001", con los cuatro digitos representando los datos correspondientes a cada año. Si se desean respaldar las tablas relativas al año "2000", el comando mysqlhotcopy tendría que usarse como sigue:

shell> mysqlhotcopy -u root -p widgets./^.+('2000')$/ /usr/mysql/backups
En el ejemplo de arriba, la expresión /^.+('2000')$/ le dice a mysqlhotcopy que respalde sólo las tablas
cuyo nombre finalice con la cadena "2000".

GUI EN PYTHON

TKINTER:

TkInter (de TK Interface) es un módulo que nos permite construir interfaces gráficas de usuario multiplataforma en Python utilizando el conocido toolkit Tk. Python incluye este módulo por defecto, lo que hace que sea un tookit muy popular. TkInter, además, es robusto, maduro y muy sencillo de aprender y de utilizar, contando con una amplia documentación. Por otro lado hasta la versión 8.5 Tk era famoso por lo poco atractivo de sus widgets (cosa que se podía solucionar hasta cierto punto gracias a Tile). No es hasta esta versión que contamos con cosas tan básicas como textos con antialiasing en X11 o widgets como Treeview. En esta versión también se incluye Tile por defecto, por lo que contamos con un mejor aspecto general para todas las plataformas. Sin embargo Python no se distribuye con Tk 8.5 hasta la versión 2.6, por lo que, para versiones de Python anteriores, es necesario recompilar TkInter para Tk 8.5 por nuestra cuenta, o bien usar Tile si no necesitamos ninguna de las nuevas características. Es más, para poder usar la mayor parte de las nuevas características de Tk 8.5 es necesario instalar una librería que actúe como wrapper de Ttk (el nombre con el que han dado en llamar al conjunto de los nuevos widgets y temas de Tk), como puede ser pyttk. TkInter se distribuye bajo la PSFL (Python Software Foundation License) una licencia compatible con la GPL creada para la distribución de software relacionado con el proyecto Python. La PSFL carece de la naturaleza viral de la GPL, por lo que permite crear trabajos derivados sin que estos se conviertan necesariamente en software libre. Por último, veamos una pequeña aplicación de ejemplo escrita con TkInter, sin uso de eventos y con solo unos pocos widgets, que nos servirá para comparar el aspecto de los distintos toolkits.
//////////////////////////////////////////////////////////////////
from Tkinter import * root = Tk() frame = Frame(root) frame.pack() label = Label(frame, text="Hola mundo" ) c1 = Checkbutton(frame, text="Uno" ) c2 = Checkbutton(frame, text="Dos" ) entry = Entry(frame) button = Button(frame, text="Aceptar" ) label.pack() c1.pack() c2.pack() entry.pack() button.pack() root.mainloop() ////////////////////////////////////////////////////////////////////// 750) this.width=750" border=0> Pros: Popularidad, sencillez, documentación.

Contras: Herramientas, integración con el sistema operativo, lentitud. Recomendado para: ¿Prototipos rápidos?

WXPYTHON :

wxPython es un wrapper open source para el toolkit anteriormente conocido como wxWindows: wxWidgets. wxPython es posiblemente el toolkit para desarrollo de interfaces gráficas en Python más popular, superando incluso a TKinter, que, como comentamos, se incluye por defecto con el intérprete de Python. wxPython cuenta con más y mejores widgets que TKinter, y ofrece un muy buen aspecto en todas las plataformas, utilizando MFC en Windows y GTK en Linux. wxPython cuenta además con herramientas muy interesantes como wxGlade, una aplicación RAD para diseñar las interfaces gráficas de forma visual. Sin embargo, la API adolece de una cierta falta de consistencia y un estilo muy alejado de Python y más cercano a C++, ya que, de hecho, uno de sus objetivos es no distanciarse demasiado del estilo de wxWidgets. Esto ha provocado que hayan aparecido distintos proyectos para abstraer al programador de los entresijos del toolkit, como Dabo o Wax, aunque estos han tenido un éxito muy comedido. Tanto wxPython como wxWidgets se distribuyen bajo una licencia "wxWindows Licence", que consiste esencialmente en una LGPL con la excepción de que las obras derivadas en formato binario se pueden distribuir como el usuario crea conveniente. Algunos ejemplos de aplicaciones conocidas creadas con wxPython son DrPython, wxGlade, Boa Constructor, Stani's Python Editor y ABC. Finalmente, a continuación podéis consultar el código de la aplicación de ejemplo:
///////////////////////////////////////////////////////////////////// import wx class Frame(wx.Frame): def __init__(self): wx.Frame.__init__(self, parent=None) panel = wx.Panel(self) text = wx.StaticText(panel, -1, "Hola mundo" ) c1 = wx.CheckBox(panel, -1, label="Uno" ) c2 = wx.CheckBox(panel, -1, label="Dos" ) t = wx.TextCtrl(panel) b1 = wx.Button(panel, -1, label="Aceptar" ) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(text, 0, wx.ALL, 0) sizer.Add(c1, 0, wx.ALL, 0) sizer.Add(c2, 0, wx.ALL, 0) sizer.Add(t, 0, wx.ALL, 0) sizer.Add(b1, 0, wx.ALL, 0) panel.SetSizer(sizer) panel.Layout() app = wx.App(redirect=True) Frame().Show() app.MainLoop()
///////////////////////////////////////////////////////////////// 750) this.width=750" border=0> Pros: Popularidad, herramientas, multiplataforma. Contras: API muy poco pythonica. Recomendado para: Desarrollo
multiplataforma.

PYGTK:

Posiblemente PyGTK sea la segunda opción más utilizada para la creación de interfaces gráficas con Python, solo por detrás de wxPython, con la que compite de tú a tú. PyGTK, como podemos suponer por su nombre, es un binding de GTK, la biblioteca utilizada para desarrollar GNOME. PyGTK cuenta con una API muy clara, limpia y elegante y es, además, muy sencillo de aprender, solo superado en ese aspecto por Tkinter. PyGTK también cuenta con grandes herramientas para construir la interfaz de forma gráfica, como Glade o Gazpacho. Un punto negativo es que, hasta hace poco, era necesario instalar X11 para poder usar PyGTK en Mac OS, dado que GTK no había sido portado. Actualmente se puede utilizar el GTK+ OS X Framework que se encuentra todavía en versión beta. PyGTK se distribuye bajo licencia LGPL. Algunas aplicaciones escritas con PyGTK son Deluge, Exaile, Listen, Envy, WingIDE, DeVeDe o emesene. Veamos el código de la aplicación de ejemplo en
PyGTK: ////////////////////////////////////////////////////////////////////// import pygtk import gtk window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.connect("destroy", gtk.main_quit) box = gtk.VBox(False, 0) window.add(box) label = gtk.Label("Hola Mundo" ) c1 = gtk.CheckButton(label="Uno" ) c2 = gtk.CheckButton(label="Dos" ) entry = gtk.Entry() button = gtk.Button("Aceptar" ) box.add(label) box.add(c1) box.add(c2) box.add(entry) box.add(button) window.show_all() gtk.main() /////////////////////////////////////////////////////7 750) this.width=750" border=0>

Pros: Popularidad, sencillez, herramientas.
Contras: Ligeramente más complicado de instalar y distribuir en Mac OS. Recomendado para: Cualquier tipo de aplicación. Especialmente interesante para Gnome.

PYQT :

Es posible que PyQt, el binding de Qt para Python, sea la menos popular de las cuatro opciones, aunque es un toolkit sencillo de utilizar y con muchas posibilidades. Es especialmente interesante para el desarrollo en KDE, dado que Qt es la librería utilizada para crear este entorno. No obstante el interés en Qt no se limita a KDE, sino que es una biblioteca multiplataforma que, además, desde la versión 4, utiliza widgets nativos para las distintas plataformas (anteriormente Qt emulaba el aspecto de la plataforma en la que corría). Como aplicación de RAD se puede utilizar Qt Designer. PyQt utiliza un modelo de licencias similar al de Qt, con una licencia dual GPL/PyQt Commercial. Si nuestro programa es compatible con la licencia GPL, es decir, si vamos a publicar el código fuente y permitir a los usuarios modificar nuestra aplicación, podremos usar PyQt sin más preocupaciones. En caso contrario tendremos que pagar para obtener una licencia comercial. Un par de ejemplos de aplicaciones que usan PyQt son Eric y QTorrent. El código de la aplicación de ejemplo en PyQt tendría el siguiente aspecto:
/////////////////////////////////////////////////////////////////////// import sys from PyQt4.QtCore import * from PyQt4.QtGui import * class Form(QWidget): def __init__(self): QWidget.__init__(self) layout = QVBoxLayout() layout.addWidget(QLabel("Hola mundo" )) layout.addWidget(QCheckBox("Uno" )) layout.addWidget(QCheckBox("Dos" )) layout.addWidget(QLineEdit()) layout.addWidget(QPushButton("Aceptar" )) self.setLayout(layout) app = QApplication(sys.argv) form = Form() form.show() app.exec_
() //////////////////////////////////////////////////////////////////7 750) this.width=750" border=0> Pros: Sencillez, herramientas, multiplataforma. Contras: Ligeramente más complicado de instalar y distribuir en Mac OS. Licencia. Recomendado para: Cualquier tipo de aplicación. Especialmente interesante para KDE.

viernes, 6 de marzo de 2009

EVALUANDO PRIMEROS TEMAS:

Bueno aqui ire colocando ejercicios de toda indole claro algo muy basico para poner en practica todos los temas basicos vistos hasta ahora:

lista
tuplas
diccionarios
expresiones y sentencias
sentecias condicionales como:

if:
aqui tenemos el if y mostramos como simular el switch el python a traves de un diccionario ya que en python no existe propiamente.

while:
en la parte de los bucles tenemos el while y a traves de un while infinito simulamos un do while en Python.

for:
nuestro famoso for que en python se utliliza para recorrer secuencias ;)

La entradas de datos con input() asi que tenemos varios tenemas para poder hacer ejercicios basicos asi que yo ire proponiendo algunos para ver las mejores soluciones posibles y espero que ustedes tambien propongas ejercicios:

EJERCICIO 1:

Traduce las siguientes expresiones matematicas a Python y evalualas. Trata de utilizar el menor numero de parentesis posible.

a) 2+(3.(6/2))

b) 4+6/2+3

c) (4/2)^5

d)(4/2)^(5+1)

e)(-3)^2

f)-(3^2)

EJERCICIO 2:

Son validos los siguientes identificadores:

1)Identificador
2)indice/dos
3)dos palabras
4)__
5)12horas
6)hora12
7)var!
8)from
9)uno.dos
10)'var'

EJERCICIO 3:

Diseña un programa que, a partir del valor de la base y de la altura de un triangulo (3 y 5 metros, respectivamente), muestre el valor de su area (en metros cuadrados).Recuerda que el area A de un triangulo se puede calcular a partir de la base b y la altura h como A = bh/2.

EJERCICIO 4:

Escribe un programa en Python para pasar un número de binario a decimal y de decimal a binario.


EJERCICIO 5:

Escribe un programa en Python que reciba dos listas y devuelva los elementos comunes a ambas, sin repetir ninguno (intersección de conjuntos).

EJERCICIO 6:

Escribe un programa en Python que reciba dos listas y devuelva los elementos que pertenecen a una o a otra, pero sin repetir ninguno (unión de conjuntos).

EJERCICIO 7:

Escribe un programa en Python que reciba dos listas y devuelva los elementos que pertenecen a la primera pero no a la segunda, sin repetir ninguno (diferencia de conjuntos).


EJERCICIO 8:

Escribe un programa en Python que, dada una lista de números,devuelva otra lista que sólo incluya los números impares.


EJERCICIO 9:

Escribe un programa en Python que, dada una lista de nombres y unaletra, devuelva una lista con todos los nombres que empiezan por dicha letra.

EJERCICIO 10:

Escribe un programa en Python que, dada una lista de números,devuelva otra lista con sólo aquellos números que son primos.

EJERCICIO 11:

Escribe un programa en Python que, dadas dos listas de números,devuelva una lista con todos los pares de números que podemos formar con uno de la primera lista y otro de la segunda.

EJERCICIO 12:

Escribe un programa en Python que, dada una lista de cadenas, devuelva la cadena más larga. Si dos o más cadenas miden lo mismo y son las más largas, la función devolverá una cualquiera de ellas.

EJERCICIO 13:

Escribe un programa en Python que, dada una lista de cadenas, devuelva una lista con todas las cadenas más largas, es decir, si dos o más cadenas miden lo mismo y son las más largas, la lista las contendrá a todas.

EJERCICIO 14:

Escribe un programa en Python que reciba una lista de cadenas y devuelva el prefijo común más largo. Define una función que, dado un número de DNI, devuelva la letra


EJERCICIO 15:

que le corresponde: dividir el número por 23 y utilizar el resto como índice de la cadena "TRWAGMYFPDXBNJZSQVHLCKE".


EJERCICIO 16:

Diseña un programa que lea una cadena y un entero k y muestre todas las subcadenas de longitud k.

EJERCICIO 17:

reescriba el codigo para que sea funcional en Python 3.x

print Dime tu nombre, nombre

nombre = int(raw_imput())

print Hola nombre


EJERCICIO 18:

La idea es hacer un algoritmo que el ordenador elije un numero y nosotros tenemos que adivinarlo en menos de los intentos que le digamos.El ordenador nos dará pistas de si nuestro numero es mayor o menor que el secreto.

Ayuda:

import random



EJERCICIO 19:

Realiza el juego del ahorcado:

a)con listas
b)sin listas

por ahora la palabra adivinar sera una variable ejem: palabra="adiviname", el maximo de oportunidades el que mejor les parezca.

se gana:

cuando se obtenga la palabra oculta por completo antes de agotar nuestro numero de oportunidades.

se pierde:

finaliza nuestro maximo de oprtunidades sin descubrir la palabra oculta

A Programar!!!!

ENTRADA DE DATOS EN PYTHON

Que seria de un progama sin la interaccion con los usuarios :) pues no servirian de a mucho, bueno sin tanto rodeos veamos:

def main():

____n = int(input("Digite:"))
____print (n*2)

if __name__=="__main__":main()

En Python 2.x tenemos input() para capturar enteros y float y raw_input() para cadenas pero desde python3.x solo existe input() y el valor que capture es siempre una cadena asi que si queremos que sea entero lo covertimos con la funcion int() y si queremos un float con la funcion float() convertimos el dato capturado a float asi:

def main():
____n = float(input("Digite:"))
____print (n*2)

if __name__=="__main__":main()

para capturar una simple cadena de texto seria:

def main():
____n = input("Digite:")
____print (n*2)

if __name__=="__main__":main()

Bueno si queremos validar datos para eso tenemos las excepciones en python pero ese tema lo veremos mas adelante por ahora miren un ejemplo que hize para capturar solo un entero:

def main():
____while True:
______try:
______n = int(input("Digite:"))
______break

____except:
______print ("Solo digite enteros")


if __name__=="__main__":main()

Bueno lo que hize es hacer un ciclo infinito de esta forma simulamos una do-while de otros lenguaje con python capturo un valor por teclado y si es entero break rompe el ciclo y termina, pero sino se produce una excepcion en python pero como definimos nuestro propio except nos muestra el mensaje de Solo digite enteros y vuelve a pedir que digite hasta que sea un entero :)

jueves, 5 de marzo de 2009

SENTENCIAS CONDICIONALES

Para escribir programas utiles, casi siempre necesitamos la capacidad de comprobar ciertas condiciones y cambiar el comportamiento del programa segun la condicion presente. Las sentencias condicionales nos dan esta capacidad. La forma mas asencilla es la sentencia if:

SENTENCIA IF:

def main():
numero=-8
if numero > 0:
____print"positivo"
else:
____print"negativo"


if __name__ == "__main__":main()

como vemos manejo un if con un else si numero es mayor que cero imprima positivo else (sino)
pues imprima negativo asi de sencillo :)

ok aqui tenemos otra:

if numero < style="color: rgb(0, 0, 0);">____print “Negativo”
elif numero > 0:
____print “Positivo”
else:
____print “Cero”

el primer lugar verifica que numero sea menor que cero si se evalua con verdadera imprime negativo y elif y else se ignoran, pero si numero no es negativo pregunto si es mayor que 0, si se evalua como verdadera imprimo positivo y else se ignora, si el numero no es negativo ni mayor a cero pues blanco es y gallina lo pone :) pues el ultimo else imprime cero ya que evaluaron como false el if y el elif.

Bueno ahora biene el switch pero momento en python no existe :( pero se podria emular con un simple diccionario :) veamos como:

def hola():
____print "hola"
def adios():
____print "Bye"

def defecto():
_____print "soy el default"


def main():
____switch = {
____0:hola,
____1:adios

____}
____switch.get(5,defecto)()



if __name__ == "__main__":main()

Bueno defini tres funciones hola() que solo imprime hola, adios() que solo se despide y la funcion default o por defecto si se acuerda para lo que han trabajado en otros lenguajes esta se ejecuta cuando si dentro de las opciones switch no coinciden con ninguna pues pasa a ejecutar dicha funcion, lo que llame switch ={0:hola,1:adios} este es nuestro diccionario y con la funcion get que esta explicada en el post de diccionario la funcion busca la key que le paso como argumento si la encuentra en el diccionario me ejecuta la funcion segun la key si no pues me ejecuta la funcion que le paso como parametro opcional en ese caso de no encontrar una key en especifico en el diccionario, como ven yo le paso 5 al buscar el el diccionario solo existe el 0 y el 1 asi que no existe dicho key y al pasar eso pues me imprime mi funcion por defecto () :) si le pasan el 1 pues va imprimir adios, bueno solo practiquen y si les da algun error me lo hacen saber ;)


BUCLES:

Mientras que los condicionales nos permiten ejecutar distintos fragmentos de código dependiendo de ciertas condiciones, los bucles nos permiten ejecutar un mismo fragmento de código un cierto número de veces, mientras se cumpla una determinada condición.


WHILE():
Veamos un ejemplo pra ver como funciona el while imprimiendo los numeros del 1 al 10:

def main():
i=1
while i <= 10: ____print i
____i+=1
if __name__ == "__main__":main()

Bueno esto es muy facil tambien aqui i inicia valiendo 1 el cual while lo evalua preguntando si i en menor o igual a 10 en caso de ser verdadero ejecuta el print y aumenta en uno el i la expresion i+=1 es igual que decir i=i+1 o i++ ahora i vale 2 y pregunta de nuevo 2 es menor o igual a 10 verda!!! pues imprime i y aumenta la i y ahora vale 3 bueno en ese ciclo va dura hasta que llegue a 10 y pregunta 10 es menor o igual a 10 verda!! son igual imprime 10 y aumenta i y ahora vale 11 vuelve y pregunta 11 es menor o igual 10 ni menor ni gual asi que falso!!!! y termina el ciclo :)
una explicacion muy prekinder pero vale si sos totalmende principiante :)

tambien lo podemos hacer en un ciclo infinito y preguntar que si llego a 10 para romper nosotros mismos el ciclo:


def main():
___i=1
___while True:
_____print i
_____if i==10:
________break
i+=1


if __name__ == "__main__":main()

Aqui lo que hago es un ciclo infinito no saben que es infinito ?? pues eliminen del algoritmo el break y lo corren asi y me dicen si algun dia termina de ejecutarse :) aqui el simpre va imprimir i
que incia en 1 bueno la imprime y de inmediato preguntamos que si la i vale 10 sino aumenta en 1
asi sigue imprimiendo 1 2 3 4 5 6 7 8 9 10 cuando llega a 10 se jecuta el break que rompe el ciclo infinito en caso de eliminar el break siguira imprimiendo sin terminar nunca 11 12 13 14 15 ...........................................................................................................................................................................


ahora miren este:

def main():
__i=1
__print i
__while True:
____if i==10:
______break
____i+=1
____if i % 2 == 0:
______continue
print i

if __name__ == "__main__":main()


cual sera el resultado de este codigo ?????? bueno se los dejo de tarea :) pero les voy a explicar que hace continue pues simplemente salta a la proxima iteracion :D bueno por esta vez lo voy a explicar el print que esta fuera del while me imprime 1 ya que i la inicie en 1 :) bueno entramos al ciclo infinito i==10 falso ya que 1 no es igual a 10 pues no ejecuta el break asi que seguimos i+=1
ahora desde aqui i vale 2 ;) ahora pregunta if % 2 == 0 pues aqui solo digo si el modulo de la division es igual a 0 si es verdadero pues nuestro numero es par ahora como nuestra i vale 2 es par asi que ejecuta continue y esto hace que salte a la siguiente iteracion asi que ignora el print y pasa a la siguiente iteracion, asi que esto al final me va imprimir solo numeros impares :)

FOR ...IN

secuencia = [“uno”, “dos”, “tres”]
for elemento in secuencia:
____print elemento


Los for en python se utilizan para recorrer secuencias en el caso anterior utlize una lista, el algoritmo es sencillo para cada elemento en la secuencia imprima elemento asi se lee de una forma natural bueno la primera vez elemento contiene uno imprime uno, la segunda contiene dos e imprime dos, la tercera elemento contiene tres e imprime tres asi va elemento por elemento hasta el final de la secuencia, los que han programado en C/C++ saben que el for lo han utilizado para iterar sobre las posiciones y no sobre los elementos

int mi_array[] = {1, 2, 3, 4, 5};
int i;
for(i = 0; i <>
printf(“%d\n”, mi_array[i]);
}

El enfoque e Python es mucho mas natural :)

Bueno si muy bonito pero en C/C++ para imprimir numero del 50 al 100 con for es muy facil pero como le hago con python ??? no me digas que tengo que contruir una lista del 50 al 100 para
luego recorrerla con for :( pues si pero para eso existe range() una funcion para generar secuencias de numeros veamos:

def main():
___i=range(50,101) #si estan con Python3 hacen i=list(range(50,101))
___for numero in i:
_____print numero
if __name__ == "__main__":main()


martes, 3 de marzo de 2009

FUNCIONES EN PYTHON

Bueno un funcion es una sencuencia de codigo definido por nosotros y que se ejecuta al hacer referencia a ella por un nombre que definimos, la sintaxis es la siguiente:


def nombre(lista de parametros):
sentencias

bueno aqui no es como en C o java que el contenido de una funcion lo determina { } aqui en python su creador Guido Van Rossum quiso que fuera determinado a traves de la sangria con un estandar de 4 espacios veamos para que me entiendan:

def numeros():
____print ("hola")
____print ("mundo")

def numeros():
____print ("hola")
print ("mundo")


como ven aparentemente este par de funciones hacen lo mismo pero hay una diferencia y es que en la primera al llamar la funcion numeros() imprimira hola y acontinuacion mundo ya que como se dan cuenta las dos estan perfectamente aliniadas con una sangria de 4 espacios apartir del nombre de la funcion, cuando rompoes esta sangria como es en el caso de la segunda funcion al llamar numeros() solo imprime hola, y el fin de esto es que Python quiere que escrbas codigo elegante, entendible que llegue otro programador y al ver el fuente de un compañero sea totalmente claro y no un nido de pajaros como muchos estan acostumbrados a programar.

En Python usted puede llamar las funciones como quiere excepto que utlize alguna palabra reservada algunas mencionadas en VARIABLES DE PYTHON


Nuestras funciones en python aceptan un sin nuemeros de argumentos , tambien podemos asignar valores por defecto a nuestros parametros, y a todas esta que diferencia hay entre argumentos y parametros??? bueno cuando hago llamado a una funcion:

numeros(2,7)

esos valores son los argumentos de la funcion y cuando hablo de parametros se refiere ya al valor recibido, osea es la variable que defini cuando cree la funcion para que reciba los argumentos:

def numeros(x,y): #las variable X y Y son los parametros de la funcion
____return x+y

otra cosa a tener en cuenta es saber que diferencia hay entre procedimiento y funcion ????

bueno un procedimiento es una funcion que no retorna ningun valor, y una funcion si retorna un valor, mencino esto porque en python no existen los procedimientos ya que si usted en una funcion no definen un valor a retonar Python por defecto retorna None asi que esto es de tener siempre en cuenta.

hasta ahora todo lo que hemos visto lo hemos practicado directamente desde consola. ahora en adelante ire colocando el fuente completo para que usted ejecuten su codigo y vayan acostumbrarse a realizar su codigo y sepa reutilizar las funciones de codigos hechos por usted.


este codigo lo guardan en un archivo que lo pueden llamar como quiera en este caso lo llamare suma.py

def suma(x,y):
______"""Esta funcion devuelve la suma de dos enteros"""
______return x+y


def main():

____print suma(5,6)


if __name__ == "__main__":main()


y ahora para ejecutarlo solo hacemos:

python suma.py en nuestra consola Linux o windows, tambien podemos bajar el IDE NetBeans
solo dedicado a python vamos a utilizar este ya que desde la misma pagina lo puedes bajar tanto para windows como para GNU-Linux.

Para los windowseros:

Bueno para los usuarios de windows solo lo descargan y doble-click y dan siguiente a todo :)


para los linuxeros:

Para poder ejecutar el archivo, abre una linea de comandos y en la carpeta donde está el archivo ingresa comando:

chmod +x netbeans-6.5-python-linux.sh

y despues podemos ejecutarlo:

./netbeans-6.5-python-linux.sh

Eso va a lanzar el instalador y desde ahi recordamos viejos tiempos con windows :)

bueno una vez instalado lo abrimos y esta es su ventana principal:



Damos File (archivo) y luego en New project (nuevo projecto) y se nos abra la siguiente ventana:


Negritadejamos la opcion por defecto Python project y damos next (siguiente) y definimos en nombre de projecto y en donde dice Create main file le cambiamos el nombre por el de nuestro programita
suma.py damos finish para volver a nuestra ventana principal:




Aqui damos doble click al nombre del proyecto que este caso lo llame estudio y luego en sources donde va estar nuestro primer programa:


Añadir imagen
Bueno esto no es un tutorial de Netbeans asi que busquen en google un buen tutorial para el manejo completo de esta Herramienta aqui esta el codigo fuente de suma.py en mi compu yo lo llame estudio.py veamos:

suma.py
---------

def suma(x,y):
"""Esta funcion devuelve la suma de dos enteros"""
return x+y


def main():
print suma(5,5)


if __name__ == "__main__":main()


Bueno continuamos viendo las funciones en python:

def hola(veces=1):
____print("hola" * veces)

esta funcion llamada hola contiene un parametro opcional ya que dicho parametro tiene un valor por defecto que es 1, al llamar la funcion podems hacer:

definimos la funcion:
>>>def hola(veces=1):
....____print("hola "* veces)

ahora llamamos la funcion:

>>>hola()
hola

como ven al tener un parametro opcional podemos llamar la funcion sin agumentos pero si queremos que me imprama dos veces el hola solo hay que colocar como argumento el numero de veces que queramos que nos imprima el hola veamos:

>>>hola(3)
hola hola hola


bueno python trae una forma de declar muchos argumentos por defecto a traves de una tupla
veamos:

def test(*tupla):
____for val in tupla:
____print(val)


Ahora si llamamos la funcion test() sin argumentos pues no va imprimir nada, pero si la llamamos con tres argumentos asi:

>>>test(1,2,3)
1
2
3

tambien podemos hacer que de vez de una tupla sea un diccionario, tan solo hay que hacer lo siguiente:

def test(**diccionario):
____for val in diccionario.items():
____print(val)

ahora hacemos el llamado de esta forma:

>>>test(primero = 1)
('primero', 1)

aqui primero va ser de key del diccionario y 1 va hacer de value, :) bueno en Python tambien puedo llamar una funcion dentro de otra funcion veamos:

>>>def uno():
______print("Hola mundo")

>>>def dos():
______uno()

>>>dos()
Hola mundo

Otro concepto de las funciones es cuando deciamos pasamos el argumento por valor o por referencia y algunos estaran diciendo y eso que ??
Bueno cuando nosotros pasavamos un valor por referencia lo que realmente pasamos a la funcion es la direccion de memoria donde esta almacenada en contenido de la variable y esto lo hacemos cuando queremos que todo los cambios que realize nuestra funcio a dicha variable permanescan fuera de la funcion, cuando paso un argumento por valor lo que realmente hacemos es llevarnos una copia del contenido de nuestra variable y todos los cambios que se realizen dentro de la funcion solo permanece alla dentro de la funcion ya que las variables dentro de la funcion son locales, veamos un ejemplo para captar la idea:

Bueno antes que nada hay que recordar que en Python todo es un objeto y que las listas son mutables los enteros y las tuplas inmutables ya que lo inmutable va atrabajar como paramatros por valor y lo mutable va ser paso por referencia:

>>>def f(x, y):
...____x = x + 3
...____y.append(2)
...____print x, y


>>>x=7
>>>y=[1]

llamemos la funcion y miremos que pasa:

>>>f(x,y)
10 [1, 2]

Bueno ese resultado es del print dentro de la funcion la variable x que vale 7 le sume 3 ahi se refleja en el resultado 10, y a la lista le agrague un nuevo valor que es el dos, estos valores son los de la funcion ahora hagmos un print para ver que pasa ???

>>> print (x , y)
7 [1, 2]

Como vemos fuera de la funcion el valor de la variables inmutables como la x se comporto como un paso por valor ya que dentro de la funcion tomo valor de 10 pero fuera de ella sigue valiendo 7, en cambio la lista que es mutable se comporto como un argumento por referencia ya que dentro de la funcion se le modifico añadiendo un nuevo elemento a la lista que fue el 2 y fuera de la funcion aun permanece igual :) que bonito es entender un poquito dale si quedas aun con alguna duda me la haces saber :=)

Como mencione antes en Python solo existen funciones, nada de procedimientos ya que toda funcion Python simpre retorna algo, cuando no definimos el return python retorna none ah no me crees :( pues probemos nada perdemos :) en la funcion anterior donde explique cuando para una funcion en Python en un parametro por valor y cuando es por referencia en dicha funcion no declare la el return el cual es una palabra reservada por python para retornar un valor de una funcion veamos:


esta era nuestra funcion:

>>>def f(x, y):
...____x = x + 3
...____y.append(2)
...____print x, y


ahora hagan esto:

>>>z=f( 2,[5])

ahora imprimamos la variable z haber que tiene:

>>>print(z)
None

hmm si ven veamos como utilizar el return:

>>>def sumar(x, y):
...______return x + y

>>>print sumar(3, 2)

5

asi de facil :) ahora que alguien me explique esto:

>>>def f(x, y):
...____return x * 2, y * 2

>>>a, b = f(1, 2)


Pues debe ser una trampa tuya verda??? como es que una funcion retorne dos valores imposible :( eso debe dar error, ejecutelo y te apuesto que python genera una excepcion, no ves que no ha generado nada verifica las variable a y b a=2 y b=4 , tonces como hace python??? bueno pues muy simple python sigue retornando solo un valor, lo que el hace internamente es crear una tupla con los valores de 2 y 4 y asi la funcion solo retorna una unica variable que es la tupla :)
Muy interesante, Python es una chimba :)

 
Free Domains Forwarding