Download Aprendiendo Python Desde Cero ...
APRENDIENDO PYTHON DESDE CERO (PARTE CERO) Realmente esta serie de tutoriales, que comienza en esta parte cero como introduccion, trata de que yo aprenda python y a programar con el, y por supuesto ir compartiendo lo que voy leyendo y aprendiendo con el que quiera leerlo y aprender junto a mi. Realmente no me anime a llamarlo curso, porque para hacer un curso sobre algo hay que saber, y yo no se casi nada de python, lo poco que aprendi fue a los tropezones en mi trabajo y porque tenia que programar exploits, lo cual se realiza en este idioma mayormente, por lo tanto mis conocimientos del tema estan llenos de baches, dudas que como siempre ha pasado en mi vida a medida que voy escribiendo voy aprendiendo a solucionar y a entender, por lo cual, este realmente es una especie de lo que llamamos en al argentina machete, el papelito que haciamos en la escuela para copiarnos en las pruebas, y cuando terminabamos de escribirlo nos dabamos cuenta que por solo escribirlo, habiamos aprendido algo sobre el tema y que escribir hace que se aprenda mejor y se fijen conocimientos. PORQUE PYTHON? Bueno un poco ya lo dije al trabajar en el area de seguridad y de exploit writing, el idioma natural para ello es python, aunque hay quien usa otros idiomas, como ruby, C, o algun otro, de cualquier manera sin ser yo un experto del tema reconozco algunas ventajas del idioma python para dicho uso, lo mismo que para scripting de programas, lo cual tambien veremos mas adelante (OLLYPYTHON, IDA PYTHON) nos permitiran bastante mas adelante cuando ya conozcamos el idioma, programar potentes scripts para nuestros mas usados debuggers y programas, lo cual no invalida que python pueda ser usado para programar aplicaciones de cualquier tipo, sobre todo es muy util para programar facilmente keygens, crackmes, tools y en general cualquier programa en el cual la velocidad no sea lo mas importante, y si la rapidez de programacion, legibilidad y portabilidad. Una de las ventajas de python sobre otros idiomas tambien sobre todo a nivel scripting es que es muy sencillo de entender, el codigo es breve, y cualquiera aunque no haya escrito el programa, le costara mucho menos entender lo que hace que en otros idiomas, el codigo es facilmente reusable, y como se le pueden agregar modulos, puede incrementarse con modulos propios o ajenos. Muchos programas de hoy dia que necesitan actualizacion tienen una GUI principal, hecha en algun idioma, y ejecutan scripts hechos en python, para realizar sus tareas, los cuales proveen a los usuarios una forma de manejar el programa sencilla, facil de entender, portable (ya que el codigo python funciona en linux, windows, mac, hay versiones de python para java, para celulares, para pda, notebooks, asi que realmente es un lenguaje muy poderoso, y con una curva de aprendizaje mucho mas suave que otros idiomas similares. Por lo demas la indentacion o sangria que a muchos usuarios les parece algo molesto en python, no lo es tanto, pues permite leer el codigo mas facilmente, y de cualquier forma si uno se equivoca el interprete de python se encargara de avisarle que ha cometido un error de indentacion, por lo cual, es un pequeño paso de adaptacion que conlleva a que el codigo sea mas prolijo y legible. Como por supuesto yo no se mucho de esto me baso en libros.
Estos libros que son la biblia de python : O'Reilly - Mark Lutz & David Ascher - Learning Python Mark Lutz - Programming Python Oreilly.Python_Pocket_Reference, .Mark_Lutz Estan escritos dichos libros en ingles por gente que sabe de esto, jeje, y bueno seguiremos sus pasos, por el aprendizaje de python, por otro lado, a pesar de que existen versiones de active python 2.5.x.x , nosotros usaremos aun la version 2.4 ya que es compatible con ollypython, con idapython, y realmente la diferencia con la version 2.5 es minima, asi que si uno aprende python lo puede hacer con ambos, pero todo lo que practique en estos tutoriales sera basado en este active python 2.4.x.x. Para conseguirlo vamos a la pagina http://www.activestate.com/Products/activepython/ pulsamos el boton de GET ACTIVE PYTHON
y luego de elegir la version FREE por obvias razones
Elegimos de la version 2.4.3.11 la correspondiente a windows, si realmente vamos a instalarlo en windows, en linux ya viene incluido en el sistema no hay que instalar nada, creo que en mac tambien. Una vez instalado el paquete MSI que acabamos de bajar, verificamos si esta bien instalado por lo cual antes de ejecutarlo abrimos una ventana de comandos CMD y tipeamos python Microsoft Windows XP [Versión 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\Documents and Settings\Ricardo.NARVAJA-D6A3908>python ActivePython 2.4.3 Build 12 (ActiveState Software Inc.) based on Python 2.4.3 (#69, Apr 11 2006, 15:32:42) [MSC v.1310 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> y si esta todo bien nos debe devolver la version que acabamos de instalar con el prompt de python >>>, esto debe ocurrir en cualquier ventana de comandos con cualquier path, si no ocurre, pues habra que ir a la ventana de environment variables o variables de entorno y agregar el path al ejecutable de python en mi caso C:\Python24\, aunque el instalador lo hace automaticamente.
Bueno tenemos todo listo para ejecutar el active python que acabamos de instalar, pero tambien como vimos es bueno recordar que en cualquier ventana de comandos si escribimos python, se abre un interprete del mismo, y podemos escribir alli cualquier comando o instruccion del mismo, muchas veces para hacer una pequeña cuenta no necesitamos abrir el active python, abrimos la CMD.
Como python es calculadora tambien y su lenguaje es tan intuitivo y sencillo, podremos hacer operaciones alli mismo. Pueden en una sola liea escribir intuitivamente una operación por ejemplo 56+ 45* 6 y al apretar enter lo resolvera >>> 56+45*6 326 alli esta por lo demas podemos facilmente darnos cuenta que podemos por ejemplo asignar un valor a una variable X o el nombre que queramos y en el siguiente paso operar con ella si escibimos X=5 enter y luego X+67 y enter nos dara el resultado >>> x=5 >>> x+67 72 >>> Por supuesto esto demuestra la flexibilidad del idioma que nos permite probar linea a linea cada instrucción y ejecutarla una a una, o todas a la vez si realizaramos un script, pero bueno, es importante saber que la linea de comandos, o el terminal de linux, nos quedaran muy poderosos, pues podemos ejecutar directamente instrucciones de python, que siempre son sencillas, y poderosas.
Tambien si ejecutamos el programa veremos la interface del mismo
Vemos la ventana interactiva donde podemos tambien practicar tipear lo que antes hemos hecho en la ventana de comandos de windows o en el terminal de linux.
Vemos que es muy intuitivo escribir alli, al igual que en la CMD, podemos asignar variables y al darle enter, tomaran su valor, podemos realizar operaciones con las mismas, y mientras no cerremos la ventana interactiva, quedaran en memoria los ultimos valores que se han asignado o ejecutado. Si en el caso anterior agrego Y= x + 5666 a diferencia de escribir solo x + 5666 que me muestra el resultado directo en la consola, en este caso como es una asignacion, el resultado quedara almacenado en Y el cual podemos ver tipeando Y y enter.
Por supuesto esto lo veremos mas detalladamente por ahora solo estamos mostrando la interface grafica con algun ejemplito simple. Ademas de la interface interactiva, al apretar el boton NEW
Podemos abrir un editor de script de python completos.
Pythonwin posee las dos posibilidades, la ventana interactiva y el editor de scripts, realmente la ventana interactiva solo se usa para probar comandos sueltos o pocos comandos, a ver que efecto tienen y mientras uno esta aprendiendo es muy util ver que ocurre al ejecutar algun comando, o un par de comandos, mientras que el editor de scripts, se utiliza para programar.
Cuando uno ya conoce mas las instrucciones, puede armar un script o programa completo en el editor, es lo que se utiliza normalmente, lo que si en este ultimo, no podremos ejecutar instrucciones sueltas sino que deberemos correr el script completo, por eso quizas en las primeras instancias del aprendizaje, mientras nos familiarizamos con los comandos usamos, la ventana interactiva por su efecto inmediato.
Aquí la cosas no se ejecutan ya linea a linea como en la ventana interactiva, si no que en forma bastante similar al editor OLLYSCRIPT, para los que lo conozcan, aquí podemos escribir un script entero de python, guardarlo y correrlo, ya veremos su uso mas detallado, lo importante es que se vea la diferencia de que aquí no se ejecuta instrucción a instrucción como en la ventana interactiva, sino se escriben scripts completos, como ejemplo breve.
Bueno es un miniscript que realmente se podia ejecutar directamente en la ventana interactiva pero nos servira para probar la ventana de scripts.
Guardo mi script con un nombre
Lo cierro y vuelvo a la ventana interactiva, puedo borrar todo lo que habia de antes y si corro el script ...
y puedo ver el resultado impreso en la consola,veo lo que mencione anteriormente la salida de un script, se hace por medio del comando print . Si no le colocara el print delante, el script no mostraria nada en la consola de salida, aunque pongamos el nombre de la variable solo: hello='Hello World!' hello Es de mencionar que el hecho de que la ventana interactiva, nos muestre el valor de una variable solo tipeando el nombre de la misma y ENTER, es una caracteristica especial de la misma, para ayudarnos a saber el valor rapidamente de alguna variable, pero cuando un script lo escribimos completo en el editor de scripts, y queremos que al ejecutarlo muestre el valor de algo, deberemos usar print, lo cual mostrara el valor en la consola o salida, hago esta aclaracion porque muchos lectores al ver que en la ventana interactiva puedo ver el contenido solo tipeando la variable y ENTER, se confunden y piensan que en un script tambien es similar, y no es asi, alli habra que usar el comando print para mostrar algo en la salida. El hello solo como dijimos, no mostrara salida alguna, sin el print delante, siempre debemos recordar que tipear una variable sola, y luego apretar ENTER solo sirve para ayudarnos a saber su valor, en la ventana interactiva y no funciona en el editor de scripts.
Fue realmente facil mi primer script jeje, puedo reabirlo con OPEN y agregarle cosas, modificarlo
si le agrego
y lo guardo y corro
Veo que podia escribir una sola string YO SOY PEPE o sumar strings, jeje, asi de una sumo strings , puse YO mas SOY mas PEPE y asi como venia sumo las tres a una sola string y la asigno a la variable pepe, la cual imprimi en la siguiente linea. Vemos en estos ejemplos que sin tener que declarar tipos de variables, solamente asignandoles valores, podemos trabajar con ellas, ya veremos mas adelante ejemplos y como podemos profundizar en el tema. Tambien vemos que como lenguaje para hacking es python muy poderoso, pues desde una simple linea de comandos podremos tener acceso a muchisimas posibilidades, que mas adelante veremos aunque realmente esto no sera un curso de hacking ni de exploit, se podra entrever, las posibilidades del idioma para estos menesteres. Bueno creo que ingresamos amenamente en el tema y de a poco, iremos paso a paso avanzando espero que a alguien le interese, aunque como dije, yo aprendo y el que me siga pues si quiere que me siga y si no, pues hay miles de tutoriales de otros temas, seguramente mejores y mas interesantes que este. Un abrazo a todos los crackslatinos y hasta la proxima parte 1 donde empezaremos a estudiar el idioma en profundidad.
INTRODUCCION A PYTHON (parte 1) EJECUTANDO Como vimos en la parte 0, ejecutar el mas simple script o programa en todo lenguaje, es imprimir como salida la cadena de texto HOLA MUNDO o HELLO WORLD, en la parte anterior hicimos un script que ejecutaba dicha accion, vimos que si lo corremos desde la interfase de python apretando el boton RUN
En la consola interactiva nos mostrara la salida del programa que esta guardado en el script, o sea la cadena HOLA MUNDO o HELLO WORLD. Recordemos siempre que si hacemos cambios en el script, antes de correrlo debemos guardarlos y alli se ve.
Ahora si el mismo script lo ejecutamos haciendo doble click en el mismo, estando cerrado el interprete de python, nos mostrara la salida en una ventana de comandos, si estamos en windows, dicha ventana no dara tiempo a ver nada y se cerarra como ocurre con todos los programas que tienen salida por consola, hasta que hagamos programas con otro tipo de salida es bueno saber este truquito.
Ya explicaremos bien comando por comando, pero bueno input es un comando que queda esperando que el usuario aprete una tecla y si apreta ENTER terminara y se cerrara el programa y quedara visible hasta que apretemos dicha tecla.
Y cuando apretamos ENTER podemos salir, este truquito mientras hagamos scripts que tenga salida por ventana de comandos siempre lo agregaremos al final, para que no se cierre solo, y podamos ver los scripts solo ejecutandolos con doble click. Por supuesto que este simple script es una de las tantas formas que podemos imprimir HOLA MUNDO, ya vimos que si abrimos una consola de windows y escribimos python.
Microsoft Windows XP [Versión 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\Documents and Settings\Ricardo.NARVAJA-D6A3908>python ActivePython 2.4.3 Build 12 (ActiveState Software Inc.) based on Python 2.4.3 (#69, Apr 11 2006, 15:32:42) [MSC v.1310 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> y alli tipeamos print 'Hola Mundo'
Tendremos la salida similar, lo mismo que si abrimos la ventana interactiva de python
y escribimos la instrucción y apretamos ENTER tambien obtenemos la misma salida o sea que tenemos diversas formas de lograr el mismo objetivo. 1) ejecutando la instrucción en la consola de windows o terminal en linux o mac luego de tipear python. 2) tipeando la instrucción en la ventana interactiva de python 3) haciendo un script y corriendolo con doble click o con el RUN desde python. Vemos que la opcion 1 y la 2 son para ejecutar unas cuantas instrucciones y para experimentar que resultado se produce al ejecutarlas, pero para programas largos o scripts complejos, no podemos tipear cada vez lo mismo, por lo cual en esos casos se aconseja la opcion 3 que es de guardar el codigo en un script, el cual no necesita ser retipeado y puede ejecutarse, copiarse, editarse, mejorarse, la cantidad de veces que uno quiera. De cualquier manera según lo que uno necesite hacer, python permite en forma flexible, elegir entre las diferentes opciones, lo cual es muy util, por ejemplo, en el aprendizaje de las instrucciones de python es muy util la ventana interactiva para poder ver y practicar con pocos comandos en forma
inmediata,y ver que resultados producen. Bueno abramos el primer script que realizamos ahora le agregaremos -----------------------------------------------------------------------------------------------------------------------hello = "Hello, World!" print hello print 2 * 100 input( "Presione ENTER para terminar" ) Guardamos los cambios y lo ejecutamos con doble click
Bueno ahi tenemos el primer print imprime una string que esta entre comillas , HELLO WORLD, el segundo print imprime el resultado de 2 por 100 y luego tenemos la salida de la ventana apretando ENTER. Por ahora no nos preocupamos mucho de la sintaxis, mas adelante se explicara comando por comando, por ahora estamos ejecutando scripts en forma bastante intuitiva. Realmente un script no necesitamos crearlo en python, en cualquier editor de texto, como el notepad, si escribimos el mismo texto y lo guardamos con extension .py o con extension .txt y luego se la cambiamos, sera exactamente lo mismo. Si le agregamos un * mas sera como elevar 2 a la 100 hello = "Hello, World!" print hello print 2 ** 100 input( "Presione ENTER para terminar" ) al ejecutarlo se vera el resultado de la operación
Otro truco parecido para evitar que se cierren las consolas de comandos es usando el comando raw_input, que es bastante similar a lo que pusimos anteriormente pero menos aparatoso. Si al final del script agrego raw_input () veamos que ocurre, alli en mi primer script le cambie por raw_input() el comando para evitar que se cierre la ventana, guardemos los cambios y ejecutemoslo.
Si lo ejecuto
es menos invasivo y al apretar enter tambien sale, recomiendan este y bueno cada uno aplica el que mas le guste, mas adelante detallaremos la diferencia entre ambos. Realmente cuando uno ejecuta un script de python, el interprete lo compila en una fila de extension .pyc que es el codigo de python compilado, y para mayor velocidad mientras no se cambie el codigo cada vez que se ejecuta un script, se ejecuta dicho pyc para mayor rapidez.
Igual esto no necesita saberse pues siempre uno ejecuta el .py, este o no este el .pyc es un tema de funcionamiento interno, pero bueno a modo ilustrativo es bueno saberlo. Esos pyc son ejecutados por la llamada PYTHON VIRTUAL MACHINE que es realmente un loop que lee byte a byte y según cual es ejecuta la siguiente instrucción.
Asimismo los usuarios de C veran que aquí no existe make ni es necesario compilar el codigo fuente, el mismo se compila y ejecuta automaticamente, por lo cual es mas sencillo, aunque un poco mas lento al ejecutar, pero con los avances de hardware de hoy dia, realmente no se nota gran diferencia. Tambien existen formas de crear exes, que pueden correr en maquinas que no tienen python instalados, por ejemplo en WINDOWS, existe un programa llamado Py2exe que genera exes que corren en cualquier windows, tengan o no tengan python instalado, por supuesto el hecho de llevar las dll de python dentro del mismo exe, supone un incremento de tamaño, piensen si en cada
ejecutable .NET pusieramos las dll que necesita dentro del mismo exe, para evitar que el usuario instale el runtime necesario, habria exes de NET de 60 megas jeje, que ejecutan 2 o 3 instrucciones, por lo tanto lo logico es que si alguien corre programas escritos en python, tenga instalado python en su maquina, mejor que meter todo en exes, agrandando las cosas. Bueno volvamos a nuestra ventana interactiva para tipear algunos comandos en forma intuitiva que explicaremos mas en profundidad en siguientes partes. Esto pueden tipearlo tanto en la ventana interactiva de pyton como en una consola de comandos o terminal luego de tipear python es similar. Por mejor claridad lo tipeare en la ventana interactiva de python.
Practicamos ejecutando prints, primero el clasico 'Hola Mundo' y luego una operación que se resuelve y me muestra el resultado. Si asigno algun valor a una variable, puedo ver el valor asignado a la misma solo tipeando el nombre de ella.
Alli asigne a pepe el valor 87655 que quedo almacenado, no lo muestra pues fue una asignacion solamente, si tipeo el nombre de la variable me mostrara el valor asignado.
Vemos que al tipear pepe, la ventana interactiva nos mostro el valor asignado, de cualquier manera esto no debe confundirnos, es solo una opcion para visualizar en la ventana interactiva de python, si queremos que un programa muestre en la salida un resultado deberemos usar print, si no no se mostrara. Tambien vimos que python puede concatenar strings facilmente si tipeamos print 'PEPE' + ' ES'+ ' UN' + ' GENIO'
##concatenar
lo que se halla luego de ## es un comentario que no se ejecuta y sirve para saber en un programa que se esta haciendo, si tipeo esto y lo ejecuto.
Vemos que se concatenan las strings y que el comentario por supuesto no se ejecuta, es muy practico dentro de un script complejo, poner comentarios para ayudar a la comprension del codigo. Ahora escribimos un script con estas lineas
Lo guardo con el nombre spam.py y al darle doble click
tambien si en una consola de comandos, me ubico en el path donde esta el archivo
C:\Documents and Settings\Ricardo.NARVAJA-D6A3908>cd Escritorio C:\Documents and Settings\Ricardo.NARVAJA-D6A3908\Escritorio> y luego escribo python y el nombre del archivo a ejecutar lo correra.
Es bueno poder ejecutar un script desde una linea de comandos, eso nos permite por ejemplo desviar la salida del script a un archvo de texto facilmente. Si tipeo en la linea de comandos python spam.py > pepe.txt guardara la salida del archivo a un archivo de texto llamado pepe.
Si abro el pepe.txt
Vemos que direcciono la salida al archivo de texto y no se mostro en la ventana de comandos jeje. Asi vamos practicando y divirtiendonos con ejemplos simples, que son faciles de entender antes de hacer un resumen mas detallado de las instrucciones en la parte siguiente.
Escribo import sys que me importara el modulo sys, y en la linea siguiente tipeo print sys.
Vemos que al apretar sys y el punto sale una ventana para elegir las distintas opciones que tiene el modulo sys en este caso elegimos platform con TAB lo ingresamos y luego ENTER.
Alli vemos como importamos una funcion que pertenece al modulo sys, que nos devuelve la plataforma donde estamos corriendo nuestro script, la posibilidad de importar modulos hechos y de hacer mas, nosotros mismos, o bajar de internet nos da muchas posibilidades. Tambien hay scripts en python que no muestran la consola de windows, y tienen la extension .pyw, en ese caso son scripts que tienen su propia salida y la consola de windows molesta, de forma que al guardarlo como .pyw, no se mostrara.
IMPORTANDO MODULOS La definicion de modulo en python es simple, cualquier archivo que esta hecho en python, y tiene extension .py es un MODULO. Cualquier otro archivo de python puede acceder a las posibilidades de un modulo, importandolo y usandolo. Generalmente largos programas escritos en python tienen la forma de multiples modulos, los cuales importan herramientas de otros modulos. Ya hemos visto como ejemplo la importacion del modulo sys, y hay muchisimos mas que podriamos poner como ejemplos. Si hacemos un script con ello import sys print sys.platform print 2 ** 100 y lo guardamos con el nombre Script4.py
si lo ejecuto desde la linea de comandos
alli esta la salida que nos muestra que estamos en win32. Si alli entro en python y tipeo import Script4
la primera vez lo ejecutara, sucesivas veces tendre que usar el comando reload reload (Script4)
lo ejecutara nuevamente al modulo que tiene cargado. Importar realment sirve para ejecutar modulos ajenos en el nuestro asi como usar las herramientas que este tenga programadas internamente. Vayamos a la ventana interactiva
alli importo el modulo
vemos que al escribir print Script4. Aparece las posibles tools y variables que tiene incluidas el modulo Script4, como aun no define propias, usa la que importo de sys, pero como eso no lo asigna a ninguna variable no nos mostrara nada
Esto te dice que el modulo esta alli pero no muestra nada porque no se asigna a ninguna variable, si editamos Script4 y cambiamos para que asigne a la variable llamada a el valor de la string de que sistema estamos corriendo y guardamos los cambios y ahora si importamos el modulo.
Como cerre y abri la ventana interactiva no lo encuentra ya veremos que hay una carpeta especial conde se colocan los modulos de python para que los busque alli, pero si lo abro y lo guardo ya lo localiza nuevamente.
Alli importo el modulo ahora no se ve en que sistema operativo estamos trabajando porque eso esta en la variable a dentro de Script4, si hacemos...
ahora no solo nos aparece sys como opcion si no tambien la variable a que creamos, la elegimos con TAB.
Y al apretar enter ahi si nos muestra que estamos en WIN32, vimos como usar una funcion del modulo sys, como asignarlo a una variable a dentro del modulo Script4, y luego como podemos llamar a la variable Script4.a que nos devuelve el valor solicitado, usando el trabajo entre modulos. Bueno por hoy hay bastante para practicar esto es solo un pequeño pantallazo para que se vayan familiarizando con el idioma, ya iremos detallando mas adelante. Un abrazo hasta la parte 2 Ricardo Narvaja
APRENDIENDO PYTHON DESDE CERO Parte 2 ( practicando importar modulos + keys utiles) Seguimos despacio practicando importar modulos, un poco mas para que quede claro como funciona esto, que es tan importante en python. a = 'pepe' # definimos 3 atributos. b = 'pirulo' c = 'campeon' print a, b, c Lo guardamos en un script nuevo llamado Practica1.py
Como vemos este script asigna tres variables a,b y c que pueden ser invocadas al importar este modulo desde el mundo exterior, ademas si lo ejecutamos imprime los contenidos de esas variables. En la ventana interactiva lo ejecutamos desde el boton RUN y lo buscamos donde lo hayamos guardado con el buscadorcito que trae al apretarlo. Si hacemos import...
Las proximas veces para correrlo hay que hacer reload desde aquí o apretar nuevamente el boton RUN
Ahora que esta importado en la ventana interactiva podemos invocar cualquiera de las variables de ese modulo y usarlas, ya se desde otro script o desde la ventana misma.
Vemos que apenas escribo print Practica1 y el punto me salen las opciones de variables que tiene ese modulo asignadas para usar en el mundo exterior, que somo sabemos son a, b y c, si queremos imprimr b solamente elegimos b y TAB.
Al apretar ENTER podemos imprimir la variable b, que esta asignda dentro del modulo Practica1, veremos que tambien esto se puede hacer desde un segundo script escribamos un nuevo script llamado Practica2.
Vemos que mientras escribo el nuevo script tambien me aparece la ayuda para elegir variables del modulo que importe.
Ahora vuelvo a la ventana interactiva y voy al boton RUN
y al correr Practica2, importa el modulo Practica1 y imprime del mismo la variable b.
Es muy importante entender el concepto de importacion por eso puse algunas practicas mas para que estemos seguros antes de avanzar mas.
Debemos recordar que como no guardamos el modulo en la carpeta de modulos, si cerramos y abrimos la ventana interactiva solo aceptara importar modulos que esten en la misma carpeta, que el script que estoy ejecutando, para evitar eso existe la carpeta de modulos como explicaremos mas adelante. Existe una forma de importacion selectiva de un modulo, en vez de como veniamos haciendo hasta ahora,que importabamos el modulo completo, si sabemos por ejemplo que solo usaremos la variable b y el resto no, en ese caso tipeamos en la ventana interactiva.
En este caso solo importo la variable b, y luego imprime las 3, eso es porque tiene incluido el print de las 3 variables dentro del modulo, pero si quiero usar una variable, veremos que solo puedo usar la b, pues es la unica importada. En esta forma de importar parcial, no necesito invocar el nombre del modulo original, es mas si lo hago me dara error solo haciendo print b vere el resultado.
Vemos que solo print b no me da error, tanto print a que no esta importada como print Practica1.b no funcionan esta ultima porque no importamos el modulo completo, solo una variable suelta, por eso hay que llamarla directamente, como hemos hecho con print b. A veces con modulos muy largos y con muchas variables que uno sabe que solo va a usar una o dos y no el modulo completo es mejor hacer una importacion parcial, para hacer mas rapido y que sea mas breve el codigo.
Alli copiamos nuestro mudulo de ejemplo a la carpeta lib que esta dentro del directorio de instalacion.
Tambien copiamos el modulo Practica1 alli ya que Practica2, importa Practica1 dentro de el.
Ahora veo que si abro una ventana interactiva nueva, o una ventana de comandos nueva o lo que sea, mis modulos son aceptados como modulos de python siempre, jeje.
Esa es la forma de agregar un modulo a la instalacion de python, copiandolo al directorio lib se podra usar, como los otros que ya vienen incluidos y se encuentran alli, sin problemas de path ni nada, de cualquier manera si es un script que uno distribuye y envia, puede enviar los modulos que se van a usar para que los coloquen o bien en su propia carpeta modulos, como asi tambien en la misma carpeta que el script que se ejecutara, en ambos casos funcionara igual.
Si voy a Browse Python Path puedo ver los modulos actuales y estan los mios jeje
al hacer doble click se abre tanto ese como cualquiera de los modulos incorporados
el comando dir con el nombre del modulo entre parentesis
nos muestra ademas de alguna info interna de python, el nombre de los atributos internos del modulo.
Como el modulo Practica2 no tiene ningun atributo propio, entonces sale solo la info interna de python. Para terminar este parte les dire algunos truquitos sobre el IDE de python, algunos se habran dado cuenta quizas por ustedes mismos otros quizas no sabian, aquí van: 1)Al menos en los teclados españoles, el asterisco, el signo mas deben usarse de las teclas que estan encima de la tecla ENTER, las del teclado numerico no responden. 2)CTRL mas la flecha hacia arriba sale el ultimo texto tipeado anteriormente alli guarda como una memoria mientras no se cierre la ventana, podemos recorrer con esto los comandos viejos que ya tipeamos y volverlos a ingresar sin tener que retipearlos. 3)Si tenemos una ventana interactiva abierta y algun script abierto con ALT mas I podemos switchear entre ambos. 4)CTRL + Z deshace lo ultimo que hemos hecho, si repetimos ira deshaciendo hacia atrás todo lo
que hicimos ultimamente. 5)CTRL + Y lo opuesto al anterior lo recuperara. 6)CTRL + U pasa a minusculas 7)CTRL mas SHIFT mas U pasa a mayusculas Estos creo son los mas importantes y los que recuerdo ahora a medida que vayamos usando, y me acuerde o descubra alguno mas los agregamos. Hasta la parte 3 Ricardo Narvaja
APRENDIENDO PYTHON DESDE CERO PARTE 3 ( numeros y operaciones) En python los datos toman la forma de objetos, los tipos de objetos que trae Pyhton pre-definidos son NUMEROS, STRINGS, LISTAS, DICCIONARIOS, TUPLAS y FILAS. Alli en la tabla vemos los tipos de objetos predefinidos y ejemplos de los mismos en esta parte empezaremos a ver el primer tipo que son los NUMEROS y las operaciones entre ellos.
NUMEROS Es el mas comun de los objetos, y el que uno esta mas familiarizado, uno puede llevar la cuenta bancaria, medir distancias, alturas, y demas cuentas con numeros de diferentes tipos. Python soporta los tipos numericos usuales como ENTEROS y PUNTO FLOTANTE, LONGS, HEXA, OCTALES, COMPLEJOS que veremos a continuacion. Python soporta: ENTEROS NORMALES (1234, -23,0) ENTEROS LARGOS o LONG INTEGERS (9999999999999999999L) PUNTO FLOTANTE (1.23, 3.14e-10, 4E210, 4.0e+210) OCTAL Y HEXA (0177, 0x9ff, 0XFF) NUMEROS COMPLEJOS (3+4j, 3.0+4.0j, 3J) EXTENSIONES PROVISTAS PARA REALIZAR OPERACIONES Python provee un set de herramientas para procesar los numeros y realizar operaciones.
Aquí podemos ver una tabla con los mas usados
Como lo mejor es ver las operaciones en accion abramos la ventana interactiva de python para tipear algunos ejemplos. OPERACIONES BASICAS Y VARIABLES A pesar de lo flexible que es python hay algunas reglas que debemos respetar, si usamos una variable en una operación, esta debe tener asignado anteriormente algun valor, si no no podra realizar la operación Por ejemplo si yo tipeo a= c+ 5 y apreto ENTER
Me dice el error, que la variable c no esta definida, y logicamente no le he asignado ningun valor antes de llegar a la operación por lo cual no puede realizarla.
Ahora asigno por ejemplo c = 67 y luego si podra realizar la operación con dicha variable. Realmente la variable c no existia en el programa, hasta que no le asigné un valor, en el mismo momento que le asigno un determinado valor comienza a existir y puede ser usada en operaciones. Si vuelvo a asignar un nuevo valor a c se perdera el anterior
Asignemos estos valores para los proximos ejemplo: a=3 b=4
ahora tipeemos >>> a+1, a-1 reemplazara a por 3 y hara las operaciones 3 +1 y 3-1
y nos devuelve (4, 2) En el siguiente ejemplo, usando las mismas asignaciones anteriores de a y b, multiplica b por 3 y b lo divide por 2.
En el siguiente ejemplo, suma 2.0 mas 4.0 y tambien 2.0 lo eleva a la 4
Vemos que las posibilidades de operaciones numericas son muchas y en el caso de estos ejemplos, Python interpreta los resultado como TUPLAS, porque se los entregamos separados por comas, lo cual es la separacion para las partes que componen una TUPLA la cual veremos en detalle mas adelante. Realmente no necesitamos declarar variables como en C, decirle al programa si dicha variable va a ser integer, long, string o algun tipo en especial, solo debemos asignarle un valor y con eso la variable esta en funcionamiento, inclusive una variable que tenia enteros por ejemplo, si la asignamos nuevamente, puede cambiar a string o otro tipo.
Alli le asignamos a c el valor nueve y luego le asignamos la string pepe, y si vemos el valor que quedo asignado es el ultimo, la string pepe. Hagamos otro ejemplo con las mismas asignaciones anteriores, aquí divide 4 / 2 y luego le suma 3, dano como resultado 5.
En el siguiente ejemplo, se ejecuta primero 2.0 + 3 porque esta entre parentesis y luego realiza la division de b sobre lo anterior, y coloca el resultado como punto flotante con decimales, ya que siempre que algun miembro de una operación se expresa aomo punto flotante, el resultado lo obtendremos en punto flotante tambien.
El comando print que trabaja redondeando lo que muestra, el resultado completo es:
Realmente lo que se redondea es el resultado mostrado, porque la variable continua teniendo el valor completo, asi el programa puede seguir operando con valor preciso.
Si vemos el resultado de esta operacion
al dividir por 3.0 la operación pasa a tener resultado de punto flotante y el resultado se asigna a la variable g. g vale 0.33333333333333331
al imprimirse print g redondea a 0.333333333333 igual se mantiene el valor completo en la variable g para el uso del programa. Lo mismo que expresamos sobre el punto flotante se cumple en la division. Si hicieramos 1 dividido 3, como ambos son enteros la operación se realiza como una division de enteros cuyo resultado muestra solo la parte entera del mismo, ahora como ya vimos si uno de los operadores esta expresado en forma de punto flotante, su resultado será mostrado como tal en punto flotante con decimales. 1er caso (division de enteros = resultado entero)
2do caso (division con uno o mas miembros siendo punto flotante = resultado punto flotante)
Existe ademas de la division comun representada por el simbolo / que vimos hasta ahora, una division especial que se representa con dos // y que a diferencia de la anterior, sea cual sea el tipo de numero utilizado en ambos miembros, sea entero o de punto flotante, siempre pone a cero los decimales.
En el caso de dos enteros no cambia nada el resultado sera la parte entera del mismo.
Ahora si uno de los numeros es de punto flotante, vemos que la division comun, muestra el resultado correcto con decimales, la otra division // pone a cero los decimales.
OPERACIONES CON BITS
SHIFT Podemos hacer shift de los bits de una variable, si asignamos a x=1 que en binario es 0001 y luego hago shift 2 lugares hacia la izquierda x > , tambien podemos hacer operaciones xor, and, not en la forma usual entre binarios. OR En este ejemplo realizamos la operación OR con el simbolo | que representan la operación.
x= 1 (0001 binario) y=4 (0100 binario) OR ( | ) operara bit a bit según su tabla de verdad y nos dara como resultado 5.
Donde A y B son los bits de una misma columna y X sera el resultado 0001 0100 0101 Vemos que dara 0101 binario o sea 5 decimal AND Tambien existe la operación AND entre bits
En este caso si hacemos AND (&) entre 1 y 4, el resultado sera cero 0001 0100 0000 escribimos en python x&y
XOR Tambien existe la funcion OR EXCLUSIVA o XOR ( ^ )
En python se representa con el simbolo ^
En la parte siguiente completaremos el tema numeros y operaciones para no ir demasiado rapido y fijar bien los conocimientos. Hasta la parte 4 Ricardo Narvaja
APRENDIENDO PYTHON DESDE CERO PARTE 4 ( mas numeros y operaciones) LONG INTEGERS (enteros largos) Los enteros largos son un poco exoticos, pero son numeros que pueden ser tan largos como memoria tengamos mientras que luego de escribirlos le coloquemos la letra L. Veamos estos ejemplos en la ventana interactiva de python.
Al hacer una operación siendo uno de los miembros un entero LONG, lo cual esta expresado por la L al final, el resultado sera un long tambien.
Vemos que el resultado mientras que tengamos memoria, tratara de escribirse tan largo como se pueda. En el ejemplo siguiente vemos que en las nuevas versiones de python (mayor que 2.2), si escribimos una operación que deberia dar overflow porque ninguno de los dos operadores es long, python convierte el resultado a long y lo muestra sin hacer overflow.
NUMEROS COMPLEJOS Python posee soporte para numeros complejos, para el que los utiliza, los mencionaremos por encima mostrando algunos ejemplos, sabemos que son dos partes una real ( la expresada con jota minuscula) y otra imaginaria (expresada con jota mayuscula)
Alli hay tres ejemplos, realmente los numeros complejos se usan mas que nada en usos cientificos,
y raramente en uso comun, pero dejamos constancia de como se usan en python brevemente. NOTACION HEXADECIMAL Y OCTAL Ademas de la notacion decimal (base 10) que se utiliza normalmente, python puede expresar numeros en notacion hexadecimal (base 16) y octal(base 8 ) Los octales se representan comenzando en 0 por ejemplo 01, 010, 0100 corresponden a los decimales (1, 8 y 64) y los hexadecimales se les agrega delante el prefijo 0x 0x01, 0x10, 0xFF que corresponden a los decimales (1, 16, 255) Creo que es parte de un manual de matematica y no de programacion en python, enseñar la forma de convertir de un tipo al otro y excede el objetivo este curso, pero veremos las funciones que tiene python predefinidas para hacerlo. La funciones oct y hex permiten convertir decimales a octal y hexa respectivamente
alli vemos que oct (64) transforma el decimal 64 en octal, y lo mismo hexa (64) transforma el decimal 64 en hexa. La funcion opuesta para convertir a decimal de octal y de decimal a hexa es la funcion int
Esta funcion de dos argumentos, el primero es el numero a convertir, y el segundo argumento, si es 8, convierte a octal, y si es 16 convierte a hexa, usando una sola funcion para ambos casos. Según lo visto debemos tener cuidado siempre de no poner ceros delante de numeros decimales, por ejemplo si quiero escribir el decimal 10, no debo ponerle un cero delante pues 010, sera interpretado como el octal 8, asi que siempre que escriba un decimal debo cuidarme de nunca ponerle ceros delante en ningun caso. OTRAS HERRAMIENTAS NUMERICAS A diferencia de las funciones predefinidas como vimos en el caso de int, oct,hex, que se pueden utilizar sin importarlas de un modulo externo, hay funciones que para utilizarlas hay que importar algun modulo, en el cual estan definidas.
Por ejemplo si importamos el modulo math
Cuando escribimos el punto nos aparecen diferentes funciones que podemos utilizar de este modulo, por ejemplo
math.pi
Nos devuelve la constante pi, lo mismo trae incluidas mas constantes y funciones utiles que se pueden consultar en la documentacion del modulo.
Como habiamos visto el comando dir nos mostraba todas las posibles funciones y variables del modulo math, que podemos utilizar al importarlo. Alli vemos por ejemplo la funcion sin (seno)
Bueno es muy facil darse cuenta por el nombre que hace cada funcion, aunque hay una forma mas
detallada de obtener informacion de las funciones incluidas en un modulo. En este caso tipeemos help (math) el resultado es el siguiente >>> help (math) Help on built-in module math: NAME math FILE (built-in) DESCRIPTION This module is always available. It provides access to the mathematical functions defined by the C standard. FUNCTIONS acos(...) acos(x) Return the arc cosine (measured in radians) of x. asin(...) asin(x) Return the arc sine (measured in radians) of x. atan(...) atan(x) Return the arc tangent (measured in radians) of x. atan2(...) atan2(y, x) Return the arc tangent (measured in radians) of y/x. Unlike atan(y/x), the signs of both x and y are considered. ceil(...) ceil(x) Return the ceiling of x as a float. This is the smallest integral value >= x. cos(...) cos(x)
Return the cosine of x (measured in radians). cosh(...) cosh(x) Return the hyperbolic cosine of x. degrees(...) degrees(x) -> converts angle x from radians to degrees exp(...) exp(x) Return e raised to the power of x. fabs(...) fabs(x) Return the absolute value of the float x. floor(...) floor(x) Return the floor of x as a float. This is the largest integral value the logarithm of x to the given base. If the base not specified, returns the natural logarithm (base e) of x. log10(...) log10(x) -> the base 10 logarithm of x.
modf(...) modf(x) Return the fractional and integer parts of x. Both results carry the sign of x. The integer part is returned as a real. pow(...) pow(x,y) Return x**y (x to the power of y). radians(...) radians(x) -> converts angle x from degrees to radians sin(...) sin(x) Return the sine of x (measured in radians). sinh(...) sinh(x) Return the hyperbolic sine of x. sqrt(...) sqrt(x) Return the square root of x. tan(...) tan(x) Return the tangent of x (measured in radians). tanh(...) tanh(x) Return the hyperbolic tangent of x. DATA e = 2.7182818284590451 pi = 3.1415926535897931 ----------------------------------------------------------------------------------------------------------------------Como vemos nos da una descripcion mas detallada de las funciones y posibilidades del modulo, si quisieramos solo info de una de ellas, la tipeamos dentro del parentesis.
Como vimos nos devuelve la info solo de asin y no de todo el modulo. Si escribimos
Nos sale la ayuda general Welcome to Python 2.4! This is the online help utility. If this is your first time using Python, you should definitely check out the tutorial on the Internet at http://www.python.org/doc/tut/. Enter the name of any module, keyword, or topic to get help on writing Python programs and using Python modules. To quit this help utility and return to the interpreter, just type "quit". To get a list of available modules, keywords, or topics, type "modules", "keywords", or "topics". Each module also comes with a one-line summary of what it does; to list the modules whose summaries contain a given word such as "spam", type "modules spam". You are now leaving help and returning to the Python interpreter. If you want to ask for help on a particular object directly from the interpreter, you can type "help(object)". Executing "help('string')" has the same effect as typing a particular string at the help> prompt. >>> help () Welcome to Python 2.4! This is the online help utility. If this is your first time using Python, you should definitely check out the tutorial on the Internet at http://www.python.org/doc/tut/. Enter the name of any module, keyword, or topic to get help on writing Python programs and using Python modules. To quit this help utility and return to the interpreter, just type "quit". To get a list of available modules, keywords, or topics, type "modules", "keywords", or "topics". Each module also comes with a one-line summary of what it does; to list the modules whose summaries contain a given word
such as "spam", type "modules spam". You are now leaving help and returning to the Python interpreter. If you want to ask for help on a particular object directly from the interpreter, you can type "help(object)". Executing "help('string')" has the same effect as typing a particular string at the help> prompt. Y una ventana para tipear sobre que queremos ayuda
si pongo
Me muestra el mismo resultado que si tipeo help (math). Bueno despacio terminamos de ver tipos de numeros y operaciones incluidas e importadas de otros modulos, en la parte siguiente comenzaremos a ver el siguiente tipo de objeto que son las strings. Hasta la parte 5 Ricardo Narvaja
APRENDIENDO PYTHON DESDE CERO PARTE 5 ( Strings) Sigo lentamente aprendiendo python, leyendo los libros que mencione en las partes anteriores, que me sirven de inspiracion y guia para seguir, con poco o nulo feedback de la lista, para saber si vamos bien o mal, pero bueno ya estamos acostumbrados a eso, asi que seguimos con la parte 5, tratando de aprender algo y escribirlo, por si a alguien le sirve. En esta parte empezaremos con el manejo del siguiente tipo de objeto predefinido que son las Strings, y por supuesto para ellos abrimos el editor interactivo de python asi podemos tipear los ejemplos que veremos. Ejemplos faciles: STRING VACIA: las strings se escriben entre comillas dobles o simples, si no ponemos nada dentro de las comillas tenemos una string vacia.
DOBLE COMILLAS:Si necesitamos usar una comilla simple en una string, la cerramos entre doble comillas, las cuales seran consideradas las comillas que encierran la string, y la comilla simple interna, un carácter mas dentro de la misma.
TRIPLE COMILLAS: Sirve para que cuando dicha string se utilice en un print, se respete el formato como se escribio, al mostrarlo, por ejemplo.
Si hago print a
Veo que el formato que arme dentro del entrecomillado triple se mantiene.
Si lo hago en un script veo mas clara la diferencia
Al correrlo la ventana interactiva me muestra la salida
Si le cambio las comillas por simples
Vemos que ni siquiera lo acepta pues toma a la variable a como una string que le faltan las comillas de cerrar la misma, asi que vemos claramente la diferencia entre ambas, print con comillas triples sirve para mostrar las cosas en cierto formato, de forma de no tener que lidiar con comandos de saltos de linea y demas. RAW STRING : la traduccion de esto seria string cruda o sin refinar, las strings que delante de las mismas llevan una r minuscula, se toman como strings crudas, ignorando los caracteres especiales que puedan tener dentro. Como ejemplo pensemos en un path de un archivo \temp\narvaja si lo quiero incluir en una string tendre el problema que \t y \n tienen significados especiales y no se imprimen si no que ejecutan ciertas acciones, veamos:
Vemos que no hallara nunca una fila en ese path, jeje, para que respete la string y use los caracteres especiales como caracteres comunes, simplemente debemos ponerle un r adelante y convertirla en una raw string.
De esta forma la string se utiliza en forma cruda y se imprime lo que esta dentro de las comillas, sin usar los caracteres especiales de salto de linea, etc que veremos mas adelante. UNICODE STRING: De la misma forma si le agregamos una u delante la string es interpretada como que es una unicode string.
Si la imprimimos se ve como una string comun pero el sistema la utilizara como UNICODE.
CONCATENAR STRINGS: Ya habiamos visto este ejemplo
Vemos como concatenamos strings alli me falta intercalar un espacio
Ahora si le agregue una string con un espacio vacio y me quedo mejor.
REPETIR STRINGS: Usando la misma string del ejemplo anterior, al multiplicarla por cinco se repite cinco veces.
Veo que al repetir me falta tambien un espacio vacio para que quede bien.
Ahora si, le sume a s3 un espacio vacio, lo encerre entre parentesis, para que el cinco multiplique a todo el resultado ya que si no pusiera los parentesis la multiplicacion siempre se efectua antes que la suma en matematica y saldria mal. Asi que podemos concatenar y repetir en una misma operación Veamos otro ejemplo
Vemos que la flexibilidad para concatenar strings y repetirlas es en python una herramienta muy importante.
Alli al multiplicar por 20 repito veinte veces el mensaje anterior. INDICE: Tomemos una string por ejemplo la del ejemplo siguiente
Si utizo un indice que comience en cero, entre corchetes para indicar la posicion dentro de la string.
Veo que f2[1] es la letra U y asi puedo elegir imprimirla.
Como la el indice comienza en cero, f2[0] sera la letra M. INDICE SLICE O REBANADA: De la misma forma que el anterior si quiero imprimir solo MUN, por ejemplo tengo que usar un indice SLICE.
Es igual que el indice comun, solo que se indica desde donde hasta donde quiero ultilizar de la string sin incluir el ultimo indice, o sea f2 [0:3] es igual a f2[0]+ f2[1]+f2[2] sin incluir el ultimo indice, si quiero una parte intermedia de la string.
Si omito alguno de los dos indices
Interpretara que el indice que falta es cero. LARGO: la funcion len nos dara el largo de una string, usando la string del ejemplo anterior.
Podemos incluir el indice slice para saber el largo de una parte de una string, aunque es obvio que entre 2 y 5 hay 3 de largo.
FORMATO DE STRING: lo veremos con detalle mas adelante aquí algunos ejemplos print "a %s parrot" % 'dead' vemos que en la string anterior "a %s parrot" hay un %s dentro de forma que se reemplazara por una string que se coloque a continuacion como en este caso la palabra dead, sustituira a %s. si copio en la ventana interactiva
vemos que como %s se reemplaza por la string dead, si fuera %d seria que se reemplaza por un numero.
Luego veremos mas detalladamente lso tipos de formato que se pueden reemplazar en una string, esto es un ejemplo para que entiendan la idea. %s se reemplaza por una string que esta a continuacion de la misma luego de un % %d se reemplaza por un numero que esta a continuacion de la misma luego de un % y asi hay mas formatos que veremos mas adelante en detalle. BUSCAR EN UNA STRING FIND busca en una string si existen ciertos carcateres en la string f2= 'MUNDO FELIZ' si hago f2.find (`pa') me da –1, que significa que no la encuentra.
En cambio
Siempre que halla los caracteres que buscamos, me devuelve la posicion donde se encuentran, y que logicamente es diferente a -1. REEMPLAZAR: REPLACE sustituye una parte de la string si existe por otra, por ejemplo en f2='MUNDO FELIZ'
vemos que solo es lo que se muestra si imprimimos f2 sigue siendo MUNDO FELIZ, para que cambie debemos asignarle el resultado a otra variable f1.
Ahora si f2 sigue siendo 'MUNDO FELIZ' y f1 es 'MUNDO ALEGRE' SPLIT o SEPARAR: Usadndo SPLIT podemos dividir una string, en este caso le decimos que la divida en el lugar donde encuentre un espacio, por eso le pasamos como argumento espacio, si es una string larga separada muchas veces con comas, y le pasamos split y como argumento la coma la dividira cada vez que halle una coma y asi.
si separamos en la coma
Podemos separar en cualquier carácter, siempre que lo pasemos como argumento, en el caso siguiente separamos en las e.
ITERACION: se repite una operación, mientras el indice de una string recorre la misma, por ejemplo
Alli estamos la instrucción for es una iteracion y se repetira mientras x tome todos los valores de los caracteres de la string f2 que es 'MUNDO FELIZ', sera M la primera vez, luego U y asi, cada vez lo imprimira mediante el print x, hasta que se acaben los caracteres y ahi termina y sale del for. Creo que hemos dado una primera aproximacion al tema strings, el cual continuaremos la parte siguiente, el resumen de lo que hemos visto hoy esta en esta imagen que copiamos del libro,
No crean que esto es todo en cuanto a strings es solo un pequeño comienzo de lo poderoso que es el manejo de strings en PYTHON. Hasta la parte 6 Ricardo Narvaja
APRENDIENDO PYTHON DESDE CERO PARTE 6 ( strings y secuencias de escape) Ya vimos en la parte anterior, que la comilla simple y la comilla doble, sirven para lo mismo, y que realmente pueden utilizarse indistintamente y tambien para poder insertar el otro tipo de comilla dentro.
Asi que si quiero usar una string que tiene una comilla simple, la encierro entre comillas dobles y viceversa, python reconocera las comillas externas, y tratara la interna como un carácter mas. Otra caracteristica de la concatenacion de strings que no vimos, es la posibiilidad de poner una string al lado de la otra y la concatenara tambien.
Normalmente se usa el +, pero se puede hacer de ambas formas.
SECUENCIAS DE ESCAPE Si colocamos dentro de una string una barra invertida \ , dependiendo del carácter siguiente se ejecutara una accion. s = 'a\nb\tc' si tenemos esta string y la imprimimos
Vemos que el resultado fue: a b
c
Que ocurrio aquí? La barra invertida seguida de la letra n, es un salto a la linea siguiente, no se imprime, simplemente se interpreta como que queres seguir imprimendo en la siguiente linea, y la barra invertida seguida de la t, es una tabulacion por eso la c sale separada de la b.
Explicaremos ahora una por una las secuencias de escape Anular barra siguiente:
Poner una barra invertida adelante de otra, \\ , lo que hace es deshabilitar la barra invertida que esta a continuacion, asi se interpreta como un carácter comun, o sea que si quiero anular el efecto de alguna barra invertida, poniendole otra delante, se anulara.
Alli vemos que queremos imprimir un path 'c:\pepe\new', pero la \n que se encuentra en el medio, se interpretara como un salto a la siguiente linea, la forma de hacerlo, es poner otra barra invertida delante de la \n asi ignora el salto y lo copiara como caracteres.
Ahora si, podiamos haber tambien agregado la r delante para hacer una string cruda como vimos en los capitulos anteriores.
Pero muchas veces no queremos anular la funcionalidad de TODAS las secuencias de escape que haya si no solo de algunas, para estos casos es conveniente usar la barra invertida agregada.
En este ejemplo queria imprimir los paths uno abajo del otro, y use el salto de linea \n que esta entre ambos paths, mientras que los otros dos saltos de linea que necesitaba que no se ejecuten, les agregue la barra invertida adicional. Si hubiera hecho una string cruda se anularian los 3 saltos de linea y no lograria lo que quiero, poner los dos paths uno abajo del otro.
Anular comllla simple:
Si colocamos una barra invertida seguida de la comilla simple, \' , por ejemplo si quiero que alguna comilla se interprete como el carácter de comillas y se imprima asi, y no se use como comienzo de string le pongo delante una barra invertida y se imprimira como comilla. Si quiero escribir en python por ejemplo
python me da error porque la string se cierra en la comilla del medio, si a esta la anulo, agregandole una barra invertida delante.
Vemos que el resultado es el predicho, la comilla intermedia se interpreto como carácter y las otras como el comienzo y final de la string. Anular comillas dobles:
Si colocamos una barra invertida seguida de la comilla doble, \” , es similar al anterior, hara que la comilla se imprima como tal y no funcione como comienzo o final de string.
Campana:
Si colocamos una barra invertida seguida de la letra a , \a , es un efecto de campana, si hacemos un script.
al correrlo desde linea de comandos veremos que antes de imprimir pepe, suena un aviso sonoro, dentro de la ventana interactiva solo se muestra como el carácter BEL, por lo poco usado. Salto atras
Si colocamos una barra invertida seguida de la letra be, \b , significa realizar un espacio atrás, si realizamos este ejemplo
y lo corremos en una ventana de comandos
Vemos que realmente escribe pepe, luego corre el cursor tres lugares atrás, uno por cada \b, y luego escribe papa, por eso queda ppapa, ya que al retroceder el cursor y volver a escribir encima, borramos lo que habia alli. En la ventana interactiva nos muestra lo que haria pero según como este configurada lo ejecuta o no, en este caso nos muestra BS que significa que haria BACKSPACE o salto atrás alli.
por default solo ejecuta los codigos importantes y como saltos de linea y tabulacion., el resto los muestra solamente. Alimentacion de formulario
Es muy poco usado realmente pero es bueno conocerlo si colocamos la barra invertida delante de la letra efe, \f , es un comando que permite el avance de pagina a la impresora.
En la ventana interactiva lo muestra como FF, si lo ejecutaramos en un script, alli funcionaria perfectamente permitiendo avanzar el papel de la impresora, realmente muy poco usado hoy dia. Salto de linea:
Uno de los mas importantes, si colocamos la barra invertida delante de la letra n, se produce un salto a la linea siguiente, como si al escribir apretamos la tecla enter.
Alli vemos en el ejemplo cada \n, me salta a la linea siguiente donde continua imprimiendo. Retorno de carro:
La letra r luego de la barra invertida se interpretara como retorno de carro.
La diferencia con el salto de linea según el sistema esta en esta nota:
Tabulacion horizontal:
Si coloco la barra invertida delante de la letra te , \t , se ejecutara una tabulacion.
Tabulacion vertical:
si colocamos luego de la barra invertida la letra ve, \v , se ejecutara una tabulacion vertical, la ventana interactiva solo la muestra, no es de las mas importantes pero es bueno conocerla.
Valor hexadecimal y octal:
Si coloco la barra invertida y luego la letra equis, \x , interpretara que lo que hay a continuacion es un valor hexa
Si hago un print del mismo, imprimira el caracter ASC correpondiente a dicho valor hexa. Aquí tenemos la tablita de las correspondencias entre DECIMAL, HEXADECIMAL, OCTAL y el caracter que se imprime.
Como vemos el hexadecimal 41 corresponde a la A, por eso se imprimio en el ultimo ejemplo la misma.
Si quiero imprimir la A pero usando numeros octales, en la misma linea veo que corresponde al 101, veamos que pasa.
El octal se puede ingresar directamente luego de la barra invertida y en este caso el octal 101, corresponde al carácter A. Bueno con esto ya conocemos los carecteres de escape mas conocidos, algunos nos serviran otros
nos molestaran, y sabemos como anularlos, poniendoles una barra invertida mas delante, si no queremos que se ejecuten, normalmente al usar paths que tienen la barra invertida incluida, tendremos el problema de que al intentar abrir un archivo que se encuentra por ejemplo en c:\windows\files no funciona, y es que \f es un carácter especial de escape, y hay que ponerle otra barra delante para que no se ejecute y se reconozca e imprima como \f.
al agregarle la segunda barra anulamos el \f
es muy importante entender bien esto para evitar problemas al tratar de ejecutar archivos y manejar strings de paths, que tengan las secuencias de escape que acabamos de estudiar. Hasta la parte 7 Ricardo Narvaja
APRENDIENDO PYTHON DESDE CERO PARTE 7 ( strings en accion ) Esta parte sera de ejemplos y usos de las strings, algunos ya los hemos visto y practicaremos, otros los aprenderemos a medida que los vamos viendo, para que queden bien claros. Abrimos la ventana interactiva de python. >>> a= 'Estamos practicando strings' >>> print a Estamos practicando strings >>> len (a) 27 En este ejemplito asignamos la string a la variable a, luego la imprimimos y luego le hallamos el largo, en los ejemplos siguientes utilizaremos las strings que ya estan asignadas como en este caso la variable a, asi que no cierren la ventana interactiva, sino se perderan las asignaciones y cambios que le vayamos haciendo a las variables. Asi que tenemos una string almacenada en la variable a, de 27 caracteres de largo, si a b le asignamos a*2, deberia quedar en b con el doble de largo.
Jeje, y si era muy facil, jeje. A veces repetir una string, parece algo trivial, pero pensemos el siguiente ejemplo, quiero imprimir una linea horizontal. >>> print '____________________________________________________________' es bastante fastidioso, repetir tantas veces una tecla, para hacer una linea, mas facil es >>> print '_' * 80 en este caso de largo 80, y si nos quedo larga, podemos cambiar el numero facilmente hasta que se adapte al tamaño deseado.
Ya vimos que podemos hacer una iteracion para imprimir las letras de una string.
Este es un bucle que crea una variable x, que recorrera las letras que tiene a, y las imprime una a una, la coma que hay despues del print evita que salte a otro renglon, luego de imprimir cada letra, si le quito la coma.
Cada vez que imprime un carácter, salta a la linea siguiente:.
Si asigno a la variable dias el valor 30, y en la string siguiente hago un format string con formato decimal, se reemplazara el %d, por el valor que tenga la variable dias en ese momento, siempre que sea numerico, ya que %d pide un numero. Otra forma de buscar caracteres o partes de una string, es la siguiente ya vimos el comando find, pero existe otra forma.
En este ejemplo pregunto sil a string ''zip' esta o no, dentro de 'pepe.zip', como la halla, me devuelve True o sea que es verdad que esta incluida, en la segunda parte, pregunta si doc esta dentro de pepe.zip y como no esta devuelve False o falso. Aunque todavia no estudiamos los for y los if, les muestro un ejemplo que es facilmente entendible. Primero asigno a la variable a= 'pepe.zip' y luego hago
es una especie de detector si en el nombre de una archivo esta incluida la palabra zip, si la halla, imprime 'archivo zipeado', si no la encuentra imprime 'archivo no zipeado', si cambio la string que hay en a y pongo una que no tenga incluida la string zip, y luego ejecuto el if, me dira archivo no zipeado. Aunque no hemos visto aun como deben construirse los if, aquí hay algo que nos daremos cuenta facilmente.
Ahi vemos lo que en python se llama indentacion, o sangria, como python es un lenguaje que no usa llaves }} para determinar como en este caso el formato de los if, si entraste o no en un nivel interno del mismo, se realiza el mismo trabajo mediante la llamada indentacion, al escribir el if y apretar ENTER.
Vemos que el cursor se coloca automaticamente como si hubieramos tabulado, para que respetemos la indentacion, porque lo que vamos a tipear, solo se ejecuta si la condicion del if es valida, si no sigue al else: ya explicaremos el tema de la indentacion mas adelante, pero es bueno ver que si corro el cursor de donde me sugiere.
El error me dice, que en la segunda linea esta esperando un indented block, o sea que nos olvidamos de dejar el tabulado necesario alli, la indentacion esta incorrecta. Algunos ejemplos de indices y slices que vimos en las partes anteriores.
Creo que esto quedo suficientemente claro, lo unico que nos habia quedado en el tintero es que un indice negativo cuenta de atrás de la string para adelante, o sea que si tengo una string a='navidades' por ejemplo.
En el caso de que usemos numeros negativos, el primero de atrás hacia adelante es la 's' el -1, luego la 'e' el -2 y asi consecutivamente. O sea si usamos indices positivos :
Y si usamos indices negativos:
CONVERSION DE STRINGS Hemos visto que podemos que concatenar strigs,pero no podemos concatenar strings con enteros (recordar que para ello existen los format strings) Si por ejemplo quiero concatenar la string pepe con el numero 1, salta el error que me dice claramente que no puedo concatenar strings con integers.
La forma correcta con un format string es:
Haciendolo con format string no hay problema, reemplezara %d, por el numero 1. Tambien se puede usar la funcion str para convertir el numero 1 en la string '1' asi podemos concatenarlo facilmente
str convierte el numero entre parantesis a la string correspondiente
Luego que esta convertido a string podremos concatenarlo, repetirlo y aplicarle cualquier operación como a una string mas. La operación opuesta es la que convierte una string de numeros al numero correspondiente int ('42')
De esta forma puedo transformar una string de numeros, al numero correspondiente y hacer operaciones como si fuera un numero mas.
En este ejemplo, la funcion int convierte la string 42 en el numero 42, al cual luego le sumamos 89. Existe tambien una operación para convertir strings que representan numeros de punto flotante o sea con coma.
Dicha operación es float, en este caso convierte la string '1.5', al numero correspondiente el cual despues podemos realizarle las operaciones como a cualquier numero.
Alli float convierte la string '1.5' al numero 1.5 al cual le sumamos luego el numero 89 CAMBIANDO STRINGS Una limitacion del lenguale python es que puede leer de una string un determinado indice, pero dara error al tratar de asignarle un valor diferente al mismo, la misma string es inmutable, al asignarle nuevos valores por medio de indices.
Probemos esto:
Tenemos en la variable a, asignada la string navidades, si vemos a [0] es la n, si queremos asignarle otro carácter me da error dice que este objeto no soporta este tipo de asignacion.
Pero si lo hacemos asignandolo a una nueva variable b, donde construimos una nueva string, podemos armar la que queremos, primero ponemos la u que queriamos asignar, y luego de tomamos de a, usando indices, toda la string menos el primer carácter, al sumarlo obtenemos el mismo resultado, una string que tiene la u en vez de la a inicial. EJEMPLOS DE FORMAT STRING Asigno a la variable llamada de, la siguiente string >>> de='UUUUUUUUUUUUY' al tipear >>> a= 'Al ver la factura telefonica exclamo %s que barbaridad'% de y imprimir a o al ver su contenido
Vemos que el format string reemplazo el %s por el contenido de la variable de, que tiene que ser una string, ya que %s se utiliza para reemplazar por strings solamente. Se pueden combinar diferentes format strings en la misma instrucción: a= 'Al ver la factura telefonica exclamo %s que barbaridad me cobraron %d pesos ' %(de,500) aquí dentro de la string hay un %s y un %d, por lo que al final de la misma, debere poner entre parantesis, las entradas que deben respetar el orden como en este caso, la primera es una string, y la
segunda el numero 500, si cambio el orden que espera en este caso (string, numero) dara un miserable error jeje, pero mientras le vayamos pasando en el formato correcto y en el orden correcto, no hay problema.
En el siguiente ejemplo tenemos 3 entradas en el format string "%d %s %d you" % (1, 'spam', 4)
Siempre dbemos mirar cual es la string en este caso "%d %s %d you" y ir reemplazando una a una para ver que quedaria, el primer %d lo reemplazo por el primer numero dentro del parentesis, en este caso 1. “ 1 %s %d you" luego hay un %s, lo reemplazo por la string que debe estar a continuacion dentro del parentesis. "1 spam %d you" luego nos queda el tercero, lo reemplazamos por el tercer numero del paremtesis "1 spam 4 you" Asi obtenemos el resultado, creo que se entiende. En la parte 8 seguiremos con mas sobre strings, practicando y aprendiendo. Hasta la parte 8 Ricardo Narvaja
APRENDIENDO PYTHON DESDE CERO PARTE 8 ( format strings avanzado ) Para el manejo avanzado de format strings podemos usar los codigos de la tablita siguiente, los que explicaremos a continuacion con ejemplos para que se comprenda.
Antes que nada es bueno saber que a diferencia del lenguaje C, en python no se imprime y asigna directamente con format string, si no quedebemos realizar una asignacion o sea b= 'la sandia vale %d pesos ' % 100
es valida y si luego quiero imprimir b lo podre hacer en el siguiente paso
Lo que no esta permitido es realizar el format string y la asignacion dentro del mismo print en un solo paso.
Por lo tanto, si hay una asignacion, luego de realizada esta, se puede en un siguiente paso imprimir el resultado. Vamos otros ejemplos: si asigno precio=1234 y hago un format string decimal a='este objeto vale %d' % precio
Ahora si delante de la 'd' le ponemos un numero con un guion medio delante, aqui -6 no significa numero negativo, el guion medio (-) es el comando de justificacion izquierda, o sea que en este caso, significa que al reemplazarlo, lo hara, pero antes creara un espacio vacio de 6 lugares a partir de la izquierda, de la ubicación normal que tendria. a='este objeto vale %-6d' % precio 'este objeto vale 1234 ' Ahi vemos que luego de 1234 quedaron dos espacios vacios antes de la comilla final.
Alli vemos que reemplazo como antes por 1234, pero separo antes 6 lugares desde la izquierda del
lugar donde estaria ubicado normalmente, uso 4 caracteres y quedaron 2 vacios de los 6 reservados.
Alli vemos en verde la zona que separo de 6 lugares desde la izquierda, como 1234, tiene solo cuatro caracteres, quedan dos lugares vacios al final antes de la comilla. Si en vez de 6 pusiera 20, el lugar vacio seria mayor.
Alli vemos en verde la zona que preparo de 20 lugares vacios para escribir, y como ocupa solo los primeros 4 caracteres de esos 20, quedaran 16 lugares vacios antes de la comilla final. Otro comando curioso seria si en el ejemplo anterior en vez de usar el guion usamos un cero.
Vemos que en este caso separa los 20 lugares, los cuales llena con ceros delante, y al final coloca el valor que reemplaza jeje. O sea que hasta ahora vimos dos comandos el guion (-) que significa justificacion izquierda y el cero delante (0) que significa zero padding o llenar con ceros delante. Vemos que hay tres tipos de format string para punto flotante, %e, %f y %g, veamos los ejemplos.
Vemos que muestra el punto flotante en el formato 1.234568 e+000 con exponencial en base e, el que sabe de matematica sabe que en este caso el numero 'e', elevado a la 0, es igual a 1, asi que el valor final es el mismo. En numeros mas grandes
El resultado es 2.222222 e+055, realmente no haremos un curso de matematica aquí, pero es notacion exponencial en base ' e ' ,que en cualquier libro de matematica, se puede profundizar. Si cambiamos a %f el mismo ejemplo
Vemos que se muestra en formato de punto flotante comun y por ultimo %g muestra en punto flotante,pero redondeando el resultado.
Tambien aceptan en los format strings de punto flotante, la justificacion izquierda con el guion (-)
Vemos dos ejemplos, en el primer caso se reservan 9 lugares y como el numero de punto flotante tiene 8 caracteres, queda un solo espacio vacio antes de la comilla final, en el ejemplo de abajo como reserve 20 lugares, quedan luego de llenar con los 8 caracteres, 12 lugares vacios antes de la comilla final. Otro caso ocurre al poner un numero por ejemplo 0.2 en el formato, lo que hacemos es decidir cuantos lugares despues del punto se mostraran, en este caso, el numero 0.2, lo que hace es que se muestren solo dos decimales del numero de punto flotante.
Vemos que solo utiliza 1.23 o sea dos lugares despues del punto.
En el ejemplo anterior, mostrara 9 decimales, al poner 0,9 en el formato.
En este combinamos que se muestren solo 2 decimales al poner 05.2 y el 05 hace que se llenen con ceros delante hasta completar cinco caracteres en total usando el cero como padding, jejeeje. Vemos que combinando las tecnicas de justificacion izquierda (-), el zero padding o relleno de ceros (0) y la cantidad de decimales (0,5) por ejemplo para cinco decimales, podemos hacer muchos desastres je. En este ejemplo combinamos todo, en el primero justificacion izquierda de 30 lugares y 5 decimales y en el segundo reservo tambien 30 lugares llenando con ceros los que estan delante y 5 decimales tambien.
Es bueno repasar bien estos ejemplos y entenderlos bien, por eso yo voy despacito marcando bien cada paso.
Aquí vemos la forma sencilla que ya conocemos reemplazando el primer %d por el decimal 1, y el %s por la string 'spam' Otra forma de hacer lo mismo aunque un poco mas compleja, es la siguiente:
la string es '%(v)d %(t)s' all hay dos indices v y d que se reempazan los valores en la llave que esta a continuacion de la string. Mas adelante veremos que el objeto entre llaves se denomina diccionario, y lo aplicaremos en ejemplos en su momento. Otros de los ejemplos de la tablita que nos faltan:
Al utilizar %c, reemplaza por el carácter correspondiente al valor ASCII, que colocamos,
Los que no tienen carácter se reemplazan por la representacion correspondiente, hexadecimal 14, o
sea '\x14'.
%d es decimal y %i es entero,%u unsigned integer, no hay diferencias, en los ejemplos siguientes se ve que es indistinto, aunque el que mas se usa es el %d.
Si es %x lo pasara a hexadecimal al reemplazar.
Bueno creo que con esto ya conocemos bastante del tema de format string que luego lo utilizaremos
al programar, al menos hemos visto la mayor parte de los ejemplos sobre el tema y podemos entender que es lo que significa una string con formato y como manejarlas, punto y aparte al tema siguiente. 'Hasta la parte %d' % 9 Ricardo Narvaja PD:jejeje
EJERCICIOS HASTA LA PARTE 8 Ejercicio 1) sean las variables c='pesos' a=67.873 b=-65432.86 d='euros' crear una string con formato para cada caso siguiente, de forma que cuando la imprima la salida sea a)'si me das vuelto me quedaran 67 pesos' b)'si me das vuelto me quedaran 67.9 pesos' c)'si me das vuelto me quedaran 68 pesos' d)'si me das vuelto me quedaran 6.79e+001 pesos' e)'si me das vuelto me quedaran 6.79e+001 euros' f)'si me das vuelto me quedaran 000000000006.79e+001 euros' g)'si me das vuelto me quedaran -65364.987000 euros' ---------------------------------------------------------------------------------------------------------Ejercicio 2) sea la variable a='56743' y b='67893' Crear un script que testee si el primer caracter es un 5 y si es asi imprima CORRECTO si no es cinco imprima INCORRECTO. probar el script con a y b, y debe decir CORRECTO e INCORRECTO cuando corresponda. ---------------------------------------------------------------------------------------------------------Ejercicio 3): Igual que el ejercicio anterior, pero debe imprimir el valor de la variable 'a' o 'b', que se esta chequeando tanto si es correcto como si no lo es. ---------------------------------------------------------------------------------------------------------Ejercicio 4) : Igual que el ejercicio anterior, pero debe imprimir el valor del primer caracter solamente de la variable 'a' o 'b' que se esta chequeando tanto si es correcto como si no lo es. Ricnar
APRENDIENDO PYTHON DESDE CERO PARTE 9 ( strings-metodos) Los metodos son simples funciones que se asocian a un determinado tipo de objeto como en este caso las strings. Los metodos para las strings, funcionan solo para strings logicamente, si los queremos aplicar a otro tipo de objetos no funcionaran. Estos metodos estan preincluidos en python asi que no es necesario importar nada, y realizan funciones un poco mas complejas, los veremos a continuacion: Abramos la ventana interactiva: Si asigno una string a la variable a >>> a='japones' cuando en la siguiente linea tipeo a y a continuacion el punto me aparece una ventana con opciones.
Si recorro las opciones, veo los metodos, que ya trae incluidos python, para aplicar a una string, por ejemplo cuando aparece replace, apreto TAB.
Vemos que el metodo replace, lo que hizo fue reemplazar 'ja' por 'pi'
En a sigue estando 'japones', si quiero reemplazarlo definitivamente, debere asignarlo a otra variable, en este caso b. Ahora b contiene 'pipones' y a contiene 'japones' Aquí estan los metodos que trae incluidos python, en una lista luego los explicaremos.
En este tute, me ayuda mas y quizas es muy util que a la larga comencemos a usar un IDE un poco mas avanzado, no quiere decir que lo que se haga en un IDE, no se pueda hacer en otro, pero las ayudas que da usar un IDE mejor ya se empiezan a poner en evidencia aquí, y es bueno verlas, el IDE de activepython ayuda un poco, pero no mucho,sobre todo cuando haces scripts, en cambio el IDE que les voy a mostrar, ayuda muchisimo mas, sobre todo al programar scripts complejos. http://ricardonarvaja.info/WEB/OTROS/HERRAMIENTAS/V-W-X-YZ/Wing%20Ide%20Professional%202.0.4-1%20With%20Keygen%20En.rar alli esta con keygen y todo y se adjunta un tute de absolom porque activarlo es medio dificultoso por no decir un dolor de huevos, jejeje. Abro el WING ya activado, el mismo tiene una pestaña que es similar a la ventana interactiva de pythonwin llamada PYTHON SHELL, alli podremos tipear comandos sueltos, igual que en la ventana interactiva de pythonwin, quizas no es tan poderoso como la ventana interactiva de pythonwin, porque el poder del WING realmente se encuentra en su editor de scripts, que es el que mas ayuda a hacer programas de los que conozco, por eso es que lo utilizaremos a partir de ahora, porque ya cada vez utilizaremos menos la ventana interactiva, y mas la edicion de scripts.
Ahora comenzaremos a ver algunas ventajas que tiene la edicion de scripts en wing con respecto a otros editores.
Vemos que si creo un script vacio y solo apreto la a, me sale una lista de los comandos que python tiene incluidos que empiezan con a, y a la derecha si pongo en la pestaña SOURCE ASSISTENT, una explicacion de lo que hace cada uno, jeje, muchisima ayuda, en este caso como solo es la variable a, no hay que elegir nada sigo de largo escribiendo a=
Cuando termino de escrbir la variable a en la informacion me aparece
que es una variable string, y si cambio a la pestaña SOURCE BROWSER vere las variables que se van creando.
Volvamos al asistente SOURCE ASSISTANT.
Vemos que al igual que en el otro IDE me aparecen la lista de metodos para las strings, alli esta capitalize, pero al estar resaltado ya me aparece, el significado del metodo y como funciona.
Alli dice que el metodo capitalize, retorna una copia de la string, con la primera letra cambiada a mayuscula. Ademas me dice que se aplica a una string y que se debe escribir sin argumentos o sea con () al final. Con toda esa ayuda no necesito mas nada ya me dijo todo jeje.
Ahora imprimiremos la variable b
Al solo escribir la p me aparece la ayuda puedo elegir print y TAB o hacer doble click y se selecciona. Tambien en el menu de autocompletar me salen las variables que cree, aparece la b, la cual elijo.
Le agregue el RAW_INPUT al final y guardo con algun nombre en el menu FILE-SAVE AS.
Alli en el menu DEBUG, esta la opcion para ejecutar el script.
Alli vemos la salida que es la string 'Japones', a la cual le fue cambiada la letra inicial a mayuscula, gracias al metodo capitalize, si queremos tracear este script. Si quiero tracearlo apretando f7 que es STEP INTO me aparece un menu por si quiero cambiar algunas opciones se menu tiene una tilde que me da a elegir si no quiero que aparezca mas cada vez que corro el mismo script, la cambio y directamente acepto la ventana con OK y a tracear.
Alli estamos en la primera linea, al pasar a la segunda con f7, vemos abajo en la pestaña stack data los valores que van tomando las variables.
Tambien si hacemos click derecho en el nombre de la variable, nos mostrara donde se le asigno el valor resaltandolo.
Apretamos de nuevo f7 y en el stack data, vemos el valor que tomo b que es 'Japones' con mayuscula.
Ademas hay otra pestaña que se llama DEBUG I/O que muestra las entradas y salidas cuando estas debuggeando, si imprimis b, alli se debe ver.
Para pasar por encima del raw_input, debo apretar f6 que es STEP OVER si no dara error porque estoy intentando entrar dentro de un comando, y eso no se puede, para eso existe el STEP OVER o f6, logicamente si apreto f6 para pasar por encima, luego debo ir a la ventana de entrada y salida y ingresar el texto que me pide el raw_input y ENTER para que pase a la siguiente linea.
Tambien puedo poner Breakpoints
Si pongo un breakpoint debo saber que solo funcionara si el programa corre en modo DEBUG, si lo ejecuto, correra normalmente sin parar en ningun breakpoint. Al apretar DEBUG, correra hasta que encuentre un BREAKPOINT o algun error, o pare por algun motivo como algun input de teclado, por ejemplo. Veamos mas metodos para las strings. Si borro capitalize al tipear el punto sale la ayuda y marco CENTER
Que nos dice la ayuda
Bueno esta es un poco mas compleja pero no tanto
Si vemos cuanto vale b despues de tracear, en el stack data
Lo que hizo center, fue centrar la string 'japones' en otra de 20 lugares de largo que es el parametro que le pasamos, tiene un parametro mas opcional que por default, si no colocas nada, la centra entre espacios, pero uno puede elegir el carácter entre el cual se hara el padding para centrarse.
Si le pongo como segundo argumento una 'Y', luego traceo veo que en el stack me queda
Puedo seguir traceando hasta que imprima b, veo que la centro entre 20 letras y.
si le cambio
Lo paro, le doy f7, cuando llego al center y lo quiero pasar me da error
Y me lo dice muy grande, el segundo argumento debe ser un solo carácter, no una string, jeje logico
no se puede hacer padding con dos letras, con un solo carácter es lo correcto, jeje. Pruebo usar la letra M de padding.
Ahora si, al tracear veo que hizo el padding usando la letra M.
El proximo metodo es COUNT
Bueno este retorna el numero de ocurrencias de una substring buscando en la string principal, tambien tiene la opcion de poner un slice para marcar donde quiero que comience a buscar y donde termine de buscar. La ayuda es tanta que cuando voy a poner el primer argumento
en la ayuda me muestra cual es el que le toca entrar.
El que no sabe, luego lo explicaremos con mas detalle, self es una definicion, no es un argumento en si, el primer argumento es el que esta marcado o sea la substring, alli no lo marque yo, si no el mismo IDE, diciendome que eso es lo que debo ingresar.
Cuando escribo el primer argumento y salto al siguiente, la ayuda tambien cambia al siguiente.
Como estos son opcionales por ahora no los usaremos, asi que usamos uno solo.
Al tracear y pasar el count vemos que b quedo valiendo 1, que es la cantidad de veces que se repite, 'po' dentro de 'japones'. Si cambio la palabra 'japones' por 'pompones' que tiene dos 'po' veamos que pasa.
Traceo con f7
y b toma el valor 2, y sigo traceando lo imprimira en la salida
Si le agrego los argumentos opcionales, en este caso buscara cuantas veces esta 'po' entre las posiciones 2 y 6 y logico solo una vez estara.
Luego de pasar el print
Vemos que imprime b que vale 1. Bueno vimos algunos los metodos mas sencillos, en la parte 10 seguiremos con mas metodos para strings. Hasta la parte 10 Bye bye
Bueno en primer lugar quiero agradecer enormemente a los miembros de SHOCK por esta fantástica release… fantástica en cuanto a utilidad que no en cuanto a facilidad de uso. Espero que si alguna vez os dá por hacer una release, al menos expliqueis correctamente como se usa… Empezamos … en el archio rar del crack viene entre otras cosas las instrucciones… esas ya las podéis tirar… además viene un zip, que se llama crack.zip En su interior encontrareis…
Bien pues aquí empieza el cachondeo, para que funcione debemos adivinar donde coño, (y perdonad por la expresión) ponemos cada archivo… Antes de que os pongais a buscar ya os lo digo yo… El wingide.exe y el python23.dll los colocais en C:\Archivos de programa\Wing IDE 2.0\bin Eso si lo habeis instalado por defecto… si no pues ya sabéis .. Tupuñeterodirectorio\bin El _sre.pyd lo metéis en… C:\Archivos de programa\Wing IDE 2.0\bin\PyCore\DLLs Este como pa encontrarlo facil esta, grrrrr
NOTA importante: Para hacer estos cambios ha de estar el Wing Ide cerrado, si no, no podréis sobreescribir las dll´s.
Bueno ahora si intentais arrancar el keygen vereis que despues de escribirle como unos gilipollas todos los numeritos se os cierra antes de que os de tiempo a copiar vuestro serial… asi que InicioEjecutarcmd Y abris una consola de dos
Ahora navegáis hasta donde esta el keygen, esto supongo que no hace falta explicarlo… pero por si acaso os lo pongo tambien, pa que nadie se me queje cd.. (para subir de nivel) cd archivos de programa cd wing ide 2.0 cd bin wingide y apretamos return Abrimos también el WingIde y nos salta un cartelito que nos recuerda que aun no lo hemos crackeado… En el escribimos un codigo cualquiera del tipo CNXXX-XXXXX-XXXXX-XXXXX, donde X corresponde a un integer, digoooooooo a un número… Pongamos por ejemplo y para no complicarnos mucho CN123-12345-12345-12345 Lo pegamos en el sitio correspondiente y le damos a continuar..
Hacemos lo mismo con el keygen que deberiamos tenerlo a la espera de este primer codigo… (supongo que sabreis pegar y copiar en y desde las consolas de DOS, asi que ya no os explico más) De aquí pasamos a otra pantalla donde nos suelta un código con un formnato parecido al que nosotros le metimos y que tambien le tenemos que pasar al keygen…
señalamos la segunda opción, le metemos ese número al keygen ( se puede copypastear) y ahora una ultima bromita del cracker de SHOCK( también apañetelas tu mismo) te pregunta para que SO es el keygen (windows, linux o mac), hay que ponerle windows en minusculas ¡!! Esto es importante
Le damos a continuar y ahora si, ya tenemos el soft crackeado…
Además de activar un buen soft que nos va a ayudar en nuestra ardua tarea, hemos aprendido como NO hacer una release. Hasta la próxima.
APRENDIENDO PYTHON DESDE CERO PARTE 10 (mas strings-metodos) Realmente la idea no es que nos aprendamos todos los metodos para strings de memoria, porque son muchos, si no que sepamos, como interpretar la ayuda que nos da en este caso el IDE que tenemos, si usamos el WING, tendremos la ayuda sobre que es cada cosa, y si no, podemos usar google y buscar ayuda sobre el metodo como cualquier hijo de vecino, usando el activepython.
Vemos que el activepython nos da una lista de los metodos tambien, aunque con menos info podemos debuggear en el active python, elijjiendo la columna
. Y guardando el script antes de debuggearlo, podemos poner BREAKPOINTS y tracear tambien el codigo perfectamente en el active python, eso si no tendremos un autocompletado tan poderoso, pero hoy dia buscando en google se encuentra info de cualquier cosa, asi que,con paciencia, igual se puede hacer.
De cualquier manera nosotros seguiremos aprendiendo a manejar el WING ya que es mas completo, y ayudandonos con el, iremos hallando el significado de mas metodos para strings. El siguiente metodo que miraremos sera endswith.
Cuando tipeamos la a y el punto nos aparece el menu de autocompletado.
Al poner el cursor sobre la palabra endswith en la ayuda nos muestra: Return True if S ends with the specified suffix, False otherwise. With optional start, test S beginning at that position. With optional end, stop comparing S at that position.
O sea que si acaba en la substring que nosotros ponemos como modelo, nos devuelve True, y si no termina en la substring, nos devuelve False En el ejemplo como le pasamos la subestring 'es', nos dara True o verdadero ya que 'japones', termina con 'es'.
Alli vemos que imprime True si hubieramos puesto cualquier otra string nos devolveria False
Si ponemos que mire si termina en 'nes', por supuesto tambien nos devolvera en b= True y lo imprimira. El proximo que miraremos sera find.
Esta nos busca una substring, dentro de una string mayor y nos dice el indice a partir de donde la encuentra (recuerden que los indices empiezan en 0, como ya se explico en la parte 5). Además podemos especificar a partir de que indice debe empezar a buscar… En el ejemplo nos devuelve que la letra 'o', la encuentra en el lugar 3, por primera vez, y si repetimos la busqueda diciendo que encuentre a partir de 4 en adelante, la siguiente vez la encuentra en el lugar 9.
En este otro ejemplo busca la subestring 'os' y la encuentra en el lugar 9.
Como vemos la ayuda siempre es util, nos dice que tipo de variable lleva cada entrada, para ver que esta esperando en cada caso, y la definicion del metodo. Veremos en este caso join.
La ayuda nos dice algo, que retorna una string que se concatena con la que pasamos como secuencia, que es tambien una string, probemos.
Yo se que el parametro es una string, asi que lo mejor a veces es probar a ver que pasa, le doy debug.
Jeje, concateno la string principal, usando las letras en este caso el numero 8, de la substring sequence, jeje, si la cambiamos por 123456789, a la misma.
Vemos que separa la sequencia carácter a carácter, y en medio le pone la string principal repetida, no se si es de mucha utilidad, pero descubrimos algo jeje. El ejercicio seria agarrar algunos metodos faciles, de pocos parametros y tratar por las de uno, de averiguar que hacen, hay un anexo que va con este tute con la explicacion rapida de muchos metodos de strings, seria bueno que practiquen con uno o dos y si ven que no pueden hallar su funcionamiento, en ese caso vayan a ver que alli estan la mayoria, salvo algunos que requieren mayor explicacion como ENCODE, DECODE y se veran mas adelante. Los metodos que estan en el anexo (algunos pocos ya los vimos aqui) y que son faciles para practicar son: S.endswith S.expandtabs S.find S.index S.isalnum
S.isalpha S.isdigit S.islower S.isspace S.istitle S.isupper S.ljust S.lower S.lstrip S.replace S.rfind S.rindex S.rjust S.rsplit S.rstrip S.split Estas son todas las que hay en el anexo, no es necesario saberlas todas de memoria, las pusimos separadas a ver si alguien se anima y practica un poco como hallar con la ayuda del WING, que hace cada una, si las pongo todas aca, tienen la solucion a la vista. Bueno con esto terminamos el tema de metodos para strings en la parte 11 veremos otra cosa, jeeje hasta la 11 Ricardo Narvaja
ANEXO MODULOS PARA STRINGS S.endswith
Return True if S ends with the specified suffix, False otherwise. With optional start, test S beginning at that position. With optional end, stop comparing S at that position. O sea que si acaba en la substring que nosotros ponemos como modelo, nos devuelve True, y si no termina en la substring nos devuelve False En los ejemplos…
S.expandtabs
Si en la string que le pasamos al método hubiese un tabulador (como lo hay en el ejemplo \t) sustituira el tab, por los espacios en blanco que le pasamos como parámetro, si no pasasemos nada nos devolvería 8 espacios por cada tab. En el ejemplo se ve mas claro…
S.find
Esta nos busca una substring, dentro de una string mayor y nos dice el indice a partir de donde la encuntra (recordar que los indices empiezan en 0, como ya se explico en la parte 5). Además podemos especificar a partir de que indice debe empezar a buscar…
Ejemplo de substring
S.index
Esta en principio parece igual que la anterior pero no es asi, ya que si la substring no existe, con el metodo find nos devuelve un -1, y con el index nos saltará un error… Veámoslo…
Lo mismo con s.find
S.isalnum
Como su propio nombre indica, mira los caracteres de la string que le pasamos y si son alfanumericos nos devolverá True, en cualquier otro caso nos devolverá False.
Ahora viene una gran familia de los IS… estas son casi autoexplicativas, en todas ellas se retorna True o False, según la condicion que se compruebe en cada una de ellas, además todas devuelven False si la string está vacia. S.isalpha
Esta es parecida a la anterior pero en este caso para que retorne True, solo debe haber caracteres alfabéticos
S.isdigit
Pues otra más como las anteriores, esta vez comprueba que los caracteres sean números
S.islower
Otra más, esta vez, miramos si todas son minúsculas…
S.isspace
Esta es medio rara, miramos si toda la string esta rellena de al menos un espacio en blanco…
S.istitle
Otra un pelín rara, nos devuelve True, si la string esta en formato título. Es decir detrás de una letra, solo puede ir una minúscula, y en cualquier otro caso sólo una mayúscula (un espacio en blanco entre dos palabras es uno de ellos) Mejor un ejemplo…
Un último ejemplo de mayúsculas…
S.isupper
Lo contrario a islower….
Y con esto terminamos los IS…
S.ljust
Sirve para justificar a la izquierda un texto… podemos especificar cuantos espacios dejaremos a su derecha, y si queremos que se rellenen con algun carácter ( NO STRING).
20
S.lower
Como su nombre indica nos devuelve una string toda en minúsculas, si es que hubiese alguna mayúscula escrita…
S.lstrip
Es decir, si no ponemos nada y hubiese un espacio antes de la string lo borraría, si ponemos como en el ejemplo la substring ‘ja’, el resultado es un ‘pones’
Como recordaran en la parte 5, habiamos visto que podiamos hacer lo mismo con un slice…
Y parece que hemos hecho lo mismo, pero no es así, el resultado es el mismo en este caso, pero en el primero siempre quitamos dos caracteres, y con el lstrip buscamos coincidencia, y solo si existe extraemos la substring… S.replace
Buscamos una substring y la sustituimos por otra… en este caso suistiumos ‘os’ por ‘as’
también podemos definir el numero de veces que tiene que hacer la sustitucion, por ejemplo solo 2 veces… y solo lo hará en las dos primeras ocurrencias que encuentre…
Y aquí ya podemos lanzarnos a imaginar miles de posibilidades….
S.rfind Parecido al find, esta vez nos devuelve el indice mas alto en el que encuentra la substring
Podemos especificar un slice de comienzo y final…
En este caso le pedimos el indice mas alto en el que encuentre la substring ‘os’ hasta que llegue al indice 13, por lo que encuentra en el indice 2 la segunda parte de ‘osos’. S.rindex Exactamente igual que el anterior pero si no existe la substring, en lugar de devolver -1, saltará un error… Para mas información leer S.index S.rjust Parecida a S.ljust, pero el padding o la justificación son por la izquierda…
S.rsplit Hacemos un split de la string principal, separandola en substrings mas pequeñas cada vez que encontremos el “modelo” que pasamos como argumento, teniendo en cuenta que se empieza a mirar por la derecha de la string(por eso la r de right)… Además podemos especificar cuantas separaciones maximas queremos hacer…
Notese la diferencia entre los siguientes, y como se empieza por la derecha….
S.rstrip Como su homologa S.lstrip, pero empezando por la derecha
S.split Con esta partimos la string en substrings cada vez que encontremos el parametro.
Podemos especificar el maximo numero de cortes que haremos…
Y bueno hay muchas mas pero creo que con estos ejemplos se entiende la idea, si uno necesita alguna en especial que no este aquí, busca info en el WING o en google y siempre si tiene una base saldra adelante ricnar
11-APRENDIENDO PYTHON DESDE CERO (listas) Luego de terminar con el objeto string, pasaremos a los siguientes objetos que son las listas, las cuales explicaremos a continuacion. LISTAS Las listas son realmente una colección ordenada de objetos de diferentes tipos, pueden contener strings, numeros y pueden contener tambien otras listas. Las listas son accedidas por offsets, y como las strings pueden ser concatenadas, y pueden usarse indices, slices y demas. A diferencia de las strings las listas pueden crecer y achicarse, mientras las strings solo se achican o agrandan cuando les realizamos operaciones y asignamos en otras variables, pero la string original sigue inmutable. Si yo hago c= ' pepe' d= c + ' campeon' Realmente pepe no crecio en c sigue siendo la misma string, lo que se hizo fue concatenar con otra string y guardarla en otra variable, por lo tanto lo que dijimos anteriormente es cierto, las strings no aumentan ni disminuyen en tamaño, pero las listas si pueden aumentar y disminuir. Ejemplos de listas Si abro una ventana interactiva o en WING en la pestaña PYTHON SHELL y tipeo L1=[] Una lista sin ningun contenido es una lista vacia.
Este segundo ejemplo es una lista de cuatro items, vemos que son todos numeros y que si llamo mediante subindice al primero sera L2[0] ya que como vimos los indices siempre empiezan a contar desde cero. L2 = [0, 1, 2, 3]
Alli uso indices, si quiero obtener mas de un item, uso un indice similar al de la strings.
Alli al igual que en el caso de las strings, mostrara los dos primeros items, ya que el colocar que termine en el indice 2, excluye al que esta en esa posicion, realmente muestra el que esta en posicion 0 y el que esta en posicion 1. En este ejemplo vemos claro que una lista es realmente una lista de objetos que pueden ser de diferentes tipos. L3= ['pepe', 90, 0x34]
Si quiero los dos ultimos items al igual que con las strings pongo el indice desde donde quiero que se inicie, y si pongo los dos puntos y obvio el segundo indice, significa que obtendre todos los items hasta el final.
Dijimos que una lista podia contener otra lista dentro veamos: L2 = [0, 1, 2, 3] L3= ['pepe', 90, L2]
Vemos que el tercer item de la lista L3 es la lista L2 que esta incluida, puedo llamar a los items de L2 con doble indice ya que L3[2] =[0, 1, 2, 3] para mostrar a su vez el indice de estos tres objetos, habra que colocar un segundo indice que ubicara la posicion de los 3 items internos de esta sublista.
INDICE SLICE O REBANADA Al igual que en las strings puedo sacar una rebanada de la lista por ejemplo L4=[54, 'pepe', 'redjau', 'crackslatinos', 0, 1, 2, 3] si quiero hacer un indice slice desde 2 a 5 L4 [2:5]
Como sabemos el primer item es el de posicion cero, asi que el 2 sera 'redjau' y luego incluira todos los items hasta el 5, sin incluir este ultimo, asi que realmente seran los item 2, 3 y 4, que corresponden a: [54, 'pepe', 'redjau', 'crackslatinos', 0, 1, 2, 3] LARGO DE UNA LISTA La funcion len, nos dara la cantidad de items que tiene una lista, si usamos la del ejemplo anterior, tendra 8 items.
CONCATENAR Y REPETIR
Si tenemos a L4 y a L2 que son las listas de los ejemplos anteriores. Si concateno las dos listas L3= L4+L2
Vemos que uni las dos listas respetando el orden en que estan sus items, primero colocare todos los items de la primera lista de la suma, y luego le agregare en orden todos los items de la segunda lista, creando una tercera. Repetir una lista es sencillo L1=[9,'pepe'] si la quiero repetir 20 veces por ejemplo la multiplicare por 20 L2= L1*20 >>> L2=[9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe', 9, 'pepe'] Se repetira el contenido de la lista original la cantidad de veces que hayamos repetido, en este caso 20 veces. ITERACION En forma similar a las strings podemos interar, si usamos la lista del ejemplo anterior L2. >>> for x in L2: ... print x Recordamos que adelante del print debemos dejar lugar vacio por la indentacion, si ejecuto x, ira tomando los valores de cada item de L2, la primera vez sera x=9, la segunda vez sera x='pepe' y asi recorrera toda la lista imprimendo todos sus items hasta que se acaben. Para que no sea tan largo podemos agregar una coma, despues de la x, asi los imprime todos en la misma linea.
APPEND Si le quiero agregar un item a una lista puedo realizarlo sumandole otra lista, o con el comando APPEND.
Vemos que a la lista L1, que tiene solo dos items, con append, le agregamos un item mas , en este caso el numero 3.
Luego le agregue la string 'sol', asi que me quedo una lista de cuatro items. EXTEND
A diferencia de append que agrega items completos, extend ( 'sol'),agrega tres items, uno por cada letra de la palabra sol.
Lo mismo si es un numero lo agregara un item por cada cifra. ORDENAR: El metodo sort que se aplica a listas, sirve para ordenar, en el ejemplo son tres items que estan desordenados, y sort lo colocara en orden alfabetico. .
En el siguiente ejemplo se ordenan strings que representan numeros
Si son tres numeros los ordenara tambien
Y si son numeros y strings mezcladas, primero colocara los numeros en orden, luego las strings en orden y asi sucesivamente.
INDEX Me devuelve que indice es el siguiente item.
Vemos que si le pregunto por el item 4, me dice que esta en la posicion 1 en la lista. REVERSE Sirve para ordenar pero en orden inverso al de sort, de mayor a menor.
DELETE Sirve para quitar un item
Si quito uno del medio los siguientes se correran hasta completar las posiciones, y estar
consecutivos.
Bueno creo que con esto tenemos bastante claro el tema de las listas en la parte 12 veremos los diccionarios que es el siguiente tipo de objetos. Hasta la parte 21 Ricardo Narvaja
Lista o array .count Cuenta el número de concurrencias, items iguales a la muestra, que tenemos… En el ejemplo vemos como encuentra primero una coincidencia y despues dos con el item de referencia que es la string ‘o’.
.insert Como su nombre, indica, sirve para insertar un item en medio de otros de un array, para ello, tenemos que proporcionarle el index en el cual queremos que se inserte, y el item a insertar.
.pop El contrario del insert, lo que hacemos es quitar el elemento apuntado por el index( si no ponemos nada se quita el último por defecto), y además nos devuelve el item que quitamos en la variable que asignemos, ( si es que asignamos alguna variable para contenerlo)
.remove También sirve para quitar un elemento, pero en este caso especificamos el elemnto a quitar y si hay mas de uno quitará el de index mas bajo.
APRENDIENDO PYTHON DESDE CERO (diccionarios) El proximo objeto a estudiar seran los diccionarios, tal como las listas son colecciones de objetos ordenadas, a los cuales se apunta por indices para saber la posicion de algun item en la misma, los diccionarios son tambien colecciones de objetos, pero no se apuntan por un indice de orden como en el caso de las listas, por lo cual se puede decir que son desordenados. Al no poder referenciarse por indices numericos, el referenciamiento de una entrada dentro de un diccionario se realiza por medio de keys, veremos a continuacion ejemplos que nos aclararan facilmente el panorama sobre este objeto tan util. Los diccionarios para diferenciarlos de las listas que se escribian entre corchetes, se escriben entre llaves {}, por lo tanto asi como la lista vacia era. L1=[] el diccionario vacio es D1={} aquí tenemos un simple ejemplo de un diccionario de dos entradas. D2 = {'spam': 2, 'eggs': 3} Vemos que cada entrada de un diccionario esta compuesto por un par de valores, en el ejemplo anterior la primera entrada esta compuesto por el par 'spam': 2 donde 'spam' es la key y el 2 es el valor asociado correspondiente, realmente aquí vemos que en las listas teniamos items apuntados por indices, aquí tenemos entradas, con un valor que es apuntado por keys que son strings, y cada valor tiene una key correspondiente y unica para poder ubicarlo en el diccionario. D2 = {'spam': 2, 'eggs': 3} Por ejemplo si en este diccionario quisiera ubicar entrada 1, no serviria de nada utilizar el metodo de indices D2[1] que serviria para listas, aquí es erroneo.
Alli lo vemos sale un error diciendo que en este diccionario no hay ninguna key con el valor 1, y por eso da error, si colocaramos.
Alli si la key 'eggs', me devuelve su valor correspondiente que es 3, de esta forma los diccionarios trabajan con entradas, que son un par, una string llamada key y su valor asociado correspondiente. Si tenemos este diccionario de 3 entradas D2 = {'spam': 2, 'ham': 1, 'eggs': 3} esta claro que las keys son las strings `spam', 'jam' y 'eggs' y sus valores correspondientes seran 2, 1 y 3. Realmente los diccionarios no tienen un orden, uno puede escribirlos en un orden determinado y cuando uno los llama mas adelante, estar en otro orden, python mantiene la relacion entre una key y su valor asociado inmutable, pero su ubicación puede variar, ya que realmente no interesa, pues no se llama por indices de posicion.
Aquí vemos que yo escribi las entradas en un orden, y pyhon al mostrarlos, lo hizo en un orden diferente, pero nunca cambio la relacion entre una key y su valor asociado, por lo cual las propiedades del diccionario se mantienen, siempre en el ejemplo anterior, 'spam' se asocio con 2 y 'eggs' con 3. D9={'pepe': 5, 'ham': 'happy', 'spam': 2} Otro ejemplo en este caso el valor asignado a la key 'ham' es una string en este caso 'happy'. LARGO DE UN DICCIONARIO La funcion len, aquí tambien nos devuelve la cantidad de entradas que tiene un diccionario. D2 = {'spam': 2, 'ham': 1, 'pepe': 3} len (D2)
Logicamente el resultado es 3, pues posee tres entradas. La concatenacion y repeticion de diccionarios no esta soportada. TEST DE KEY Uno de los metodos incluidos para diccionarios, es has_key, el mismo le pregunta a un diccionario si tiene la key correspondiente entre las que posee.
D2 = {'spam': 2, 'ham': 1, 'pepe': 3} D2.has_key('ham')
Me responde que es verdadero, lo cual es logico porque posee dicha key, si pregunto una que no tiene, me respondera falso.
Otra forma alternativa de testear si un diccionario posee una key es 'ham' in D2
CREAR UNA LISTA DE LAS KEYS DE UN DICCIONARIO El metodo keys, buscara todas las keys de un diccionario y hara una lista con ellas.
Usando el diccionario anterior
D2 = {'spam': 2, 'ham': 1, 'pepe': 3} D2.keys( )
Vemos que ha creada una lista, por supuesto como toda lista va entre corchetes, y se puede asignar a otra variable que sera una lista.
CAMBIANDO DICCIONARIOS D2 = {'spam': 2, 'ham': 1, 'pepe': 3} Usando el ejemplo anterior podemos alterar un diccionario asignandole un nuevo valor correspondiente, a una key. D2['pepe']=5
Alli le cambio la asignacion a la key 'pepe', antes tenia el valor 3, ahora 5. Por supuesto como ya vimos, el valor asociado correspondiente , puede ser tanto numerico como strings, puedo asignarle a la key 'ham', el valor 'happy'.
BORRANDO UNA ENTRADA D2 = {'spam': 2, 'ham': 1, 'pepe': 5} del D2['ham']
No necesita mucha explicacion con del, borramos la entrada que queremos.
AGREGANDO ENTRADAS Se realiza de la misma forma que cambiabamos un valor asignado a una key, solo que en este caso al usar una key que no existe en el diccionario, python la creara.
Como la key 'hurra' no existia, creo una nueva entrada con dicha clave, y el valor asignado 90. MAS METODOS DE DICCIONARIO Python trae metodos especiales para trabajar en diccionarios algunos ya los vimos, existen mas por ejemplo: D.values() Usemos para todos estos ejemplos el diccionario D2. D2={'hurra': 90, 'pepe': 5, 'spam': 2} >>D2.values()
Me devuelve una lista con los valores asociados.
Alli asigne el resultado a una lista L4. D.items() Si a D2 , si le aplico el metodo items()
Me devuelve una lista con tantos items como entradas tenia el diccionario, ya sabemos que una lista puede contener en un item, cualquier cosa, en este caso entre parentesis, esta cada key con su valor asociado. D.get() Si a D2 , si le aplico el metodo get
Me sirve para obtener el valor asignado, pasandole su key, de esta forma puedo utilizarlo asignandolo a una variable.
D.update() Ya dijimos que la concatenacion directa al estilo de las listas no existe, pero el metodo update hace algo bastante parecido. Sean: D2={'hurra': 90, 'pepe': 5, 'spam': 2} D3={'keke': 7, 'pepe': 7} D2.update (D3)
Vemos que a D2, le agrego las entradas de D3, y si alguna key ambos tienen similar, el resultado tendra el valor asociado de la ultima, por eso se llama update, porque agrega lo que no tiene y cambia si alguna key esta en ambas, por el valor de la ultima. El ejemplo es claro D2 no tenia la key 'keke', por eso se la agrego, y la key 'pepe' se actualizo al valor que tenia D3, o sea 7. UN EJEMPLO DE DICCIONARIO REALISTA table = {'Python': 'Guido van Rossum', 'Perl': 'Larry Wall', 'Tcl': 'John Ousterhout' }
Alli tenemos la variable table, que es un diccionario, que esta compuesto de 3 entradas, cada una tiene una key que es el idioma, y su valor asociado es el autor del mismo. Ahora si creo una variable llamada lenguaje lenguaje='Python' Ya que dicha variable es una key en mi diccionario, puedo hallar el nombre de su creador con el. >print table [lenguaje]
Imprimiendo de acuerdo al lenguaje, el nombre de su creador ITERACION Podemos iterar por las keys de un diccionario, usando el ejemplo anterior.
De esta forma iterara tomando x el valor de todas las keys de un diccionario, es similar a:
Si quiero imprimir los valores de un diccionario
Logicamente haciendo print table [x], ya que x toma el valor de las keys, imprimira los valores correspondientes. Si quiero hacer una tablita con cada key y su valor correspondiente al lado.
Queda medio desacomodado, si le pongo tabulacion.
Ahora quedo mejor jeje Bueno creo que con esto queda claro el tema dccionarios, si quieren averiguar los metodos restantes, siempre es buena practica hacerlo uno, hay algunos mas seguro se agregaran como anexo a esta parte, y podran tener la informacion y verificar si pudieron hallar los metodos por ustedes mismos correctamente. Hasta la parte 13 que veremos tuplas Ricardo Narvaja
.clear Vaciamos un diccionario de todo su contenido, obteniendo como resultado un diccionario vacio.
.copy Bueno esto según la ayuda es una copia somera, poco profunda, de diccionarios, jejeje, es un tema peliagudo, voy a tratar de explicarlo…. Si decimos E=D.copy() creamos un nuevo diccionario E que hereda los valores de D, y a partir de este momento se comporta como undiccionario nuevo.
D2 E1 Que no es lo mismo que hacer E1=D2, que es como si dijesemos un puntero a nuestro diccionario D2, es decir si modificamos E1 modificamos D2.
En la imagen vemos como si lo asignamos directamente, en realidad E1 es un puntero, u otro nombre si lo quereis decir asi, para exactamente el mismo diccionario… .fromkeys Sirve para hacer un nuevo diccionario a partir de uno existente, asignando de esta forma.
>>> D2.fromkeys (['hurra','pepe'],8) {'hurra': 8, 'pepe': 8} O sea a las keys que elijo y cierro entre corchetes, le asigna el valor que le coloco a continuacion en este caso 8.
.get Es parecido a cuando pediamos un item determinado de un diccionario escribiendo D2[key], pero en este caso si el elemento no existe, no saltara una excepción, si no lo que nosotros especifiquemos…
Veamos ahora lo que ocurre si lo hacemos con D2[key] y no existe la key…
Coo veis con el .get, como no hemos especificado lo que hacer si no encuentra la key, simplemente nos responde ‘None’, pero al intentarlo llamando directamente a la key, nos salta una excepción. .has_key Preguntamos al diccionario en cuestión si tiene una key igual a la muestra que le pasamos, si la tiene nos responde True, y si no False.
.pop Si os acordais de cuando las listas, pues aquí basicamente lo mismo, con el pop quitamos un item del diccionario, que le pasamos. Si la key no existe, saltará una excepción a menos que especifiquemos lo contrario.
excepción
como un SHE
.popitem Otro de esos metodos estupidos que uno los mira y no sabe que carajo pretendían con él… Sirve para quitar el primer elemento del diccionario (siempre el primero)y devolverlo como una tupla de 2 (o sea una pareja ordenada)
.setdefault Ponemos por defecto el valor de la key de un diccionario, para cuando queramos asignarle un elemento, o si ya hubiese un elemento con esa key nos devuelve el diccionario sin modificarlo. Es decir es parecido al método .get, pero en este caso si la key no existe, la crea y la añade.
Y hast aquí hemos llegado. Thanks al ayudante maravilla ricnar
APRENDIENDO PYTHON DESDE CERO (tuplas) Las tuplas, son una colección de objetos ordenados que son bastante parecido a las listas, se encierran entre parentesis, en vez de corchetes como las listas. Las principales caracteristicas de esta colección de objetos son: 1) Es una colección ordenada de objetos. 2) Al igual que las listas se accede por offset o indice 3) A diferencia de las listas que podian mutar, las tuplas son inmutables, solo se cambian items al asignarlas a otra tupla. 4) Tuplas son mejor conocidas como arrays de referencia, ya que es una forma sencilla de acceder por offset a objetos. Veamos los ejemplos y alli se aclarara: Un tupla vacia. t1=()
Recordemos que las otros colecciones de objetos que conociamos, las listas se cierran entre corchetes [ ] y los diccionarios entre llaves { }, en el caso de las tuplas el conjunto se encierra entre parentesis, aunque no es obligatorio, si un conjunto de objetos esta separado por comas, y no esta encerradoentre parentesis, python lo interpretara como una tupla. t1= (0,)
Esta es una tupla de un solo item, vemos que si es de un solo item habra que poner la coma, si no la colocamos la interpretara como un entero.
una tupla de cuatro items t2 = (0, 'Ni', 1.2, 3)
Otra tupla de cuatro items similar a la anterior t2 = 0, 'Ni', 1.2, 3 Vemos que los parentesis son opcionales, podemos no colocarlos, pero cuando queremos ver el contenido de la tupla python la muestra con parentesis, por claridad visual.
Una tupla dentro de otra t3 = ('abc', ('def', 'ghi')) Al igual que en las listas, los items se llaman por indices que comienzan en cero, si usamos el ejemplo anterior t3 [0]
vemos que nos muestra el primer item, ya dijimos que las tuplas son conjuntos ordenados, asi que por medio de indices se puede acceder a ellos perfectamente. t3[1][1]
Como en el caso de las listas, si hay una tupla dentro de otra, para acceder a un solo item de la tupla que esta dentro, habra que usar doble subindice, Existe el indice slice o rebanada t2 = (0, 'Ni', 1.2, 3)
Alli tomamos una rebanada que comienza en 2 y termina en 4 excluyendo a este ultimo o sea mostrara los items 2 y 3 como vemos. y la funcion len >len (t2)
Vemos que len nos da la cantidad de items de una tupla Existe tambien concatenar y repetir Concatenar (1, 2) + (3, 4) (1, 2, 3, 4) o tambien t2 = (0, 'Ni', 1.2, 3) + (4,5) >>> t2 (0, 'Ni', 1.2, 3, 4, 5) alli vemos por si alguno no le quedo claro el concepto de mutable o inmutable, como las strings las tuplas son inmutables, lo cual quiere decir que no podemos asignarle directamente un valor nuevo a un item sin crear una tupla nueva, eso si podemos concatenarla al igual que las strings con otras y muliplicarlas, la inmutablidad se refiere al hecho de que si hago.
Veo que no puedo cambiar directamente un item, lo mismo que pasaba en las strings, no podia cambiar una letra de la misma directamente
Aca en las tuplas es similar, puedo concatenarle objetos y repetirlos, pero no puedo asignarle nuevos valores en la misma tupla, para ello hay que crear una segunda.
Ahi vemos que para reemplazar el primer item de una tupla, tuve que crear una nueva, con un item de otra tupla, y concatenarlo con los restante que corte con el slice.
t2=(0, 'Ni', 1.2, 3, 4, 5) t2*4 repetira 4 veces la misma tupla
se puede iterar perfectamente t2=(0, 'Ni', 1.2, 3, 4, 5) for x in t2: print x, Esto imprimira todos los items de la tupla.
Las tuplas no tienen metodos asociados, por lo cual si queremos operar en forma flexible, como ordenarlos con sort, o usar cualquier metodo de las listas, habra que convertirlas en listas. Si tengo la t2 anterior y la quiero convertir a lista t2=(0, 'Ni', 1.2, 3, 4, 5) l2=list (t2)
Con eso creo una lista con el mismo contenido que la tupla original , vemos que esta cerrada entre corchetes, y podre aplicarle los metodos que existen para listas. Ahora puedo aplicarle por ejemplo el metodo sort para ordenarlo l2.sort ()
Y ahora que esta ordenadito puedo transformarlo a tupla nuevamente
t2=tuple (l2)
Una vez que todo alumno aprende lo que son las tuplas, salta la pregunta inmediata, porque existen las tuplas si ya existen las listas? Bueno la respuesta es un poco dificil, muchas son razones historicas de los lenguajes, pero en la practica vemos que a veces en un programa muy extenso trabajar con tuplas nos asegura la inmutabilidad de las mismas y que no haya en el medio una asignacion que cambie un item de la misma, y nos trastoque el programa. Es a gusto del consumidor hay dos variedades de algo parecido, para que se creo el destornillador tipo philips existiendo el plano, y bueno, son parecidos cumplen una funcion parecida y cada cual colocara tornillos philips o planos según crea mejor, es asi. En el caso de la programacion, es bueno tener diferentes alternativas y aunque parezcan parecidas, las caracteristicas de inmutabilidad o mutabilidad, o que existan metodos o no, pueden hacer que en algun caso sea mejor usar un tipo de objeto que otro. Hasta la parte 14 Ricardo Narvaja
14-APRENDIENDO PYTHON DESDE CERO (archivos) El ultimo tipo de objeto son los archivos y con esto terminamos la enumeracion y la explicacion de cada uno. La mayoria tenemos nocion de los archivos de nuestra PC, compartimientos que alojan informacion en nuestra computadora que son manejados por el sistema operativo. La funcion open que viene incluida en python, crea un objeto archivo, que es un link a un archivo que reside en nuestra maquina. Una vez que llamamos a open, podemos leer y escribir en el archivo externo asociado, gracias a metodos especiales para archivos, que python trae incluidos. OPEN o FILE (ABRIR O CREAR) Cuando queremos establecer un link a un archivo existente o crear un nuevo archivo utilizamos el metodo open. (es similar a usar file (), los parametros son los mismos y hacen exactamente lo mismo) b=open('C:\myfile1', 'w')
Veo que cree un archivo vacio llamado myfile1, con permiso de escritura (w), y especifique el path, si no especificara path, lo creara en donde se ejecuta el script. Bueno alli cree un archivo vacio donde podre escribir, aunque realmente lo que tengo en python es ahora un link a ese archivo, que se mantendra hasta que no lo cierre. Si escribo ambas lineas en wing en la parte superior, donde se escriben los scripts, para ver la ayudaque me da , vere que al tipear b, que es un objeto archivo y el punto me salen los metodos que puedo aplicarle al archivo.
Elegimos el metodo write para escribir algo en el archivo, vuelvo a la ventana shell o interactiva, para poder ver los resultados al ir tipeando cada linea.
b=open('C:\myfile1', 'w') b.write('hello text file\n') y luego cerramos el link al archivo con lo cual ya no podremos aplicarle metodos a no ser que usemos open en el nuevamente. b.close () Ahora que lo cerre el trabajo esta terminado, si lo arrastro a un bloc de notas vere lo que escribi
Ahora si quiero leer el contenido tendre que reabrirlo, en este caso lo hago con el parametro r o de lectura, El parametro 'r' funciona solo si el archivo ya existe en nuestra maquina en el path correcto, si el archivo no existe dara error al no hallarlo, o sea 'r' no crea un archivo vacio como en el caso de 'w', solo lee lo que ya existe. a = open('C:\myfile1', 'r') a.readline()
Con readline leere la primera linea del archivo
Si repito a.readline(), intentara leer la siguiente linea y como no hay mas dara vacio. a.close () Ahora la cierro y le agrego a mano varias lineas mas al archivo, en este caso lo guarde con la extension txt.
a = open('C:\myfile1.txt', 'r') lo reabro para lectura puedo leer linea a linea a.readline( )
cada vez que hago un readline salta a leer la linea siguiente
puedo crear una lista con las lineas que leo del archivo. a.readlines( )
Como habia leido ya dos lineas con readline, en este caso readlines continuara donde dejo el puntero de lectura el metodo anterior y creara una lista desde alli hasta el final. Puedo leer el contenido entero. S= a.read ()
Veo que tuve que volver a hacer open nuevamente, porque readlines deja el puntero de lectura apuntando al final del archivo, en el siguiente caso tambien lo tuve que hacer. Puedo leer una cierta cantidad de caracteres S= a.read (5)
Si repito veo que lee los cinco siguientes, por lo cual debo tener cuidado al leer porque si el puntero de lectura queda apuntando al medio, pues no podre leer del principio sin resetearlo.
Si quiero escribir en el mismo archivo, tendre que cerrarlo con close y abrirlo nuevamente para escritura. a.close () a = open('C:\myfile1.txt', 'w') a.write('pepito')
Crearemos el equivalente al comando copy (COPY MENU.TXT MENU.BAK) # Primero abrir dos archivos para read(r) y write(w)se llamaran menu.txt y menu.bak, el archivo menu.txt debe existir y es el que quiero copiar, el menu.bak lo creara el script. inp = open("menu.txt","r") outp = open("menu.bak","w") # leeremos cada linea, copiando cada nueva linea al otro archivo dentro de una iteracion, line ira tomando los valores de cada linea y los escribiremos en la salida. for line in inp: outp.write(line)
print "1 file copied..." # Ahora cerremos los archivos y listo inp.close() outp.close() ACLARACION IMPORTANTE DEL FUNCIONAMIENTO LOS PARAMETROS DE R W y A PARAMETRO 'r': Para abrir un archivo que ya existe en nuestra maquina, lo podemos hacer con r, asi lo abriremos para lectura, pero si el archivo no existe no lo creara y nos dara error. PARAMETRO 'w':Abrira un archivo que ya existe para escribir en el y lo que habia alli antes se perdera, si no existe el archivo se creara vacio. PARAMETRO 'a' Si el archivo no existe y queremos crearlo nuevo, podemos como vimos usar 'w', pero perderemos su contenido, la otra opcion es usar 'a', tambien si no existe lo creara, y si existe le agregara datos a continuacion de donde termino sin perder la info existente. Por eso esta variante del script
inp=open("C:\menu.txt","r") outp=open("C:\menu.bak","a") for line in inp: outp.write(line) print '1 file copied...' #ahora cerremos los archivos y listo inp.close() outp.close() cada vez que lo usemos ira agregando a continuacion en bak sin perder la info que tenia antes el mismo, siempre apendeando al final lo que haya en menu.txt.
Existen muchos metodos para archivos los cuales probablemente con la ayuda de WING podremos saber facilmente que hace cada uno, es muy probable que un anexo con algunos metodos mas se agregue a esta parte, ustedes pueden tratar de sacarlos y si no pueden revisar los anexos para ver si esta bien lo que hallaron o ayudarse. Terminamos con los objetos, y su enumeracion, en la parte siguiente seguiremos con otro tema. Hasta la parte 15 Ricardo Narvaja
Unos pocos métodos que nos faltaban para completar la parte de manejo básico de archivos. Algunos poco útiles otros quiza más… a.isatty Nos devuelve True si el archivo esta “conectado” a un dispositivo TTY. Evidentemente en mi caso da False
a.seek Este comando esta relacionado con el read, que ya se vió en la parte principal del curso. Sirve para colocar el puntero de lectura en un byte determinado del documento (ojo este documento hay que abrirlo con permisos suficientes o nos dará error al leer… quedáis avisados.)
Como veis hemos colocado el puntero en el 4 byte, y a partir de alli como no hemos especificado un numero de bytes en read hemos leido hasta el final del documento. Podemos pasarle un segundo parámetro al seek, que puede ser 0, 1 o 2. En el cual especificamos desde donde tiene que empezar a contar los bytes… Por ejemplo, si queremos leer un byte desde el final de nuestro documentousaremos el 2, y como es hacia la izquierda le pasamos una “distancia” negativa( en este ejemplo de -3)
a.tell Sirve para decirnos el largo del archivo en bytes...
a.truncate Este sirve para especificar cuanto debe de pesar el archivo como máximo en bytes… Por ejemplo…
Si ahora vamos a C:/ veremos colgando allí nuestro archivo… del cual ha recortado un trozo para que pese exactamente 10 bytes.
Para terminar una cosita que se nos había pasado en la parte 14, y que a lo mejor debería ir allí, y es como leer un archivo en binario ( por supuesto podemos mover el puntero a lo largo del archivo y leer desde donde queramos. Se hace llamando a open con el parametro ‘rb’, para que se vea mas claro he cogido un .exe que tenía casualmente colgando de C:\
Y si abrimos el archivo que hemos creado con un editor hex
Hasta la próxima.
15-APRENDIENDO PYTHON DESDE CERO (comparaciones) Cualquier objeto de python puede compararse, veremos a continuacion las diferentes posibilidades en las comparaciones. Abramos el WING o el editor que utilicemos y vayamos a los ejemplos: En python no debemos confudirnos, el signo igual simple, refiere a la asignacion como hemos visto en las partes anteriores, si yo hago pepe= '90' es una asignacion, a la variable pepe, le asigno la string '90', pero existe para las comparaciones el signo de doble igual == que sirve para testear si un objeto es igual a otro y nos devolvera verdadero si lo es o falso, si no es igual. A= [67,99,'pirulo'] B= [67,99] C=['pirulo'] alli tenemos tres objetos listas, vemos que asignamos valores a cada una de ellas mediante el simple signo igual.
Las copio en la ventana interactiva o shell del WING, ahora si comparo A con B a ver si son iguales mediante el doble signo igual A==B
Vemos que al no ser A igual a B, el resultado es False o falso. Ahora si comparo A con B+C concatenadas, es obvio que el resultado sera True pues es una igualdad ya que a B le faltaba solo el item 'pirulo' y C es justo eso.
Por lo tanto el operador == testea si ambos miembros de ña igualdad, tienen el mismo valor.
Existe otro comparador que se utiliza poco pero hay que conocerlo tambien que es el operador is, dicho operador testea si ambos objetos son iguales y ademas estan ubicados en el mismo espacio de memoria del programa, lo cual es poco util pero bueno, jeje.
Vemos una comparacion de si d y e son iguales y logicamente son iguales, eso se ve a simple vista, pero que pasa si usamos is ademas de comparar si son iguales verifica otros aspectos.
En el caso de strings chicas ademas de ser iguales, se ubican en el mismo buffer de memoria y el comando is, nos devuelve que es true.
Aquí vemos dos strings grandes h , i que son iguales, pero al ser muy grandes el comando is, no solo verifica que sean iguales como ==, si no que ademas verifica que esten en un mismo cache de memoria, y al ser grandes caen fuera y me puede devolver falso, como en este caso a pesar de ser iguales. Realmente el chequeo que se usa en todos los programas para igualdad es el signo doble igual, el comando is queda para roles de programacion avanzada, pero bueno al menos conocemos un poco algun caso que nos muestra una diferencia entre ambos, por lo cual vemos que es conveniente usar == para verificar igualdades. Tambien tenemos en las comparaciones los simbolos de mayor ( >) y menor (>> while x>0: ... print x, ... x=x-1 ... 9 8 7 6 5 4 3 2 1>>>
Bueno ahi tenemos una iteracion while, que aun no estudiamos pero while significa que va a iterar mientras sea verdadera la condicion que esta al lado, en este caso que x sea mayor que cero. Como x comienza valiendo nueve y la condicion es verdadera o sea que x es mayor que cero, entrara en el while y loopeara hasta que vaya disminuyendo por el x=x-1 que esta dentro y cuando llegue a cero saldra por que x>0 sera falso. Una variante de esta iteracion, es imprimir el valor de x cuando salio, para verificar el valor de x en ese momento.
Si armamos un script x=9 while x>0: print x, x=x-1 print 'salimos con x valiendo',x
Si lo debuggeo con STEP INTO o f7, vere como va loopeando escribiendo en la pestaña DEBUG I/O lo que va imprimiendo y en el STACK DATA vere los valores de cada variable como van cambiando, especialmente x que empieza siendo nueve, y va disminuyendo hasta que se hace cero y sale. Sale del loop cuando x vale cero y imprime el mensaje de salida.
La verdad el cartel de salimos con x valiendo cero, deberia estar en otra linea, lo arreglaremos.
Ahora si al ponerle el \n, salta a la otra linea donde escribe el mensaje de salida.
Es bueno ver que el print del mensaje de salida, debe estar nuevamente en el margen, sin indentacion, eso significa que se ejecutara cuando salga del while, si le colocara la indentacion que tiene x=x-1, se ejecutara dentro del while y se repetira cada vez que pase por alli en el loop.
Alli vemos el segundo ejemplo, testea que se repita el loop mientras x sea verdadero, o sea diferente de cero como habiamos visto, en la practica se comporta igual que el caso anterior ya que antes testeaba que fuera verdadera la expresion x>0.
Los valores numericos se comparan directamente y el mas grande es el mayor, en este caso se ve que la variable y que vale 2 es mas grande que la variable x que vale -1 que es negativo y menor, si pruebo si x es mayor que y el resultado es falso.
Si aumentamos x para que sea mas negativo en este caso -5, vemos que sigue siendo menor que y que vale 2.
Ya vimos que los objetos numericos son verdaderos si son diferentes de cero y falsos si son cero, los objetos que no son numericos son verdaderos si su contenido es diferente de vacio, si quiero saber si una lista es verdadera, debera tener algun contenido, la lista vacia [] es falsa.
Alli en la tabla vemos ejemplos de objetos verdaderos y falsos, la string 'spam' como cualquier otra que tenga contenido es verdadera, mientras que la string “” que es vacia es falsa, asi mismo la lista vacia [] es falsa, el diccionario vacio {} es falso, el numero 1 es verdadero y el 0 es falso, y None que es un objeto especial similar al NULL de C, es tomado siempre como valor falso. Existe una extension que se aplico a partir de Python 2.3 y es un nuevo tipo llamado booleano (bool) o sea que puedo asignar a una variable directamente el valor True o False.
Funcionan realmente de la misma forma como hablamos de que cero era verdadero y diferente de cero era falso, es el mismo concepto en este caso, se le da entidad con un nuevo tipo que se puede asignar directamente comparar etc.
Alli vemos otro ejemplo, a la variable x se le asigna el valor True al inicio, por eso mientras x sea verdadero entra en el while, donde imprime, y luego se le cambia el valor a False, lo que hace que salga e imprima que salimos con x valiendo False.
En el siguiente ejemplo vemos que tambien se puede usar combinados el mayor e igual (>=) y el menor e igual (>> [a, b, c] = (1, 2, 3) >>> a, c (1, 3) Vemos que en el primer miembro hay una lista de 3 items, y en el segundo una tupla de 3 items, python entendera que queremos asignar uno de cada uno de la lista, con el correspondiente de la tupla y si ambos tienen la misma cantidad de items lo hara.
Si le agrego un item mas a cualquier miembro, dara error ya que no puede asignar uno del primer miembro a uno del segundo, sin que sobren.
En el siguiente caso en el primer miembro hay una tupla de 3 items y en el segundo una string de 3 caracteres, de la misma forma python asignara cada item de la tupla con un caracterer de la string, siempre y cuando tengan la misma cantidad de items y caracteres. >>> (a, b, c) = "ABC" >>> a, c ('A', 'C') En el ejemplo la tupla tiene 3 items y la strings 3 caracteres exactamente por eso es aceptada. ASIGNAMIENTO MULTIPLE
Vemos que asignamos dos o mas variables a la vez con un mismovalor, en este caso ambas con el valor 36.
Alli asigne tres variables a la vez. Otra formas exoticas de asignar: red, green, blue = range(3)
la funcion range() devuelve el rango de enteros de cero al valor encerrado entre parentesis en este caso 3, excluyendo este ultimo, o sea que devuelve 0, 1 y 2, los cuales al ser exactamente 3 valores, se asignan a las tres variables de la tupla del primer miembro.
Lo mismo, si le cambio el argumento a la funcion range a cuatro, al devolver cuatro valores, me dara error, al no coincidir las 3 variables de la tupla con los 4 valores y quedar desparejas. Reglas para los nombres de variables Realmente los nombres de las variables son bastante flexibles, deben eempezar siempre con una letra o guion bajo, no se aceptan en el primer lugar numeros, a partir del segundo carácter pueden ir numeros o letras o guiones bajos perfectamente.(no se aceptan otros simbolos en el nombre de variables que no sean los guiones bajos. Validas: _spam spam spam33 s33pam Spam SPAM_ SPAM
Son todas validas con la aclaracion que en el nombre de las variables no es lo mismo mayusculas que minusculas por los tanto SPAM sera una variable diferente que spam y que Spam, no conviene poner en el mismo programa dos variables que solo se diferencien en que sean mayusculas o minusculas, como las de este ejemplo ya que se puede prestar a confusion pensando que es la misma variable y es diferente. No validas:
1spam (empieza con numero) $spam (no se permiten simbolos solos guiones bajos) @spam (idem) spam@ (idem) Tampoco podemos elegir las palabras reservadas de python, si quiero llamar a una variable while, python no me dejara y me dara error, pues posee un comando con ese nombre. En la tabla siguiente vemos las palabras reservadas por Python.
ASIGNACIONES DE INCREMENTACION A partir de python 2.0 se han agregado formas de incrementar variables abreviadas que provienen del lenguaje C. Por ejemplo forma tradicional: X=X+5 nueva forma: X+=5 En la tabla siguiente vemos las diferentes formas que se aceptan
O sea: X+=Y
es similar a
X*=Y
es similar a X=X*Y
X=X+Y
y asi sucsivamente con todas las operaciones de la tablita Si le apicamos a una string pepe= 'spam'
pepe+= ' campeon' implicara que se esta concatenando
Bueno terminamos con el primer tipo de declaraciones, la proxima seguiremos con el siguiente. Hasta la parte 17 Ricardo Narvaja
17-APRENDIENDO PYTHON DESDE CERO (declaraciones, print,if )
DECLARACION PRINT En este caso print es un comando de python reservado para ejecutar la accion de imprimir la salida. El comando print, imprime objetos a la salida standard, que al igual que en C es stdout: y es normalmente la salida, al menos que uno la redireccione a un archivo, como ya hemos visto en partes anteriores. Veamos el comportamiento de print, un poco mas en detalle, el mismo agrega por default un espacio al imprimir items que estan separados por comas.
Vemos que si escribimos un print de varios items separados por comas, al imprimirlos les agregara un espacio entre ambos como indica la flecha. Lo mismo, al terminar el print, si colocamos una coma, continuara imprimiendo a continuacion, si no coloco la coma, saltara a la linea siguiente. Alli vemos el siguiente script donde se aprecia bien lo explicado
Vemos que al ejecutarlo, al imprimir el contenido de la variable z que es 'fff', lo hace a continuacion, pues el print anterior termino en una coma y dejo abierto que se continue imprimiendo seguido. Si ahora agrego otro print mas.
Vemos que luego de print z, salto a la siguiente linea al no haber coma al final. Dentro de un string podemos concatenar strings.
Y aplicar format strings como vimos en capitulos anteriores, habiamos visto que no podiamos asignar a una variable y hacer formats strings dentro de un print a la vez, pero si solo imprimimos no hay problema podemos hacer format strings, dentro de un print sin asignar.
El programa 'HELLO WORLD' es el primer programa que se realiza cuando uno esta aprendiendo un idioma vemos que es realmente muy sencillo en python.
Realmente es un programa de una sola linea, el input () lo coloco para que la ventana de comandos no se cierre, y me permita ver la salida, pero no es realmente parte del programa. Existe una forma un poco mas dificil de entrada y salida, que muchas veces en necesario usar, según el programa.
Si pongo el cursor en sys y veo la ayuda vemos que sys es un modulo que nos provee acceso a ciertas funciones y objetos que interactuan con el intreprete entre ellas stdin y stdout.
stdin y stdout son usados por input, raw input y print respectivamente asi que si los utilizo puedo realizar la misma accion sin usar esos comandos.
Vemos los ejemplos:
Vemos que pude imprimir sin usar el comando print, analogamente puedo ingresar texto sin usar el input o raw_input lo cual veremos cuando estudiemos los inputs. DECLARACION IF La declaracion if es usada para seleccionar acciones alternativas según el resultado de algun test. El ejemplo mas simple usando if
Alli vemos un script simple usando if, en este casoa la variable x le asigno el valor 9 al empezar, y al entrar al if, el test verifica si x es mayor que 10, si es mayor, entra e imprime la string 'mayor que 10', si es menor salta al else e imprimira 'menor que 10' Realmente el comando if toma una desicion según el resultado del test, de imprimir una o otra instancia, entrara en la primera solo si es mayor y si no lo es seguira a la segunda instancia donde si entrara. Debemos recordar que la sintaxis del if lleva un simbolo dos puntos (:) luego del test, y el else tambien lleva un simbolo dos puntos a continuacion, y logicamente cada nivel que entremos dentro de un if llevara una indentacion mas, si anidamos un if dentro de otro.
Alli vemos que el if interno debe comenzar indentado para que se ejecute dentro de la primera instancia, y tendra a su vez, sus print tendran una indentacion mas para demostrar que entramos otro nivel mas, cuando imprimimos si es mayor o menor que 20.
Vemos que la salida imprime que el valor de x que es nueve, es 'menor que 10' , ahora si ponemos x igual a 18.
Puedo tracear y ver como según las comparaciones entra y va imprimiendo los mensajes correspondientes.
Alli comienzo a tracear
llego a la comparacion, puedo mirar en el stack data los valores de x
si traceo con f7 veo que como es mayor que 10 entra.
Sigo traceando
Alli tambien entra ya que el test es verdadero o sea x es menor que 20, sigo con f7.
La siguiente linea es el fin del programa ya que a los else solo llegaria si alguno de los test fue
falso, lo cual no se da en este caso. En este otro ejemplo solo uso el if, el else no es imprescindible ya que si no se cumple la condicion tambien sale fuera y continua el programa asi que puede usarse de esta forma: este ejemplo
de esta forma al no tener indentacion el print de salimos, python interpreta que ya salio del if y el programa continua y imprime 'salimos'.
Otro ejemplo:
Al testear la condicion de si 1 es verdadero ya sabemos que siempre se evalua un valor diferente de
cero, sera verdad y si fuera cero sera falso asi que como es verdadero, imprimira la palabra verdadero. Si cambio el 1 por 2 sera similar
En una expresion que se evalua si es verdadera o no, cualquier valor diferente de cero es verdadero, y por lo consiguiente imprime verdadero.
Este caso es la inversa, analizamos si la expresion not 2 es verdadera y sabemos que 2 es verdadero asi que not 2 sera falso, por lo cual saltara al else que imprimira la palabra falso. Otro ejemplo un poco mas complejo:
Alli vemos que la variable x contiene la string 'asesino de conejos' y la compara para ver si es Roger Rabbit , si es verdad que la variable x es 'Roger Rabbit', entra e imprime 'Hola Jessica', luego hay un nuevo comando que se llama elif que es como si fuera una segunda posibilidad o nuevo if, para que fuera verdadero, antes de saltar al else, en este caso vuelve a testear si es 'Bugs Bunny' y si es entra e imprime 'Que hay de nuevo viejo' y si no es, pues en ese caso considera que es el asesino de conejos y salta al else y pide que salgan corriendo jejeje. Se pueden poner tantos elif como queramos
Si x llega a valer alguno de los valores contra los cuales compara, entrara e imprimira su precio si
no es igual a ninguno ira al else y imprimira 'precio equivocado' al no hallar nada. DECLARACIONES EN EXPRESIONES Python puede usar en algunos casos, declaraciones en expresiones, nos referimos a expresiones sueltas que no tienen un comando de los que reserva python para ejecutar una accion determinada, como en el caso anterior print o if, los ejemplos son los siguientes: Print interactivo: Si tipeamos el nombre de la variable en una ventana interactiva, python nos devuelve el contenido de la misma sin tener que hacer un print, por ejemplo:
Alli vemos que tipeamos solo spam y si es una variable nos muestra su contenido, o sea esta evaluando esa expresion y mostrando su contenido sin ponerle ningun comando, es un caso de los que podemos mencionar de “declaraciones en expresiones”, seria similar para los que conocemos assembler sera como si yo tipeara solo EAX y se realizara alguna operación sin colocarle delante ninguna instrucción POP, PUSH etc, Vemos que existen ciertos casos de una declaracion, que es solo una expresion que se evalua, y no hay un comando delante como en el caso anterior, si el print delante, igual se muestra el resultado en nuestra ventana. Realmente vemos que se esta ejecutando una acccion determinada sin usar un comando o palabra reservada de python asignada para ello, solo usando una variable, que puede cambiar y tomar diversos nombres. Comparaciones: Otra expresiones que devuelven resultados y no tienen un comando asociado son por ejemplo comparaciones que si las tipeamos nos devolvera si es verdadero o falso.
Alli vemos que la expresion X>0 se evaluo y retorno un resultado (true) sin usar comandos reservados de python , solo al evaluar la expresion. Los casos de llamadas a funciones, llamadas a metodos, tambien son declaraciones en expresiones, las veremos con mas detalle mas delante, lo que queria es instalar la idea que son posibles declaraciones sin usar los comandos reservados de python en ciertos casos como los mencionados anteriormente.
Alli estan en esa tabla los casos que mencionamos, vemos que en todos se usan expresiones, por ejemplo el nombre de una funcion o metodo ha sido creado y no es un comando de python reservado para ejecutar alguna accion, y sin embargo se ejecutara, en el caso de comparaciones ya vimos que son variables que se comparan y en ningun caso se usa un comando reservado de python para ejecutar esa accion son realmente expresiones sueltas que cobran significado según hallamos asignado anteriormente nombres a las variables, metodos o funciones que intervienen. Bueno creo que con esto vimos algunas declaraciones en la proxima parte seguiremos adelante Hasta la parte 18 Ricardo Narvaja
18-APRENDIENDO PYTHON DESDE CERO (declaraciones, while,break, continue, for, input, raw_input ) Seguimos con las declaraciones, en las partes anteriores vimos el print y el if, ahora nos toca el turno de profundizar en el uso del while, el for, y aprender para que sirven el break y el continue y al final la diferencia entre input y raw_input. DECLARACION WHILE: La declaracion while es la forma mas general de loopear en python, lo que hace es repetirse mientras el test que se evalua se mantiene verdadero, una vez que el test pasa a ser falso se termina y sale del mismo, veamos los ejemplos que nos aclaran mejor la situacion:
En este primer ejemplo, testea si el numero uno es verdadero, ya vimos que cualquier valor diferente de cero es verdadero, por lo tanto al no cambiar, nunca saldra del loop, y se mantendra repitiendo el print, hasta que uno se canse y aprete CTRL + C, asi se interrumpe el script y se sale a la fuerza del mismo.
El proximo ejemplo es este:
En este ejemplo la variable x es igual a 'spam', y como eso es verdadero, pues es diferente de la string vacia, entra al while, imprime el valor de x, y luego vuelve a asignar x, quitandole la primera letra, mediante un indice slice, quedando en x la string 'pam', y asi sucesivamente, la proxima vez x sera 'am', luego 'm' y al final sera la string vacia, por lo cual se hara falso el test, terminara el while y saldra. Ejecutemos a ver si es cierto esto, poniendo un input() al final, para que no se cierre cuando termina.
Perfecto, funciono como lo pensamos, el que tiene alguna duda, puede tracearlo e ir viendo como x va tomando los valores de la string 'spam' y se va rebanando la misma por el indice slice, hasta hacerla vacia y salir.
Alli tenemos otro ejemplo, asignamos a la variable a el valor cero y a la variable b el valor diez, el test es que mientras la expresion a < b sea verdadera o sea mientras a sea menor que b, se repetira el while, y dentro del mismo cada vez que loopea, se va incrementando a, de uno en uno, hasta que cuando valga diez, a y b seran iguales y saldra, ejecutemoslo a ver que pasa.
Vemos que fue realmente como dijimos, al valer a menos que diez, lo va imprimiendo, cuando vale diez, ya sale del while por eso no se imprime el valor diez dentro del mismo. Existe la posibilidad tambien de usar un else en conjuncion con el while el formato seria este:
De esta forma mientras el while testea y la expresion es verdadera, se repite el print a y el incremento de a, cuando x valga diez, alli salta al else y termina de loopear, imprimiendo la palabra 'salida'.
Realmente no es muy diferente esta opcion, de la que imprime la palabra salida, luego de terminar el while, seria asi.
Vemos que el resultado es el mismo, pero bueno, existen ambas posibilidades en python y es bueno conocerlas. DECLARACIONES BREAK Y CONTINUE La definicion de break es la siguiente, es un comando que colocado dentro de un loop, cuando se ejecuta te hace salir del mismo en ese mismo instante sin evaular nada, es una saliida forzada. En cambio el comando continue, cuando se ejecuta, te hace saltar al inicio del loop, veamos ejemplos que aclaran facilmente la situacion.
Alli vemos el comando break dentro de un loop, vemos que hay un if dentro del loop, que cuando a tome el valor ocho, directamente ejecuta el break y sale del loop, independientemente que el test sea verdadero o falso, el break rompe al ejecutarse el loop y sale fuera del mismo en forma forzada.
Alli vemos que el break fuerza la salida del loop, siendo mas importante que el mismo test del while, si hay un break cuando se ejecute, saldra del loop, el que quiere puede tracear el mismo y ver que el break interrumpe la ejecucion normal del loop para salir en forma forzada y no se llega a imprimir los valores nueve y diez, porque se sale a la fuerza cuando a vale ocho. En este ejemplo vemos el caso del uso del continue
El continue fuerza la vuelta a la primera linea del loop, por lo tanto, en este ejemplo cuando a tome el valor dos, se ejecutara el continue y volvera al inicio, salteando el print a, no imprimiendo el numero 2. Si lo ejecuto.
Veo que el dos no se imprimio, porque el continue forzo que cuando, a vale dos, se salte forzadamente al inicio, salteando el print y alterando la normal ejecucion del loop, a partir de alli, como a se vuelve a incrementar y se hace tres, ya no se ejecuta mas el continue, y sigue el loop funcionando normalmente hasta que sale al valer diez.
En este ejemplo vemos ambos en conjunto, cuando se ejecuta el continue, se fuerza a saltar al inicio evitando la impresion del 2, y el break hace que salga del loop cuando a vale 8, a la fuerza.
Las flechas me indican como afecto la salida, la flecha verde me muestra que cuando se ejecuto el continue, este evito la impresion del dos, y la flecha roja me muestra que cuando se ejecuto el break forzo la salida anticipada del loop, cuando a valia ocho.
Aquí vemos otro uso del break, el while solo se usa para repetir ya que no tiene la expresion que se testea, la posibilidad de ser falsa para salir, como uno siempre es verdadero, se repetira constantemente y no saldra del loop por causa de la falsedad del test. Vemos que se pide el ingreso de un nombre y se imprime 'Hello' y el nombre ingresado, eso se repetiria eternamente sin salida, salvo que si uno tipea 'stop', entra en el if y se ejecuta el break que sale fuera del loop en forma forzada, imprimiendo 'FINAL' y terminando el programa.
De esta forma el break es usado para forzar la salida de un loop, cuando se cumple la condicion que permite ejecutarlo. Veamos si lo corro.
Se cumplio lo que analizamos, solo pudimos salir del loop, si tipeamos la palabra 'stop', que permitio ejecutar el break y salir en forma forzosa. DECLARACION FOR: Ya hemos visto el formato de la iteracion for, se ejecutara mientras un indice tome todos los valores posibles de un objeto, y saldra solo cuando no haya mas indices posibles de los cuales tomar valores. Tambien en el for, existe la posibilidad de intercalar breaks y continues, como en el while, funcionando de la misma forma, el break forzara la salida en el punto donde se ejecute, y el continue retornara al inicio de la iteracion, cuando se ejecute. Veamos algunos ejemplos que siempre aclaran el punto:
En este ejemplo x tomara todos los posibles valores que posee el objeto que esta luego del in, empezara valiendo “spam”, y se imprimira, luego en la segunda iteracion tomara el valor “eggs” y se imprimira, y la ultima vez tomara el valor “ham” y se imprimira, y como no puede tomar mas valores pues saldra del for y terminara.
Alli vemos que al ejecutarlo se cumple lo previsto, la impresion de x, que tomo todos los posibles valores dentro del objeto.
Otro ejemplo, la variable sum comienza valiendo 150, y la variable x , va tomando los valores 1,2,3 y 4 del objeto que esta luego del in. La primera vez : sum=150 ; x=1 sum =150+1 O sea que la primera vez sum valdra 151, y asi sucesivamente la siguiente vez que entre, sum seguira valiendo 151, x valdra 2 y al sumarse se imprimra 153, etc Ejecutemoslo para ver si es cierto.
Otro ejemplo:
La lista T, esta compuesta de tres items, cada uno de ellos es una tupla, por lo tanto en el for, a y b tomaran los valores de cada uno de los pares de esa tupla, y los imprimira.
Vemos que en ejemplo anterior, insertamos un break, por lo tanto, cuando se ejecuta el mismo, se termina la iteracion a la fuerza.
Vemos que se imprimio solo la pareja 1, 2 al valer a=3, se ejecuta el break y sale en forma forzosa. ALGUNAS VARIANTES DE LOOPS a)RANGE La funcion range nos devuelve un rango de enteros, de la siguiente forma:
Si solo tiene un valor dentro de los parentesis, comenzara en cero y se incrementara de uno en uno hasta el maximo que sera dicho valor, el cual es excluyente o sea que en el primer caso el range (5), nos devolvera los enteros del 0 al 4, y en el segundo al haber inicio y final dentro del parentesis, nos devolvera los enteros entre estos dos valores sin incluir el ultimo, o sea 2, 3 y 4. Esta funcion es muy util para construir loops, ya que nos permite hacer que una variable tome todos los valores enteros que querramos y se incremente de uno en uno.
Aquí vemos que la variable i tomara los valores 0,1 y 2, que nos suministra la funcion range(3), y luego dentro imprime el valor de i y de i+2. Cuando i salga del rango, o sea valga tres saldra del loop. b)ZIP La funcion zip permite tomar varios valores simultaneos a la vez de varias secuencias diferentes para una iteracion o loop, veamos el ejemplo:
Alli la funcion zip devuelve pares de items, tomando uno de cada lista, en este caso, si esto lo aplicamos a un loop.
Vemos que x e y tomaran valores, uno de cada lista, gracias a la funcion zip, y los imprimiran dentro, es una forma de recorrer dos o mas listas o objetos diferentes, en forma transversal, asignando valores de ambos a la vez.
Vemos que zip, puede recorrer cualquier cantidad y tipos de objetos simultaneamente, si uno es mas corto que los otros, zips trunca alli y sale sin aceptar mas valores, solo toma valores mientras encuentra en todos los objetos definidos, items simultaneos, que existan.
En este ejemplo, achique L2 a ver si es cierto que solo tomara hasta el tercer item, ya que el cuarto no existe en una de las listas.
Vemos que asi fue, solo imprimio los tres primeros pares de valores. ZIP puede trabajar con mas y diferentes objetos a la vez, veamos mas ejemplos.
Vemos que es una funcion muy flexible, en este caso el tercer objeto es una string de cuatro caracteres, y igual toma cada carácter como un item y los empareja con los correspondientes de las otras listas, hasta la tercera posicion, pues siempre llega como limite, a tomar la cantidad de valores que tiene el menor de todos, en este caso tres y alli trunca. c)MAP MAP es similar a ZIP, pero realiza un padding de los objetos de menor cantidad de items hasta igualar a todos, en el caso anterior si uso map en vez de zip.
El primer parametro de map indica el padding en este ejemplo usamos None, y vemos que en el cuarto item que la lista L2, no posee, lo rellena con el None, que usa de padding. INPUT y RAW_INPUT Hemos visto que input y raw_input permiten la entrada desde el teclado pero cual es la diferencia entre ambas veamos en el WING.
Si pruebo en la ventana interactiva o python shell, que pasa si tipeo input () y ENTER, veo que si tipeo valores numericos no hay problema en el ejemplo, tipee 67 y ENTER y me lo devolvio, pero cuando tipee la y me dio error y aquí esta la principal diferencia entre ambas, input () sirve para ingresar valores numericos solamente, mientras que raw_input (), me permite ingresar strings y texto, mientras no aprete enter, puedo escribir lo que quiera, lo cual sera devuelto como una string. Si probamos con raw_input ()
Vemos que puedo tipear lo que quiera hasta que aprete la tecla ENTER y que me lo devuelve como una string, asi que hay que saber según lo que necesito ingresar, si es un valor numerico, usar input () y si es una string o texto, usar raw_input () Bueno creo que hemos visto ya todo lo relativo a loops, las declaraciones if, while, for, break, continue,input y raw_input, asi que terminamos con estas declaraciones. Hasta la parte 19 Ricardo Narvaja
19-APRENDIENDO PYTHON DESDE CERO (funciones ) En esta parte empezaremos a estudiar las funciones, que son paquetes de codigo que pueden ser llamados repetidamente con diferentes entradas y salidas cada vez. Realmente ya hemos usado muchas funciones, ahora el tema sera identificarlas correctamente, y empezar a escribir nuestras propias funciones. Muchos conocen las funciones, porque provienen de programar en otros lenguajes, alli son llamadas subrutinas, o procedimientos, pero lo importante mas alla del nombre, es poder organizar un codigo para que realice una determinada accion, a la cual se la puede llamar desde diferentes partes del programa y pasarle diferentes valores, para que realice sus operaciones y me devuelva una salida, muchas veces hay que repetir una operación varias veces y no se puede escribir de nuevo el mismo codigo cada vez que debamos ejecutar la misma accion. Tambien si un programa tiene que ejecutar una accion compleja, es mucho mas claro dividirla en varias tareas simples, para facilidad de entendimiento del mismo, y mayor claridad. Como dice el libro que me inspira, si tengo que poner a un robot a hacer una pizza, mejor que hacer un programa larguisimo que abarque todo la fabricacion de la misma, es mejor hacer programas que tengan bloques o partes, por ejemplo,divdir en partes como amasar, cocinar, poner la muzzarella, poner aceitunas, etc, cosa de que si quiero repetir o hacerle hacer solo una de estas mini tareas, la pueda llamar individualmente, y puedo encadenarlas, repetirlas o cambiarlas de orden, para ir ejecutandolas. Seguramente a medida que vayamos haciendo ejemplos se aclarara la cosa. FUNCIONES – DECLARACION DEF Hemos usando ya funciones que python trae incorporadas como open, para abrir un archivo, o len para determinar el largo de un objeto, realmente si uno usa una funcion incorporada o que uno no hay programado, debe conocer que accion ejecuta, y pasarle los parametros correctos, tanto en tipo, como en cantidad, y la misma se ejecutara y nos devolvera el resultado acorde a la accion realizada, si le paso un argumento equivocado, como una string en el lugar que espera un entero, por ejemplo, nos devolvera error, lo mismo que si espera tres argumentos y le pasamos dos, o cuatro, tiene que respetarse el formato de la funcion y usarse de acuerdo a ello. Hay algunos conceptos que hay que manejar cuando hablamos de funciones. que enumeraremos en los siguientes puntos, antes de pasar a los ejemplos donde se aclararan bien, si alguno no lo entienden mientras lo leen, no se hagan mucho problema, al verlos en los ejemplos se aclara: REGLAS: 1. La declaracion def que se utiliza para definir una funcion, es ejecutable, a diferencia de otros lenguajes como C, aquí la funcion se crea cuando se ejecuta el def, y la funcion no existira, si python no llega a ejecutar el def correspondiente, si por ejemplo ponemos un def dentro de un if, cuya condicion no permite acceder al mismo, pues la funcion no se creara. 2. La declaracion def, crea un objeto funcion y le asigna un nombre, este nombre sera una referencia para el objeto funcion creado, para poder ser llamado desde otras partes del programa. 3. Si la funcion devuelve un objeto mediante return, este se le devolvera al caller o punto de llamada o sea si alguien llama a la funcion, obtendra el resultado que devuelve la funcion. 4. Los argumentos son pasados a una funcion por asignacion, de esta forma no necesariamente tiene que compartir entre el caller y la funcion los mismos nombres de las variables, esto se aclarara mejor cuando veamos ejemplos.
LA DECLARACION DEF Es la declaracion mas utilizada para crear funciones, aunque hay otras que veremos mas adelante, el uso de la misma es el siguiente: def (arg1, arg2, ...etc): codigo a ejecutar o declaraciones return (opcional) O sea que un primer ejemplo que podriamos hacer seria el mas simple.
Vemos que pongo el def de la funcion al inicio, para que se ejecute y la cree, y luego la misma es llamada tres veces, para sumar 9 mas 8 y devolver el resultado en la variable a, luego para sumar 20 mas 40 y devolver el resultado en la variable b, y luego al final suma a y b y lo devuelve en la variable c. Veo aquí que el ejemplo de la pizza, aunque sea una simple suma de numeros, es muy coherente, porque podria ser en vez de una suma, una operación o conjunto de operaciones complicadas, y por medio de la creacion de una funcion, no tendre que repetirlas, si no que llamo la funcion desde diferentes lugares y siempre me devuelve en este ejemplo, el resultado de la suma de los numeros que le pase. Veamos algunas de las reglas, que habiamos mencionado antes para ver aquí en el ejemplo como se aclararan: Deciamos que si el def no se ejecuta, la funcion no se crea, y esto es 100% cierto, vemos que lo primero que hace el programa es crear la funcion, si fuera al reves.
Como en python el programa se ejecuta linea a linea, al llegar a a=sumaNumeros (9,8) Python no sabe que es sumaNumeros, ya que aun no ha sido creada porque el def se ejecuta al final y me dara error, veamos corramos esto.
Y si, el error es logico, no puedo usar una funcion que no fue definida antes, es como si uso una string, la tengo que definir anteriormente de que el programa la empiece a utilizar, aquí es similar, por lo tanto el punto uno de las reglas queda claro, tengo que ejecutar el def que define una funcion, antes de poder llamarla y usarla. El punto 2 de las reglas , tambien este claro en el ejemplo, volvamos al que funciona correctamente.
Deciamos que en el punto dos, que el def creaba un objeto funcion y le asigna un nombre en este caso el nombre es sumaNumeros, y a partir de que esta creado este objeto se puede llamar de diferentes partes de un programa, es este caso es llamado tres veces. El punto tres esta tambien claro en el ejemplo, las variables a b y c llaman a la funcion con diferentes parametros y obtendran el resultado que la misma devuelve a traves del return, en este caso sera la suma de los argumentos que le pasamos. Y el punto 4, que es el mas dificil de entender se explica tambien facilmente, la funcion tiene como parametros num1, y num2, sin embargo cuando se llama a la misma lo que ocurre es que a esas variables se le asignaran los valores que le pasamos como argumentos, o sea en el primer caso a num1 se le asigna nueve y a num2 se le asigna ocho, se realiza la asignacion por medio de la llamada a la funcion, respetando el orden de los argumentos, uno a uno. Vemos que al definir esta funcion, realmente no estamos diciendo especificamente que las variables deban ser de algun tipo especifico, si hago una llamada mas de esta forma.
Veo que el WING al haber definido la funcion, al autocompletar sugiere el nombre de la misma, la
agrega a la lista de funciones existentes.
Vemos que la funcion concatena la strings sin problema, la unica limitacion que tiene, es si realiza dentro de la misma alguna operación prohibida para el tipo de objeto que le pasamos, en este caso, no hay problema pues la suma, concatena las strings y luego se imprime la salida sin error. Ahora que ya vimos un ejemplo y entendimos la reglas de funcionamiento, podemos ir avanzando con ejemplos mas complejos.
Aquí vemos la definicion de una misma funcion en tiempo de ejecucion y según una condicion que en este caso es el valor de la variable entrada, si es menor o mayor que 100, según eso,se define en distinta forma la funcion, ya que el if decidira si entrar a la prrimera definicion o a la segunda. En el estricto caso del ejemplo, como entrada es menor que 100, la definicion de la funcion sera la segunda y los argumentos se restaran si vemos la salida.
Vemos que los argumentos que le pase se restaron como corresponde, lo que si tenemos que
entender que en este caso la definicion de la funcion ocurre cuando se ejecuta el def correspondiente y no antes. En este otro ejemplo:
Vemos que la variable entrada es menor que 80, en ese caso entra a la primera definicion, pero al salir del if, la funcion se vuelve a definir con el mismo nombre, ocurriendo como en el caso de las strings, la ultima vez que se asigna es la que vale, perdiendose la asignacion anterior de la funcion. Es similar a : pepe= 'hola' pepe= 'chau' El valor que tendra la variable pepe sera 'chau', pues fue el ultimo que se ejecuto, en el caso de las funciones sera similar, si la asignamos una vez, si se vuelve a ejecutar una nueva asignacion al mismo nombre, valdra la ultima como vemos por el resultado. Bueno como hemos visto, una funcion consta de una definicion, que debe ser ejecutada para asignarla, y luego las llamadas a la misma que pueden ser realizadas desde cualquier punto del programa.
Veamos mas ejemplos:
Aquí tenemos la definicion de una funcion, que se llama mulVariable, que toma las dos variables que le pasamos y nos devuelve la multiplicacion de las mismas. Por supuesto el def, es lo que primero debo ejecutar cuando corro el programa, luego habra llamadas a esta funcion que obviamente le tienen que pasar dos valores que se puedan multiplicar.
Vemos que se ejecuta sin problemas, ahora si yo guardara este programita en la carpeta de modulos, o en la misma carpeta donde se ejecuta otro script que lo importa, como vimos cuando estudiamos como crear e importar modulos, pongamosle por ejemplo que al moduloque multiplica lo llamo operaciones.py
Ahora apreto el boton NEW para crear otro script que importara a este.
Y por supuesto lo que primero hago es importarlo para poder usar las funciones que trae dentro.
El autocompletado me sugiere los modulos existentes.
Al escribir el modulo operaciones y el punto, me sugiere las variables que usa ese modulo, y las funciones que trae incluidas, como vimos al importar modulos.
Ahora nos faltaria decirle al wing que agregue este script al proyecto
y que lo ponga como archivo principal de debuggeo a este ultimo.
Vemos al debuggear paso a paso con f7, que cuando realizo la importacion del modulo este se ejecutara completo por lo cual las definiciones, que esten dentro del mismo se ejecutaran.
Vemos que al importar, operaciones.py ejecuto el cidigo propio e imprimio tambien la salida, ahora al seguir traceando vuelvo al script y veo que cuando llamo a la funcion, esta realiza la operación que necesito y imprime el resultado de la variable total. Otra forma de realizar la importacion es la siguiente
En este caso no sera necesario poner operaciones.mulVariables , pues como importamos la funcion esta se comporta como una funcion local.
Vemos que el modulo ejecuta tambien las operaciones internas que tiene fuera de las funciones, por eso puedo llamar tambien a sus variables y saber su contenido, tanto a, b y c son accesibles desde el script principal. Asi que ya vimos ejemplos de funciones creadas por nosotros dentro del mismo programa, y funciones creadas por nosotros en un modulo externo al programa, y como puede ser llamada la funcion en ambos casos.
Bueno volvamos a trabajar en operaciones.py, debuggear.
pongamosla como archivo principal para
Ahi esta nuevamente
La variable resultado es una variable local, ya que pertenece a la funcion mulVariables y funciona dentro de ella, asimismo las variables num1 y num2, tambien son variables locales, pues son asignadas por la misma funcion cuando se la llama, en cambio las variables a b y c, no son variables locales, pues estan ubicadas fuera de la funcion y tienen significado fuera de la misma. Todas las variables locales desaparecen cuando se llega al return si coloco un breakpoint aquí.
Veamos que ocurre si fuera de la funcion cuando quiero imprimir el valor de num1, que es una variable local de la funcion...
Ahi vemos que la variable num1, no existe mas, ya que es una variable local que se crea dentro de la funcion y al llegar al return, desaparece.
Bueno creo que con esto ya vemos teniendo una idea de los que es una funcion en la parte siguiente seguiremos el tema funciones que es muy importante, llevandolo a ejemplos mas complejos de a poco. Hasta la parte 20 Ricardo Narvaja
20-APRENDIENDO PYTHON DESDE CERO (funciones-variables locales y globales ) Hemos visto en la parte anterior la idea de funciones, y la de las asignaciones de variables, dentro de una funcion y como esas variable tendran validez mientras estemos dentro de la misma funcion y no fuera de la misma. NIVEL SUPERIOR Cuando en python nos referimos al nivel superior, queremos decir el nivel maximo, o sea al inicio estamos en el nivel superior, mientras no entremos en alguna funcion estaremos en el nivel superior, luego de entrar en las funciones habremos bajado un nivel, y al salir de la misma volveremos al nivel superior.
En el ejemplo se ve bien claro, la parte rosada es el nivel superior de ejecucion, y si entramos a la funcion, estaremos en un nivel mas bajo que es la parte amarilla. VARIABLES GLOBALES Una variable global es una variable creada en el nivel mas alto de la funcion, generalmente al inicio, pero cualquier variable en la parte rosada, cuando se asigne sera una variable global, y funcionara en todo el programa tanto dentro como fuera de las funciones, pues tiene validez universal.
Alli tenemos un ejemplo de variable global, la variable a esta creada al inicio del programa, y la funcion la usa dentro tambien, en la suma. Tengamos en cuenta que una variable global, es tambien local mientras estemos ejecutando en la parte rosada pues se creo alli, asi que es local mientras estemos ejecutando en lo rosado, y por supuesto sera tambien una variable global, pues fue creada en el nivel superior, en cambio las variables creadas en la parte amarilla dentro de la funcion, solo valen alli dentro de la misma, al llegar al return desaparecen, asi que seran locales cuando estemos alli y fuera no existiran. Veamos un ejemplo que nos aclarara las cosas, aquí tenemos un programita con una funcion
llamada func, dos variables globales que se llaman variableglobal y b, pues se crean en el nivel superior.
Cuando estamos parados en la primera instrucción del programa en el nivel superior, aun no se asigno nada, asi que en el stack data, aun no hay variables ni locales ni globales que yo haya asignado.
Las que muestra son variables que el sistema uso para ejecutar el script, pero aun no hay variables de nuestro programa alli, al pasar con f7 la primera instrucción
Vemos que ya ejecutamos la asignacion a variableglobal que deberia valer 500, veamos el stack data.
Alli vemos que se asigno la llamada variableglobal que vale 500, y esta mencionada en ambos tipos de variables, eso es porque estamos ejecutando en el nivel superior, asi que las variables que son creadas en el mismo son globales, y ademas por ahora son locales pues estamos ahora ejecutando en el mismo nivel que se crearon, cuando salgamos del nivel superior, seran solo globales, y ya no locales (sino visitantes jajaja, pues estaran fuera de donde se crearon) Sigamos traceando apreto f7 una vez mas y pasamos por encima del def, que solo asigna la funcion alli, recordemos que el def solo asigna y crea la funcion al ejecutar el mismo, no se entra en la misma, hasta que no se la llama desde algun otro punto del programa.
Llegamos a la siguiente instrucción alli b tomara el valor del resultado que me devuelva pasarle a la funcion el valor uno. Con f7 ahora si entramos dentro de la funcion llamada func y por supuesto salimos del nivel superior.
Vemos que dentro de la funcion, las cosas estan bien claras, Y es una variable creada dentro de la misma y por supuesto sera local y ya se asigno el valor 1 ya que es el argumento de la funcion, la variable Z, aun no se asigno, sera la proxima vez que apretemos f7, alli se creara. Apreto nuevamente f7.
Alli se creo Z, y la funcion uso en la suma la variableglobal, pues como ya dijimos las variables
globales sirven tanto fuera y dentro de las funciones asi que no hay problema, no necesitan definirse nuevamente dentro, aunque como vemos aquí dentro, no estando en el nivel superior, la variableglobal funciona, aunque desaparecio de la lista de locales, porque no fue creada en este nivel mas bajo. Apreto f7 nuevamente
Y al salir al nivel superior nuevamente,vemos que desaparecen todas las variables que habian sido locales dentro de la funcion, ahora Y, Z, ya no existen, en cambio b y variableglobal, si existen pues son globales y al estar nuevamente en el nivel superior, vuelven a ser locales aquí. Por lo tanto una variable global tiene estas caracteristicas; 1)Funciona en cualquier nivel del programa 2)Es local mientras estemos ejecutando en el nivel superior, pues alli fue creada, al salir del nivel superior, sera solo global. Ahora existe alguna forma de declarar una variable dentro de una funcion, que sea global?, jeje si existe.
Aquí vemos el tipico error al tratar de usar una variable local como es Z, de una funcion, fuera de la misma, como sabemos la funcion devuelve el valor de Z, pero la misma desaparece al llegar el return, y si la uso despues del mismo me dara error, porque no esta definida, ahora veamos de esta forma.
Alli vemos un nuevo tipo de declaracion que es la instrucción global, con la misma, las variables creadas en un nivel inferior, seran tomadas como globales y seguiran valiendo fuera, si ponemos un breakpoint en el print.
Al detenerse en el mismo vemos que Z es considerada una variable global, por lo tanto las variables creadas con la declaracion global, son tan globales como si hubieran sido creadas en el nivel superior. De cualquier manera el uso de la declaracion global no es muy aconsejable, porque como es una excepcion a la regla, la persona que no escribio el programa, le costara mas entenderlo, y si las variable globales se crean en el nivel superior, el orden se respeta como se supone que debe ser, sin excepciones que hacen mas dificil interpretar el codigo a los que no lo crearon. Lo mismo que las variables creadas dentro de una funcion son temporales y desaparecen al terminar de ejecutarse la misma, si creamos una funcion dentro de otra, la misma sera temporal y desaparecera al terminar de ejecutarse la funcion, es lo esperado, aunque normalmente siempre las funciones se definen en el nivel superior y son globales, puede darse el caso de funciones temporales creadas dentro de otras y por eso lo mencionamos, si llamanos a esa funcion desde el nivel superior no existira, solo existira dentro de la funcion donde fue creada. Alli vemos este programita muy particular con dos funciones:
por supuesto se define el nombre f1 cuando se ejecuta el primer def, pero no se entra en dicha funcion hasta que no se la llama por eso la f2, no existira hasta ese momento, si lo traceamos veremos claro lo que digo.
Estamos al inicio con ninguna variable nuestra asignada aun, y no se crearon las funciones todavia, si apreto f7.
Por supuesto el def solo crea la funcion f1, y no entra a la funcion solo la define, saltamos a la
siguiente linea que es f1( ) que es la llamada a la funcion f1, que en este caso es una funcion sin argumentos, con f7 entramos en la misma.
Alli se asigna la variable x que valdra 88 que por supuesto es local, y seguimos traceando con f7 y llegamos al otro def, como habiamos visto la funcion f2 aun no existe pues no se habia ejecutado este def aun, asi que al ejecutarlo, por supuesto crea la funcion sin entrar en la misma y salta a la siguiente instrucción fuera de la funcion .
Ahora si va a llamar a f2 que es una funcion sin argumentos, la cual ya ha sido creada anteriormente, dentro de la funcion f1, y como aun estamos dentro de la misma sigue teniendo validez, apretamos f7 y entramos en ella.
Dentro de la funcion f2 se imprimira el valor de x que es 88, luego ya sale de la misma y del programa sin hacer mas nada. Si le agrego una llamada al terminar el programa a la funcion f2, veo que me dara error pues como vimos es una funcion temporal solo definida dentro de f1 y que no tiene validez alli en el nivel superior.
Bueno creo que es muy importante el tema del las funciones y tener bien claro que las mismas cuando se llega a ejecutar el def, solo se crean no se entra en ellas, por eso el wing las saltea, alli el def se usa solo para asignar, por eso no debemos confundirnos cuando traceamos, si llegamos a un def, es como hacer a='pepe' le damos un nombre a la funcion, que luego sera llamada desde otros puntos del programa, eso se ve muy claro alli en este ultimo ejemplo, cada vez que llega al def se crea la funcion correspondiente asignandole el nombre, pero no se entra en ellas hasta que no se las llama especificamente con f1 () y f2() en este ejemplo. Seguiremos aun con el tema funciones hasta que quede claro, pues es muy importante y debe estar perfectamente entendido, para seguir adelante. Hasta la parte 21 Ricardo Narvaja
21-APRENDIENDO PYTHON DESDE CERO (funciones- pasando argumentos-share) Hasta ahora hemos visto que al pasarle argumentos a una funcion estamos realizando realmente una asignacion, en el mismo momento que se llama a la misma, pero aun hay que ver mas ejemplos y detalles que no hemos visto de el tema de pasarle argumentos a una funcion y como afecta al caller, o sea a quien llama a dicha funcion. Cuando le pasamos argumentos a una funcion, este los tomara y los asignara a sus variables locales, veamos el ejemplo mas simple.
Alli vemos en el ejemplo def por supuesto, al ejecutarse solo crea la funcion llamada funct, luego al continuar el programa tenemos un llamado a dicha funcion, o sea lo que llamariamos el caller a la misma, en este caso, vemos las flechas de asignacion, a la variable local x se le asignaria el valor 3 y a la variable y se le asignaria el valor 4, y dentro de la funcion hace la suma z=x+y y devolviendo la funcion el valor de z, el cual se imprime, mediante el print del resultado de la funcion. Vemos aquí, que las asignaciones que se realizan cuando llamamos a la funcion, no afectaran en ningun modo al caller, pues la funcion solo usa variables locales, y devuelve una salida mediante return que se imprimira, el caso es que no se afectan ni cambian variables del caller, veamos otro ejemplo.
Alli vemos al igual que antes la defincion de la funcion, al ser ejecutado al incio el def la creara, luego tenemos la creacion de variables globales, tanto a y b son variables que se crean en el nivel superior, y son variables de alcance global, luego llamamos a la funcion, pasandole los valores de estas a las variables locales x , y, alli se realiza una asignacion como muestran las flechas. A la variable x se le asigna el valor de a A la variable y se le asigna el valor de b Vemos que x se automodifica dentro de la funcion, pero no afecta al valor de a y b, pues todas las modificaciones son locales, la salida en este caso es otra asignacion la variable f se le asignara el valor de la salida de la funcion que es x. Una vez que salimos de la funcion x e y dejaron de existir, pues eran variables locales, solo quedan a, b y f que son variables globales, y como vimos a y b no se afectaron por la llamada a la funcion de ninguna forma, siguen teniendo el mismo valor que antes y f muestra por medio del print la salida. Sigamos con mas ejemplos donde veremos como la cosa puede ir cambiando.
Alli vemos un ejemplo donde la cosa varia, al igual que antes la funcion es creada mediante def, y luego comienzan las asignaciones, la variable a=5, y se crea una lista llamada b =[7,8] con dos items, y se llama a la funcion asignando x el valor de a y a la variable local y se le asigna la lista b. Como la lista es un objeto que puede mutar y automodificarse, dentro de la funcion se cambia el primer item de la misma con y[0]='pepe', quedando el primer item de la misma con dicha string, y al salir se devuelve el valor de la funcion el cual se asigna a f. Hasta aquí todo bien, pero si vemos los resultados de la impresion algo ha cambiado, la lista b, ahora tiene como primer item, el que se asigno localmente dentro de la funcion, como puede ser esto, si y es una variable local, al salir deberia desaparecer y b no haber cambiado. Pero cambia, esta es una propiedad de los objetos mutables como las listas, que cuando se asignan, se shearean entre las referencias a la misma lista, a diferencia de los objetos no mutables como strings, enteros etc. O sea el ejemplo de objetos shareables se puede entender mejor asi:
Sin funcion nin nada , vemos que al solo asignar un objeto mutable, ocurre exactamente lo mismo, al asignar a la variable c la lista b, no solamente se le pasan los valores, si no que ambos quedan apuntando al mismo objeto, cualquiera de los dos tanto b o c, que cambie algun item del mismo afectara al otro, es una propiedad de los objetos mutables que se llama share, y que no solo ocurre dentro de una funcion,si no en cualquier parte del programa, si uno tiene varias referencias al mismo objeto mutable, cualquiera de ellas que cambie un item o varios del mismo, simultaneamente afectara a todas las referencias al mismo, por eso es importante recordar cuales objetos son mutables y tienen esta propiedad de share y cuales no.
Alli vemos la tablita de tipos de objetos que conocemos y vemos que esta propiedad de share solamente se puede utilizar en las listas y en los diccionarios que son los objetos mutables, y tambien, aquí estamos respondiendo a esa pregunta de ¿ porque se crearon las tuplas?, Como vemos la propiedad mutable de las listas, puede ser buena en algunos casos, pero en otros que cualquier referencia a la misma, pueda cambiar el contenido de todas, es un poco descontrolado, y en ese caso, que quiero mantener datos y usarlos, sin cambiarlos, a traves del programa, lo mejor sera usar tuplas, que son inmutables y al querer cambiarse a si misma, ya que no se pueden automodificar, me dara error al tratar de hacerlo, pues no son objetos mutables.
Existe una forma de evitar que se sharee un objeto y es pasando en la asignacion, una copia del mismo, con lo cual evitamos que cambie el objeto principal veamos esto.
En el ultimo ejemplo al asignar c=b[:] lo que estamos haciendo es asignar una copia de b, con lo cual ambas referencias seran a objetos diferentes, y lo que haga c no afectara en nada a la variable b, alli vemos en la salida que solo con ese cambio, lo que c se automodifico, no afectara a b, pues ambos quedaran referenciando objetos diferentes, c modificara a la copia de b, no a b mismo. Y en el otro ejemplo sera igual, si no quiero que lo que la funcion me cambie la lista principal, debere pasarle una copia de la misma asi no hay share.
Como vemos se cumplio lo que queriamos, al asignar una copia de b, lo que y modifico dentro de la funcion, no afecto a b, que a la salida al imprimir vemos que mantiene su valor, los cambios fueron realizados en una copia de b, no en b mismo. Por supuesto si uso una tupla en vez de listas, en ambos ejemplos obtendre un error porque no puedo automodificar una tupla que es inmutable.
Ni dentro ni fuera de la funcion, esta permitido automodificar las tuplas, solo se pueden modificar asignandolas a otra variable, por eso el contenido de la tupla original quedara protegido contra cambios no deseados. Bueno seguiremos profundizando el tema de las funciones, hasta que seamos expertos con ellas, veremos en profundidad cada caso y forma de usarlas con ejemplos, y aprenderemos todo sobre ellas, pues es uno de los pilares sobre los cuales se contruye la programacion en python, la parte siguiente con mas sobre las mismas. Hasta la parte 22 Ricardo Narvaja
22-APRENDIENDO PYTHON DESDE CERO (funciones- pasando argumentos en forma especial) PASANDO ARGUMENTOS POR POSICION El metodo que hemos visto hasta ahora para pasar argumentos, es en el se pasan según el orden que se colocan en el def de la funcion, si alli hay varios argumentos definidos, cada valor que le pasemos al llamar a la funcion se asignara uno a uno, en el mismo orden desde la izquierda a la derecha. . def funcioncita (pepe, pirulo): Alli en el ejemplo repetimos lo ya visto es una funcion de dos argumentos y cuando se la llame . funcioncita (4,5) Se asignaran de izquierda a derecha pepe=4 y pirulo=5, siempre y cuando coincidan en cantidad de argumentos el def y el caller que llama a la funcion, si no dara error.
Alli vemos el def y el caller, correctamente ambos tienen dos argumentos y cuando se llame a la funcion desde el caller y correspondera la asignacion uno a uno de izquierda a derecha esto ya esta claro. Este es el caso por default que ya hemos visto pero hay otras formas de asignar argumentos al pasarle valores a una funcion. PASANDO ARGUMENTOS POR NOMBRE Existe la forma de pasar argumentos por nombre sin importar el orden de los mismos.
Alli vemos que al llamar a la funcion, directamente le decimos a que argumento pasarle cada valor, aquí no importa la posicion, son tres argumentos y a cada uno, le hago la asignacion del valor en el
mismo llamado. Veo que en el ejemplo a coco le asigno cuatro, a pepe le asigno cinco, y a pirulo seis, directamente en el llamado a la funcion, mientras que cubra todos los argumentos y no deje ninguno sin asignar no habra problema python lo aceptara.
Tambien se pueden combinar ambos metodos, aunque suena bastante sofisticado es bueno saber que se puede hacer.
Aunque tengo que cuidarme de asignar uno a uno en el ejemplo anterior, el primer valor que le paso
a la funcion, ira al primero del def que es pepe, y luego el segundo valor del caller es pepe=5 con lo cual cometo un error por multiple asignacion a un mismo argumento.
Ahora si queda correcta, el 1 se asignara a pepe, por ser el primero y luego las otras dos asignaciones, como son por nombre, ya no interesa el orden, a coco se le asignara cinco y a pirulo seis. Muchos se preguntan para que asignar por nombre si ya existe la asignacion por posicion, y a veces en codigos muy complejos, la asignacion por nombre da una mejor idea de lo que estoy pasandole a la funcion, no es lo mismo en el medio de un script larguisimo pasarle a una funcion: func(nombre='Pedro', años=40, trabajo='pintor') que func('Pedro', 40, 'dev') La primera me da una idea mejor, de que es cada valor, entonces muchas veces se usa por ese motivo, aunque no sea necesario, la claridad visual a veces es muy importante en programas complejos. DEFINIENDO VALORES POR DEFAULT En una funcion se pueden definir valores por default, en ese caso si le pasamos un nuevo valor lo tomara y si no usara el valor por default. Alli vemos que por default a la variable coco se le asigna el valor 5 por si no le pasarmos nada quedaria con ese valor, pero al asignarle un nuevo valor, sera coco=4 y no usara el valor por default.
Ahora si no le paso valores a coco, este tomara el valor por default o sea cinco.
Este es el unico caso en el que se le puede pasar menos valores que los argumentos que tiene la funcion, porque si no le paso el ultimo, usara el default, solo puedo dejar de pasarle un valor, a un argumento que tenga valor por dafault, para que la funcion sepa si no le paso valor alguno, cual usar.
Al no pasarle el tercer argumento, usa el definido por default en este caso el cinco OTROS EJEMPLOS DE PASAR CANTIDAD DE ARGUMENTOS ARBITRARIOS Para pasarle a una funcion una cantidad variable de argumentos existen dos formas una con el asterisco(*) y la otra con el doble asterisco (**) Si defino una funcion asi, por ejemplo usando el asterisco simple:
Aquí vemos que en el ejemplo le paso dos argumentos y el asterisco, crea una tupla con esos dos argumentos Si le paso la cantidad de argumentos que sea, creara una tupla con esos argumentos que le pase.
Y si no le paso nada creara una tupla vacia
El doble asterisco es similar pero creara un diccionario con la cantidad de parejas de argumentos pasados.
Vemos que creo un diccionario con las parejas de valores que le pasamos, asi en el ejemplo a sera la primera key, y su valor correspondiente el uno y asi, jeje.
Si combino ambos, poniendo al principio valores sueltos para la tupla y valores en pareja para el diccionario, y combino ambos metodos con el simple asterisco y el doble asterisco hara ambos a la vez creado una tupla y un diccionario de longotud variable, jeje.
Eso si, no puedo mezclar, debo poner primero todos los valores sueltos para la tupla, y luego las parejas de key y valor para el diccionario si las mezclo dara error.
Aca tenemos un ultimo ejemplo y combinamos todas las posibilidades, para ver que ocurre según los diversos casos que vimos. def func(spam, eggs, toast=0, ham=0): # Los dos primeros son obligatorios print (spam, eggs, toast, ham) Es una funcion con cuatro argumentos, pero solo los dos primeros son obligatorios, porque el tercero y cuarto tienen valores por default o sea si les asignamos nuevos valores, los tomaran y si no usaran los valores por default. func(1, 2)
# Output: (1, 2, 0, 0)
Si le paso solo los dos primeros argumentos, la asignacion sera spam= 1, eggs= 2 y los otros dos tomaran los valores por default toast=0 y ham=0. func(1, ham=1, eggs=0)
# Output: (1, 0, 0, 1)
En este caso a la misma funcion le paso estos argumentos, el primero como es por orden, pasa a spam=1, y luego por nombre sera ham=1 y eggs=0 , y quedara toast que como no se asigno tomara su valor por default que es cero. func(spam=1, eggs=0)
# Output: (1, 0, 0, 0)
En este caso le paso solo dos asignaciones por nombre, a spam=1 y eggs=0, mientras que toast y ham quedaran en cero con el valor por default. func(toast=1, eggs=2, spam=3)
# Output: (3, 2, 1, 0)
En este asigno por nombre a toast=1, eggs=2 y spam=3, quedando ham = 0 con su valor por default. func(1, 2, 3, 4)
# Output: (1, 2, 3, 4)
en el ultimo asigno por posicion los 4 valores o sea que por orden spam= 1, eggs= 2, toast= 3 y ham=4 no usando ninguno de los valores por default.
Aquí esta la imagen de este ejemplo ,
Creo que hemos profundizado mas en la forma de pasar argumentos, y como los recibe la funcion, en la parte siguiente seguiremos con otro apasionante capitulo de... jeje bueno el capitulo que viene. Hasta la 23 Ricardo Narvaja
23-APRENDIENDO PYTHON DESDE CERO (funciones- ejemplos) Veremos en esta parte algunos ejemplos un poco mas avanzados de funciones, para practicar y reforzar conocimientos y hacernos duchos en el manejo de las mismas. En este ejemplo, deberemos crear una funcion a la cual le pasemos una arbitraria cantidad de argumentos de cualquier tipo y realice la comparacion entre ellos aun siendo de diferente tipo y halle el minimo, por supuesto debe aceptar tantos argumentos como quieras (inclusive cero argumentos) Por lo tanto abramos el WING y iremos construyendo la funcion. (o copiandola del libro y entendiendola, jeje)
Alli esta, veamos que hace. Vemos que, gracias al asterisco, como vimos en la parte anterior, la variable args ira guardando los argumentos como tupla. Luego inicializa una variable local que se llama res, con el primer argumento, y hace un for en el cual la variable arg tomara todos los valores de la tupla uno a uno y luego comparara el argumento anterior que estaba en res con el siguiente y guardara el minimo de ambos en res, el cual devolvera al salir de la misma con el return. Para probar podemos hacer una llamadita a la funcion
Alli tenemos la llamada a la funcion min1, a la cual le pasamos argumentos y deberia devolver el minimo de todos y imprimirlo.
A la variable res, le pasa el primer argumento de la tupla, que es 3 y entra en el for.
La variable arg, tomara todos los valores de la tupla, menos el primero ya que lo limitamos a que sea for arg in args[1:] o sea que args [1:] limitara usando el indice slice, a tomar todos los valores menos el primero. .
Alli comparara el primero que esta en res, con el siguiente que esta en arg, y el menor de los dos quedara en res para la siguiente pasada, y entonces a medida que se repitan los loops, solo se guardara en res si el argumento actual es menor que el que habia antes guardado, quedando a la salida el minimo de todos los argumentos que se devuelve en el return.
El resultado pues es 1 que el el minimo de lso argumentos que pase, y cumple con lo pedido, veamos que ocurre si le pasamos argumentos mezclados de distintos tipos de objetos.
Vemos que funciona, los numeros son mas chicos que las strings, y el mas pequeño es el 2, compliquemos un poco mas,
Ahora veamos si funciona comparando entre las strings.
Si le pasamos listas como argumentos.
Funciona perfectamente faltaria ver que pasa cuando no le pasamos argumentos.
Bueno me da una excepcion y sale, por lo cual realmente funciona, solo que cuando es cero sale del programa sin dar resultados, podriamos tambien testear si la tupla args es vacia y si lo es salir para evitar el error.
Ahi vemos la funcion mejorada para que no de error y devuelva vacio si no le paso argumentos, coloco al inicio un if que testeara si la tupla en args es vacia, y si asi lo es, no hara nada y saltara al else donde asignara vacio a res y saldra, solo entrara a hacer operaciones si hay argumentos.
Al pasarle vacio sale directamente, al testear que la tupla es vacia, y le adjudica a res = ()
Aca vemos otra funcion diferente, que hace el mismo trabajo, pero asigna las variables de otra forma, en este caso, directamente la primera que le pasamos se asigna por posicion a la variable first, y el resto se asignan a la tupla rest gracias al asterisco, vemos que es muy parecido al ejemplo anterior, solo que en este caso ya tenemos en una variable la primera entrada, y el resto en una tupla. En el for directamente arg tomara cada uno de los valores de la tupla y los comparara con el primero si alguno es menor, lo guardara a este en first, obteniendo alli el menor de todos y saliendo. No necesitamos debuggear este es muy parecido al anterior veamos algunos ejemplos al pasarle diferentes argumentos.
Si le paso solo strings.
Vemos que funciona igual que la anterior solo quedaria el caso de cuando no le pasamos argumentos.
Vemos que aquí tendremos un error, pues al menos un argumento le tenemos que pasar ya que el primero se asigna por posicion y es obligatorio, la tupla no hay problema, alli podemos pasar la cantidad que queramos o ninguno gracias al asterisco, pero el primero es mandatorio.
Vemos que al colocarle el valor por default vacio al primer argumento, funcionara perfectamente y nos devolvera vacio si no hay argumentos y si le pasamos otros argumentos.
Siempre nos devuelve el minimo, vemos que al colocarle el valor por default, evitamos la obligatoriedad de ese argumento y solucionamos el problema del error cuando no le pasamos argumentos. Este tercer caso de funcion es tambien para realizar el mismo trabajo:
En este caso, le pasamos los argumentos que queramos en forma de tupla mediante el asterisco, y luego hacemos una lista de esa tupla y la asignamos a la variable tmp, que como tiene una lista, la cual es mutable, la podemos ordenar mediante el metodo sort() . Por lo tanto con sort () ordenamos la lista y luego retornamos el item del indice mas bajo que sera el menor de todos, veamos si funciona.
Funciona perfectamente pero ya nos damos cuenta que tendremos problemas cuando no le pasemos argumentos a la funcion.
Alli el error se produce, porque al querer tomar el primer item de la lista, como no hay ningun item dara error, podemos repararlo tambien chequeando si tmp es vacio.
Alli salvamos el error, antes de asignar chequeamos si tmp es diferente de vacio, solo asi asignaremos el primer valor de la lista , si no salimos directamente y obtendremos None. Si no le paso argumentos, saldra imprimiendo None, y sin error
Bueno hemos visto tres funciones que relizan el mismo trabajo, hallar el minimo de los argumentos que le pasemos, sea cual sea el tipo de objetos de los mismos, y evitamos el error cuando no le pasamos argumentos, hemos cumplido con la funcion pedida. Hasta la parte 24 donde continuaremos con mas python Ricardo Narvaja
24-APRENDIENDO PYTHON DESDE CERO (funciones -lambda) LAMBDA Hemos visto en las partes anteriores como crear funciones mediante def y luego ser llamadas por un caller desde cualquier punto del programa, esta es la forma general de escribir funciones, pero python provee un comando que nos permitira escribir pequeñas funciones, solo en una linea y devolvera un resultado directamente. El comando es LAMBDA y aquí vemos la diferencia entre la misma funcion creada con def y con lambda. >>> def func(x, y, z): return x + y + z ... >>> func(2, 3, 4) 9 Si creamos la funcion con def ya vimos que necesitamos la definicion de la misma y el llamado a ella, con lambda seria de esta forma. >>> f = lambda x, y, z: x + y + z >>> f(2, 3, 4) 9 Parecen similares pero no lo son si vemos con detalle, lambda no provee el nombre de la funcion ya que al ejecutarla retornara un valor que se podra asignar a una variable o no, como en este caso f, mientras que def, explicita el nombre de la funcion y solo devuelve valores cuando la llamamos desde el caller, si dentro hemos colocado un return. Por lo demas vemos que def usa lo marcado en amarillo en el ejemplo, que son dos comandos (def y return), mientras que lambda solo usa uno. Ademas lambda esta limitada solo a una linea, por lo cual las funciones complejas quedan reservadas para def, no pudiendose hacer con lambda, vemos que en el caso del ejemplo anterior dicha funcion es ideal para el formato lambda ya que solo posee una operación entre los argumentos, lo cual la hace ideal para usarlo. Por lo tanto python limita a lambda a pequeñas funciones, y def a cualquiera, pero en especial las funciones mas complejas. Tambien en lambda tenemos la asignacion por default, vemos el ejemplo. x = (lambda a="PEPE ", b="GANO ", c="DINERO": a + b + c) si la llamamos sin argumentos como no hay ningun obligatorio, devolvera print x()
Ahora si le paso un argumento dejara usar el default y usara el que le pase
Vemos que ocurre, lo mismo que en el def, existe la asignacion por posicion en este caso hara que el unico argumento, sea asignado a la primera variable o sea la a, las otras dos variables b y c tomaran los valores por default por eso imprimira PIRULO GANO DINERO. Tambien existe la asignacion por nombre si le pasamos.
Vemos que en este caso le pasamos por nombre a la variable b el valor PERDIO, lo cual hace que a la salida nos muestre PEPE PERDIO DINERO, las otras dos variables a y c, tomaran los valores por default. PORQUE EXISTE LAMBDA? Como vemos lamba es util para la creacion de pequeñas funciones las cuales pueden ser creadas y llamadas facilmente, aunque su uso es realmente opcional, pudiendose utilizar def siempre, y es solo un tema de facilitar una comodidad para los casos de funciones simples. Lambda tambien es utilizado en jump tables, o sea casos en donde debemos realizar una accion dependiendo de la entrada. L = [(lambda x: x**2), (lambda x: x**3), (lambda x: x**4)] for f in L: print f(2) print L[0](3)
# Prints 4, 8, 16 # Prints 9
En el ejemplo anterior vemos que se utiliza lambda dentro de una lista, en cada item se define una funcion, por lo tanto si hago un for como en el ejemplo donde f tome los valores de todos los items de la lista, al pasarle f(x) nos devolvera el resultado de cada funcion embebida dentro de cada item, en forma de lista. En el ejemplo anterior f(2), como f toma todos los valores de la lista sera: [ f (lambda x: x**2), f(lambda x: x**3),f (lambda x: x**4) ] y como x tomara el valor 2 se resolvera cada funcion con ese valor imprimiendo 4 8 16
y en el segundo print print L[0](3) lo que hara ya que tiene dos subindices, en el primero elijira el numero de item de la lista o sea el cero que es: (lambda x: x**2) y luego el segundo subindice le pasa el valor 3 a la funcion por lo cual devuelve 9. Como vemos lambda nos provee una forma tambien sencilla de embeber una funcion pequeña dentro de operaciones, listas y diccionarios, ya que al ser de sintaxis simple y siempre devolver un resultado, se puede asignar a cualquier variable, y ubicar practicamente en cualquier lugar lo cual a veces es dificultoso con def, el ejemplo anterior es un caso testigo, meter funciones con def dentro de una lista puede ser engorroso, mientras que lambda lo hace sencillo al ser una funcion de una sola linea, y que devuelve siempre un valor.
Aquí vemos otro lindo ejemplo, b es un diccionario, pero no puede obtener el resultado de las funciones, y no puede imprimir un resultado asi como esta escrita, si la corremos nos imprimira el diccionario con las tres funciones sin resolver. {'got': , 'already': , 'one': } Si le paso una key veamos que ocurre.
El resultado muestra lo logico, con una key= 'got', elegimos un valor asociado que es una funcion lambda, pero para resolverla hay que pasarle los argumentos en este caso no tiene, pero si es una funcion debera pasarsele () para que la resuelva sin argumentos.
Ahora si al llamar a la funcion () sin argumentos, la resolvera y devolvera el resultado de la misma que es ocho Otra forma de realizar lo mismo es:
Movemos alli el [key] () y de esta forma definimos que la clave que usara el diccionario estara definida por la variable key y que esta llamara a a una funcion sin argumentos, de esta forma b siempre tendra el resultado de la funcion, la cual cambiara el resultado según sea el valor que le coloquemos a la variable key que es la que maneja todo, si a key, la cambiamos y le pasamos 'one'.
En este caso la key llamara al tercer campo, mediante 'one' y resolvera la funcion lambda que hay alli gracias a que la clave del diccionario esta definida al final del mismo como [key] (). def f1( ): ... def f2( ): ... def f3( ): ... ...
key = ... {'already': f1, 'got': f2, 'one': f3}[key]( ) Vemos que hacerlo con def es posible definiendo las funciones y usandolas dentro del diccionario, pero se ve que lambda es mas comoda en una sola linea se resuelve todo y se ve el contenido de las funciones ahi mismo, mientras que en un programa el def puede estar bastante lejos de donde se ejecuta y es un poco mas incomodo. key = 'one' b={'already': (lambda: 2 + 2),'got':(lambda: 2 * 4),'one':(lambda: 2 ** 6)}[key]() Vemos que al utilizar lambda no solo es mas breve, si no que de un golpe de vista sabemos que hace cada funcion, mientras que en la que utilizamos def, deberemos ir a buscar en algun lugar donde esten definidas las funciones que hace cada una, creo que la diferencia es bien clara y esta a la vista. Vemos que por un lado lambda se limita solo a una expresion, lo cual hace que no siempre se pueda utilizar en funciones complejas, pero ese mismo hecho permite embeber las funciones lambda en cualquier lugar veamos mas ejemplos de funciones lambda embebidas.
Aquí vemos una funcion lambda embebida dentro de un def, de esta forma cuando llame la funcion del def con un caller, lo que ocurrira que me devolvera otra funcion lambda donde el parametro x de la misma, sera reemplazado por el argumento de la funcion action, veamos el ejemplo, si la llamo a action con: action (5) ya que tiene un solo argumento
Vemos que en s, me quedara una funcion lambda que sera según el argumento que le hemos pasado s= lambda y: 5 + y ya que el cinco de la funcion action sera asignado a la variable x. Ahora si le paso s(4) resolvera la funcion lambda tomando 4 como argumento y reemplazando y por cuatro.
Alli esta el llamado a la funcion lambda devuelve 9, al resolverla, vemos que lambda es muy facil de embeber dentro de cualquier cosa practicamente jeje.
Bueno creo que ya quedo claro el funcionamiento de lambda. Hasta la parte 25 Ricardo Narvaja
25-APRENDIENDO PYTHON DESDE CERO (funciones avanzadas- apply-map-filterreduce) Existen otras formas de pasar argumentos, veremos la funcion incluida en python llamada apply que sirve para eso veamos como funciona tanto en el caso de def como lambda. APPLY Veamos como funciona con unos ejemplos sencillos.
Alli vemos que apply tiene dos argumentos, el primero es el nombre de la funcion al cual le enviara los argumentos y el segundo es una tupla con los argumentos a pasar, el hecho de que sea una tupla y que sea automodificable, es util pues puede ser creada en el transcurso del programa, y de esta forma la pasamos como argumento a la funcion. Utilizando lambda es similar.
En ambos casos podremos pasarle los argumentos como tupla, gracias a la funcion apply.
Aquí vemos que el programa construyo una tupla y la asigno a la variable args, la cual se pasa directamente como argumento de la funcion.
Vemos el mismo ejemplo, en el caso tradicional, si hubieramos llamado a la funcion con func (args), a pesar de ser una tupla de 3 items, en forma directa, python reconoce que es una sola variable, y al realizar la asignacion por posicion, le dara al primer objeto, la variable args completa y los otros dos argumentos y, z que son obligatorios, al no tener valores por default, me dara error al no pasarselos. Tenemos mas ejemplos y usos de apply def echo(*args, **kwargs): print args, kwargs Aquí la definicion de una funcion llamada echo, que construye tuplas ilimitadas y diccionarios tambien ilimitados de los argumentos que le pasemos como vimos en las partes anteriores según el uso del asterisco y doble asterisco en cada caso. Si llamo la funcion con estos argumentos: echo(1, 2, a=3, b=4) Por lo que estudiamos anteriormente hara una tupla con los argumentos 1 y 2, y luego las parejas a=3 y b=4 conformaran un diccionario.
Vemos que el resultado es como pensamos, imprime la tupla y el diccionario a la salida, y como usamos apply aquí?
La funcion apply permite el paso de un tercer argumento opcional que es un diccionario, podemos hacer uso de eso, para ingresar el mismo, esto nos permite tener construidas tanto la tupla como el diccionario en tiempo de ejecucion del programa, y antes de ser llamada la funcion. El uso de apply es opcional y puede siempre ser reemplazado por: >>> apply(func, args)
# apply con tupla
>>> func(*args)
# Sintaxis que funciona como apply
Ambas son similares y es bueno conocer el funcionamiento, por si aparece en algun programa, saber realmente que hace, vemos que utilizando asterisco y doble asterisco, se puede reemplazar facilmente apply y sus tres parametros.
apply(func, args, args2)
# apply con tupla y diccionario
func(*args, **args2)
#Sintaxis que funciona como apply
Son similares tambien, usandose preferentemente la ultima, aunque apply permite elegir enre diferentes funciones pasadas como argumento, realmente solo para esos casos, queda reservado el uso de apply frente a la sintaxis que usa asteriscos, que es mucho mas utilizada. MAPEANDO FUNCIONES SOBRE SECUENCIAS- MAP Una de las mas comunes tareas realizadas en python, es aplicar una funcion a cada item de una secuencia, por ejemplo es muy comun lo siguiente:
counters = [1, 2, 3, 4] updated = [ ] for x in counters: updated.append(x + 10)
# Agrega 10 a cada item.
print updated [11, 12, 13, 14] Este ejemplito toma la lista que asignamos a counters y en el for, a cada item le suma 10 y lo va asignando mediante append a la variable updated, creando una nueva lista. Si definimos la operación que debe realizarsele a cada item, por ejemplo en el caso anterior sumarle 10 a cada uno de ellos.
Alli definimos la funcion que se le aplicara a cada item, se llamara inc y sumara 10 y devolvera el resultado mediante return. Lo que hace map, es aplicarle la funcion a cada item de una secuencia y devolvera una secuencia similar con la funcion aplicada, alli vemos que el resultado se asigna a la variable a, la cual tiene cada item 10 mas grande que el original. Vemos que la funcion map nos ahorra el trabajo de construir el for para aplicar la funcion a cada item y apendearlo a una nueva lista. Como el primer argumento de map, es una funcion, alli se puede embeber una funcion lambda. map((lambda x: x + 3), counters) En este ejemplo
Vemos que la funcion lambda pasada como primer argumento a map, le sumara 3 a cada item de counters y lo devolvera en la variable a. Existen muchas funciones que python trae incluidas para facilitar el trabajo , ya vimos apply y map, pero hay mas a continuacion veremos filter. FILTER La funcion que trae incluida python llamada filter, lo que hace es evitarnos tambien un loop, filtrando mediante una funcion, los items de una secuencia. .filter (funcion, secuencia) Veamos un ejemplo que siempre aclara las cosas, para reemplazar este loop que compara cada item de una tupla y ve si son mayores que cero y si es verdadero los apendea .
Vemos que la funcion incluida filter nos ayuda, el primer argumento es la funcion filtro que debe ser verdadera para los items que quiero que formen parte de la tupla o secuencia final, y el segundo argumento es la secuencia a filtrar.
Vemos que la secuencia de entrada es una tupla asignada a la variable T, a la misma aplicandole filter, se limitaran los items, la tupla de salida, tendra solamente los que correspondan a que la funcion sea verdadera, en este caso x>0. Si hacemos que el filtro sea x>25 por ejemplo
Vemos que solo el 75 pasa el filtro que se le aplica mediante la funcion, obviamente si quisieramos hacer esto mismo sin la funcion filter el loop es bastante mas molesto para armar, aquí solo pasandole la funcion fltro y la secuencia ya lo tendremos funcionando. REDUCE Otra funcion incluida es reduce se utiliza para casos en los que se loopea de la siguiente forma.
En este ejemplo toma el primer item y lo suma al segundo y el resultado lo suma al tercero y el resultado al cuarto y asi, la funcion reduce realiza lo mismo sin tener que crear un loop.
El comportamiento de reduce es simple, toma cada par de valores en orden de la secuencia y le aplica la funcion que definimos, en este caso, suma ambos primeros pares de valores y los guarda, y luego a esa suma , le aplicara la funcion con el tercero y la guardara y asi sucesivamente, realmente realiza lo mismo que el loop anterior.
En este ejemplo tomara 1, lo multiplicara con 4 y a ese resultado le multiplicara 9 y a ese resultado le multiplicara -3 y por supuesto dara 108 negativo. 1 * 4= 4 4 * 9= 36 36 * (-3)= -108
Bueno creo que con esto es suficiente por ahora seguiremos en la parte 26 con mas sobre funciones, ya estamos terminando el tema. Hasta la parte 26 Ricardo Narvaja
26-APRENDIENDO PYTHON DESDE CERO (ultima parte de funciones -yield-iter) Bueno ya casi estamos terminando el tema funciones, y no quise que sea un pantallazo asi nomas, sino que sea lo mas profundo que me permite mis pocos conocimientos del tema, quizas no usemos todo lo que vimos en estas ultimas partes completamente o quizas no lo usemos nunca, pero es bueno haberlo leido aunque sea una vez y recordar que si veo un map o un reduce, lo lei en el curso, y bien puedo consultar el mismo o a veces con la ayuda del wing me acordare como funcionaban. GENERADORES – DECLARACION YIELD Se denomina generadores a las funciones que en lugar de return para devolver un valor, utilizan la declaracion YIELD, y que las diferencia, muy simple, si llegamos a un return la funcion devuelve el valor y termina, en cambio yield, devuelve un valor y pausa la funcion, de forma que la vez siguiente que se la llame, continuara desde el punto que quedo, es como una pausa.
Alli tenemos una funcioncita con varios yields , vemos que nos devuelve un objeto de tipo generador y no podemos ver el valor, para poder llamar a los resultados de un generador debemos usar el metodo next () que actua como el play de los CD, arranca y resume, todo en un solo comando veamos.
Vemos que el al llamar a la funcion generador, el primer next ejecuta hasta el yield, devolviendo el valor de la variable solucion hasta ese momento, que es 10 y se pausa, vuelvo a despausar a la funcion con next y se continuara ejecutando desde donde quedo y devolvera el segundo resultado y asi sucesivamente yield devolvera un valor y pausara la funcion y next la despausara y continuara desde donde quedo.
Alli lo vemos claro, el primer a.next() ejecuta hasta el primer yield la parte rosada, y devuelve el primer valor 10, el segundo a.next () ejecuta la segunda parte de la funcion, como si esta hubiera
quedado pausada alli y el siguiente a.next() le quita la pausa, y devuelve el valor 14 en el segundo yield, y el tercer a.next() despausa por ultima vez a la funcion devolviendo el ultimo valor 15. por supuesto uno podria hacer un for que imprima todas las partes del generador una a una.
Lo que hacemos con el for es llamar a la funcion varias veces, ya que la variable i, tomara todos los valores posibles del generador, entonces llamara las veces que el generador tenga definido internamente cuantas veces retornara, en el ejemplo, cada vez que llama a la funcion, imprime el resultado que devuelve el yield, y esta queda pausada, la siguiente vez que la llama el for, se despausa y continua desde donde habia quedado, y asi hasta que sale finalmente la ultima vez. Realmente no es una declaracion muy usada, pero a nivel informativo es bueno conocerla, o aunque no la conozcamos, saber que si algun dia la encontramos podemos venir y mirar aquí lo que es, y como trabaja. ITERACIONES -ITER El metodo iter funciona en forma parecida a lo que vimos con yield, pero sin funcion, sino, aplicado a listas, tuplas, diccionarios o cualquier secuencia que usemos. Si a una secuencia como en el ejemplo siguiente una tupla, le aplicamos el metodo iter, veo que me devuelve un objeto con nombre raro, tupleiterator jejeje, significa que para ver los resultados de la misma debo usar el comando next(), el cual le dara play en cada item y al devolver el mismo el metodo iter lo pausara. Realmente el conjunto iter – next funciona en forma similar al conjunto yield - next del ejemplo anterior solo que aplicado a secuencias. En este caso el iter es el que pausa al devolver cada item y el next es el que rranca o despausa el mismo.
Veamos que ocurre.
Vemos que al igual que con los yields, aquí iter devuelve el primer item y queda pausado, si vuelvo a llamar con next se despausara y continuara con el segundo.
Alli vemos los tres resultados o sea que iter, trabaja en secuencias devolviendo un item y pausando, y el comando para despausar y saltar al siguiente item sera next, que actuara como un play. Y bueno terminamos con el tema funciones, quedaba solo este pedacito, ahora vienen los ejercicios en los cuales practicaremos lo que hemos visto, y podremos pasar al siguiente tema con tranquilidad de haber hecho un paneo por todo el tema funciones. Hasta la parte siguiente y por supuesto tendremos los ejercicios para practicar sobre el tema funciones, que estaran tambien en la web mañana. Ricardo Narvaja
27-APRENDIENDO PYTHON DESDE CERO (Modulos) Bueno para terminar con los conocimientos basicos que nos permitiran comenzar a programar en forma completa, nos faltan profundizar en el conocimiento de los MODULOS que ya hemos visto a vuelo de pajaro y luego el ultimo punto son las CLASES. Luego de haber visto a fondo estos dos puntos, comenzaremos una nueva etapa en el curso de python que nos llevara a programar en forma avanzada. Como ya vimos los modulos son paquetes que nos sirven para utilizar en nuestros programas, y de esta forma contar con herramientas preprogramadas por nosotros mismos, o por otras personas, que podremos agregarlas a python para evitar la repeticion y utilizar los modulos existentes o creados ad hoc. Como recordaremos las declaraciones: .import Con la declaracion import traemos el modulo completo, para ser utilizado en nuestro programa o script. .from Nos permite importar solo un nombre en particular del modulo (puede ser una funcion, un objeto, etc) y la funcion predefinida .reload () Nos provee una forma de recargar un modulo, cuando deseemos, sin detener python. Por supuesto sabemos que si tenemos un script A que importa todo el contenido de otro B y este a su vez importa todo el contenido de otro C , podremos acceder automaticamente desde A al contenido de C. A importa B importa C
..... implica que A puede acceder al contenido de C
LA IMPORTACION MAS A FONDO La importacion esta compuesta de tres acciones para completarla. 1)BUSCAR EL ARCHIVO DEL MODULO 2)COMPILARLO 3)CORRERLO PARA QUE CREE LOS OBJETOS, EJECUTANDO SUS PROPIOS DEF Y ASIGNACIONES.
1)BUSCAR EL ARCHIVO DEL MODULO Vimos en las partes anteriores que cuando importamos python no nos pide colocar el path y la extension del archivo a importar, el mismo presupone que tiene que ser un archivo de extension py o pyc como veremos mas adelante, por lo tanto no tenemos que agregarselo, por lo demas el path tampoco es necesario ya que python buscara en determinados lugares, para poder encontrar el
modulo. El orden en que python buscara el modulo a importar es el siguiente:
LA CARPETA DONDE SE EJECUTA EL ARCHIVO PRINCIPAL O HOME DIRECTORY PYTHONPATH (si esta seteado) CARPETA DE LIBRERIAS LO QUE NOS INDIQUE UN ARCHIVO DE EXTENSION .PHT
Como vemos lo primero que hace python es buscar en la carpeta donde se encuentra el script que se lanzo primero, o script principal, normalmente es la carpeta de trabajo del programa Si alli no lo encuentra, pasa a la fase dos que es buscar en el pythonpath, que es una environmentvariable con este nombre que se puede configurar para que cualquier programa de python busque alli, aunque por defecto no se crea. La tercera opcion es buscar en la carpeta lib del directorio de instalacion de python, si tenemos instalado python en c:\Python24 dentro estara la carpeta lib c:\Python24\Lib\ donde buscara cualquier modulo para importar como tercera opcion. La cuarta opcion es que uno cree un archivo de texto con la extension .pht , que tendra en cada linea un path valido donde buscara modulos, un archivo de estos colocado en algun directorio que acceda el programa, multiplicara la busqueda a traves de mas directorios. De cualquier manera lo mejor es no complicarse la vida y colocar los modulos a trabajar en la misma carpeta donde se ejecutara el programa y si es un modulo generico, copiarlo a la carpeta lib de python, porque a veces el efecto indeseado es que termina ejecutando un modulo que no es el que uno quiere, si tenes dos modulos que se llaman igual, en distintos lugares donde accede python a buscar.
2)COMPILARLO Una vez que encuentra el modulo a importar, python lo compilara byte a byte y creara un archivo con el mismo nombre del modulo original, pero de extension .pyc en la carpeta del programa, Esto lo realizara siempre y cuando no haya un pyc del mismo nombre ya compilado, en la carpeta del programa, si ya hay uno verificara que sea similar y no haya cambios y saltea este paso, solo lo recompilara cuando encuentre cambios entre el codigo del pyc de la carpeta del programa y el modulo original. Hay que mencionar que la compilacion se realiza cuando se ejecuta el import y no antes, por lo tanto podemos ver en la carpeta del programa los pyc correspondientes a las importaciones que va realizando a medida que se van ejecutando los imports correspondientes. El script principal, no se compila si no que se ejecuta directamente, por eso no encontraremos pyc de el en la carpeta principal.
3)CORRERLO PARA QUE CREE LOS OBJETOS EJECUTANDO SUS PROPIOS DEF Y ASIGNACIONES El ultimo paso es ejecutar el codigo del modulo importado, de esta forma se ejecutaran los def con lo cual se crearan las funciones, se realizaran las asignaciones, y si el modulo tiene codigo propio que ejecutar, lo hara y en el momento de importar veremos el resultado de la ejecucion del mismo. Por ejemplo si alguna asignacion del modulo importado, depende de una funcion creada alli, y el mismo modulo llama a la funcion, para hallar un resultado y este lo asigna a una variable, se realizara todo este trabajo en el momento de la importacion, uno tiene que tener en cuenta que el modulo importado correra en ese momento como un programa mas y cuando termine volvera a continuar con la importacion del modulo siguiente o la ejecucion del programa principal. Como vemos importar implica un trabajo y tiempo, por eso, la importacion se realiza solo una vez, y cuando ya ha sido realizada alguna vez, si la volvemos a hacer y no ha habido cambios, python usara el pyc que ya ha creado antes, salteando los pasos 1 y 2 para ahorrar tiempo.
MODULOS EN ACCION Sabemos como crear un modulo, ya lo hemos visto en capitulos anteriores, tenemos que observar que como los modulos son importados y sus nombres se utilizaran dentro del programa, por lo tanto para la creacion de nombres de modulos, correran las mismas reglas que para las variables, no se podran usar modulos que se llamen if o else jeje, bueno ninguna palabra reservada. Una vez creado un modulo, al ejecutar un import o un from, como hemos visto se buscara, luego si no se ha compilado anteriormente o si tiene cambios se compilara y luego se ejecutara. Comencemos con los ejemplos borramos todos los proyectos anteriores, y creamos una nuevo.
Le ponemos un nombre por ejemplo module1.py y lo guardamos con SAVE AS.
Bueno este modulo lo usaremos para importar desde otro, asi que lo guardamos en una carpeta donde estara el principal, por ejemplo puedo poner ambos en el escritorio, asi no hay problema y lo hallara, para no meter tanta cosa en la carpeta lib, que esto es solo para practicar.
Alli vemos la funcion printer que esta definida dentro de este modulo, guardamos los cambios que realizamos.
Ahora apretando NEW creamos un segundo script en la misma carpeta que el anterior, y le ponemos como nombre principal.py, este sera el modulo principal por lo tanto lo agregaremos en la pestaña proyectos, y lo colocaremos como debug file, o se que si lanzamos el debugger, comenzara debuggeando por este script.
Agregamos el archivo y luego volvemos a hacer click derecho SET AS DEBUG MAIN FILE.
Y quedara marcada en rojo como el script para debuggear inicial. En este segundo script importaremos el module1.py ya vimos que no es necesario poner el path asi que colocaremos import module1.
Ahora llamaremos a la funcion printer de module1.
Vemos que al tipear module1 y el punto, WING nos sugiere lo que podemos utilizar de ese modulo, en este caso printer, si apreto TAB lo completara.
Lo que estamos haciendo es llamar a la funcion printer, lo mismo hemos hecho en las partes anteriores, la mayor parte de las veces llamando funciones ubicadas en el mismo script donde estaban definidas, ahora importamos un modulo con funciones, y las llamamos desde otro script.
Vemos que el efecto es el mismo que si hubieramos credao la funcion printer, aquí en el mismo script principal
Vemos que lo que al importar module1.py crea en la carpeta del programa, el archivo compilado module1.pyc mientras que el script principal no es compilado, se ejecuta directamente y no tiene pyc. Contrastando con el ejemplo anterior, si usamos from para importar solo la funcion printer quedara de esta forma.
Vemos que cuando importamos una funcion sola, con from, no necesitamos mencionar el modulo a que pertenece, directamente se puede utilizar su nombre, sin la referencia a que modulo corresponde, porque eso esta implicito en el from, python mirara esa linea from module1 import printer y vera que printer corresponde a module1, no necesitando mas nada para identificar la funcion. Si utilizamos el metodo from y asterisco, para que devuelva todas las variables y funciones, tampoco necesitara la referencia, python se arregla para determinar siempre que usemos from, a que pertenece cada cosa.
IMPORTS SE EJECUTAN SOLO LA PRIMERA VEZ Una de las dudas que se prsentan generalmente, es que cuando hago import por primera, vez ejecuto el modulo importado, pero si lo vuelvo a importar ya no ejecuta nada, sus variables y funciones continuan pudiendo ser usadas, pero no se vuelve a ejecutar, por lo tanto si necesito ejecutarlo nuevamente necesitare usar la funcion reload (). Veamos si modificamos apenas el ejemplo
Dentro del module1, le agrego una linea mas, fuera de la funcion, para que cuando se ejecute al importarse, imprima la string EJECUTANDOSE y guardo los cambios.
Ahora vuelvo al script principal y pongo un breakpoint en la llamada a la funcion printer.
Si le damos a DEBUG vemos que apenas el modulo se importa, ya imprime la string EJECUTANDOSE como vimos, al importar se corre el modulo, y al correrlo se imprimira la string, luego cuando se llama a la funcion printer, imprimira el HOLA MUNDO!!! y nada mas, pues solo ejecutara la funcion llamada.
Si agregamos una segunda importacion del mismo modulo, y debuggeamos, vemos que solo imprime EJECUTANDOSE en el primer import, la segunda vez no lo imprime, pues como ya esta importado no se vuelve a correr.
Y llegando al final del script solo habra un EJECUTANDOSE que corresponde a la primera importacion.
El segundo import no ejecuto el modulo nuevamente.
Vemos que para lograr ejecutarlo por segunda vez necesito la funcion reload (), alli volvera a ejecutar el modulo perfectamente, y saldra nuevamente el cartel EJECUTANDOSE, esto lo hemos visto al inicio del curso a vuelo de pajaro, pero es bueno afianzar los conocimientos.
Vemos que para hacer reload el modulo debera haber estado importado antes en forma completa con import, la forma from no permite reload del modulo. Como los defs, los import y from tambien son ejecutables y solo funcionan si se ejecutan dentro de un script, el hecho de que haya un import en algun lado de un script, no significa que ya esta el modulo importado, tiene que ser alcanzado ese import y ejecutado, si esta dentro de una condicion que no se cumple, por ejemplo y no se alcanza a ejecutar el import, el modulo no se importara.
Alli vemos una importacion realizada dentro de un if, que no se cumple al ser falsa la condicion y sigue el programa sin importar module1, cuando llega al printer, como no fue importado, dara error, pues no se ejecuto nunca el from que lo deberia importar. Tambien cuando importamos un modulo ocurre el fenomeno de la asignacion en objetos mutables, si una variable asignada en el modulo, es un objeto mutable, y dentro de mi script se cambia el valor de la misma, este objeto se compartira entre los modulos que corren y cualquiera que lo modifique afectara su valor. Agrego dentro del modulo la siguiente asignacion de variables:
Si ejecuto el script principal, importara el modulo1, y tambien el valor de las variables x e y, que fueron asignadas en el modulo, los cuales se imprimiran en el script principal.
Ahora luego de la asignacion realizada en el modulo, cambiaremos los valores en el script principal:
Vemos que mis asignaciones en el script principal, cambiaron las variables x e y logicamente, x es un objeto no mutable, al asignarlo nuevamente en otro modulo, sera una nueva x con un nuevo valor, pero al asignar a y un nuevo valor que es un objeto mutable, ambos el modulo y el script mio compartiran este objeto, el cual es shareable como hemos visto o sea que cualquiera de los dos que asigne valores, cambiara el valor en ambos, probemos esto.
Cambiemos el module1.py de esta forma, asi cada vez que se llama a la funcion printer imprimira el valor de las variables, x e y aquí dentro del modulo.
El script principal lo modificamos asi, y le colocamos varios breakpoints para ver cuanto vale x e y en diferentes puntos, le doy a DEBUG.
Vemos que el primer print, imprimira el valor de x e y que asigno el module1 al ejecutarse, o sea x=1 e y=[1, 56] Si doy debug nuevamente y paro en el segundo break, justo antes de llamar a la funcion printer, x e y son asignados en principal.py por lo tanto x= 44 y la variable y=[67,56] ya que hicimos y[0]=67
Continuo con debug y para en el tercer breakpoint.
Vemos que el module1.py la variable x, la asigno localmente, pues es una variable de la funcion que al ejecutarla, le asignara la string HOLA MUNDO.
Por lo tanto para el modulo, x es una diferente x que la que yo estoy trabajando en el script principal, pues no es mutable y por lo tanto no es shareable, pero en el caso de la variable y que es una lista y que es mutable y shareable, los cambios que le introduje en el script principal, repercutieron aquí, y al imprimer vemos que para modulo1.py, la variable y ahora vale [67,56], habiendo aceptado los cambios que le hice, y como vemos sera un objeto que compartiran entre ambos modulos, cualquier cambio que se haga en uno, repercutira en el otro.
En el ultimo print de mi script veo que para el mismo x sigue valiendo 44, no importa lo que haya hecho el modulo dentro pues no es un objeto mutable ni shareable, el x de la funcion printer es local, mientras que y que comparten tiene el mismo valor para ambos. En el ejemplo anterior si yo quisiera imprimir el x del modulo en mi script principal deberia volverlo a importar en forma completa, y alli al escribir print module1, al escribir el punto, puedo elegir las variables internas del mismo, en este caso la x interna.
Pero en este caso imprimira 1 que es el valor de x en el nivel superior del modulo, ya que el x='HOLA MUNDO' es una variable interna de una funcion que al terminar de ejecutarse desaparece. Quiere decir que en los objetos no shareables como en este caso, en el script principal la variable se llama x, y se diferencia de la del odulo importado ya que esta se llama module1.x y no son el mismo objeto. Es bueno ir debuggeando y mirando como cambian de valores las variables, en el caso este, si debuggeamos con f7 entrando dentro del module1.py veremos lo que acabamos de entender por medio de los prints, que la variable x de mi script principal, no es la misma que la variable x del module1.py, y que la variable y sera shareada entre ambos modulos al ser un objeto mutable y shareable. Vemos que en el ejemplo anterior tuvimos que hacer un import completo, para poder tener acceso al nombre del modulo, el from no nos permire eso, por lo tanto para casos como ese, es mejor el import completo. Hagamos una modificacion mas para aprender algunos detalles, cambio el module1.py asi.
Y luego el principal.py de esta forma y guardo los cambios.
Coloco un breakpoint en el print, y le doy a debug para que pare en el mismo.
Alli estamos parados, nosotros conocemos del WING, la pestaña DEBUG I/O que es la entrada y salida , la pestaña EXCEPTIONS que nos muestra las excepciones, la pestaña MODULES nos muestra los modulos que tenemos importados, ya sea que los importamos nosotros, o el sistema, o provienen de importaciones indirectas..
Alli vemos que entre los modulos que tenemos tenemos importados esta module1, y si expandimos vemos las variables de este modulo con sus valores.
Por supuesto este modulo aparecera en la lista, solo despues de ejecutar el import y no antes. Tambien sabemos que la pestaña PYTHON SHELL es una ventana interactiva, que no tiene relacion con el programa que esta debuggeandose, lo que tipeemos alli no lo afectara ni tendra ninguna relacion con el mismo, la pestaña STACK DATA nos muestra los valores de las variables agrupadas por locales y globales según donde estemos ubicados, y WATCH nos permite seguir el valor de una variable o expresion, si la tipeo alli, en cualquier punto del programa podre ver cuanto vale dicha expresion, puede ser una operación que necesito saber el resultado por ejemplo x+y o sea la suma de dos variables que cambia mucho, y en cada punto que pare voy y miro el resultado de la operación o la expresion que coloque, pero me queda una pestaña, para que sirve la pestaña DEBUG PROBE, estamos en un punto justo para probarla, parados en un breakpoint, dentro de un programa debuggeandolo.
Alli estamos parados y DEBUG PROBE tiene una linea como si fuera una ventana interactiva, pero que lo diferencia de la de PYTHON SHELL, es que esta ultima es independiente al programa que estamos debuggeando, mientras que la pestaña DEBUG PROBE, todo lo que tipeemos alli seria como si le insertaramos la instrucción que tipeamos en ese punto que esta parado, veamos un ejemplo. Podemos en esta pestaña DEBUG PROBE, ver cuanto valen variables tambien, pues es una ventana interactiva relativa al programa.
Veo que puedo averiguar el valor de una variable en ese punto, pero la puedo cambiar?
Pues parece que si, esto sirve para chequear que pasaria en un programa, si alguna variable cambiara, veamos si continuo debuggeando el mismo me imprime.
Vemos que me imprimio el 5, el valor que yo ingrese en la DEBUG PROBE, quiere decir que cualquier instrucción que sea valida para el programa que ingresemos alli, se insertara y las asignaciones que hagamos tendran validez, lo mismo que podemos ejecutar funciones del programa
o de algun modulo importado y ver que resultados devuelven. Vayamos nuevamente hasta el breakpoint, en la pestaña DEBUG PROBE hay un botoncito OPTIONS con la opcion, CLEAR para limpiar la misma.
Si tipeo module1.__dict__ en medio de muchas tonterias sobre el sistema al inicio de todo me muestra algo muy interesante. Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. All Rights Reserved., 'NameError': , 'dict': , 'input': , 'oct': , 'SystemExit': , 'StandardError': , 'repr': , 'sorted': , 'False': False, 'RuntimeWarning': , 'list': , 'iter': , 'reload': , ...................... vemos mucha info pero antes que nada vemos esto 'dict': , dice que __dict__ es un diccionario, veamos cuales son sus keys, con la funcion keys()
Bueno vemos las keys de este diccionario, quiere decir que puedo ver las keys, y podria ver los valores relacionados con cada una.
Vemos alli la funcion printer que declaramos dentro del module1, por supuesto, no devuelve resultados si no le pasamos una string para imprimir.
Aquí vemos que pasandole el argumento correspondiente , la funcion funciona de la misma manera que si la llamamos en el debug probe en forma directa.
Vemos que el DEBUGPROBE nos permite pasarle argumentos a una funcion que este definida en el programa y este activa, y ademas que python guarda todo lo relativo a modulos importados, asignaciones, funciones, y variables, en un diccionario llamado __dict__ . Vemos los valores de las variables en forma directa y llamandolas por medio del diccionario.
Lo mismo pasa con las otras keys, por ejemplo __name__
o __file__
Ahi vemos que python cuando importa trabaja sobre el archivo pyc, y por eso vemos porque no podemos obtener la misma info sobre el modulo principal, todo esta referido a modulos importados efectivamente que usan los pyc. Bueno en esta parte vimos aproximadamente la mitad del tema modulos, la parte siguiente posiblemente ya terminemos con el mismo, y pasemos al ultimo tema antes de empezar a programar que sera CLASES. Hasta la parte 28 Ricardo Narvaja
28-APRENDIENDO PYTHON DESDE CERO (Modulos-continuacion) CAMBIANDO LA CARPETA DONDE BUSCA EL PROGRAMA EN EJECUCION Utilizando sys.path, previamente habiendo importado sys, podemos imprimir los paths a los cuales nuestra maquina tiene acceso para buscar un modulo.
Tenemos que tener cuidado ya que a pesar que que no haremos cambios definitivos si cambiamos estos paths en forma completa, python no encontrara ya ningun modulo y no podra trabajar, como es una lista podemos usar append, para agregar un path mas.
Vemos que lo agrego el path, ahora creemos la carpeta PIRULO en C y un archivo dentro para ver si despues del append lo encuentra y lo puede importar.
Ahi armamos la carpeta PIRULO y dentro pusimos un archivo compara.py , importemosolo despues del append a ver si lo lee. El archivo compara.py solo tiene dentro un a=5 suficiente para ver si podemos importar y acceder a esa variable.
Alli vemos que imprime el 5 que leyo al importar compara, asi que el metodo funciona, es posible agregar paths en tiempo de ejecucion, sin problemas. CAMBIANDO EL NOMBRE DEL MODULO IMPORTADO Pongamos como ejemplo que un modulo importado tiene un nombre muy largo, o molesto y quiero importarlo, respetando el nombre original, pero poder usar un nuevo nombre internamente para llamar a sus funciones y variables. Pongamos a compara en la misma carpeta donde estoy trabajando con el script principal para que lo halle, y veamos este ejemplo.
Alli tenemos la importacion del modulo compara, pero si lo hago de esta otra forma podre reemplazar compara por el nombre quie quiera para llamarlo internamente.(en este caso C)
Vemos que import compara as ..... reemplaza el nombre compara por el que pongamos luego de as en este caso C, asi que podemos llamar a las funciones de compara con C seguido de punto, en este caso C.a es similar al original compara.a. Tambien se puede aplicar a from cuando una funcion es un nombre largo, o molesto para trabajar.
En este caso reemplace el nombre de la variable a con la letra P, que no es mucha diferencia, pero hay funciones con nombres larguisimos que a veces es mas facil buscarle un nombre corto si hay que trabajar mucho con ellos. Bueno realmente no hay mucho mas sobre modulos, hay un par de puntitos pero son casos muy especificos que veremos en el momento de usarlo, no agregandaremos mas esta parte por casos tan especificos y puntuales. Bueno hasta la proxima parte donde veremos el ultimo tema de la primera etapa CLASES. Hasta la 29 Ricardo Narvaja
29-APRENDIENDO PYTHON DESDE CERO (CLASES-inicio) Hemos hablado de como manejar objetos que ya existen en python, como por ejemplo strings, listas, tuplas, etc, pero como python es un lenguaje orientado a objetos, existe la forma que nosotros podamos crear objetos a nuestro gusto, de esta forma si creamos una clase de objeto, luego todos los objetos de esa clase, tendran un nombre, y valores diferentes, pero conservaran las propiedades de objeto que les otorgue al crearlos. Muchos diran que con lo que vimos hasta ahora se puede hacer de todo y es casi cierto, pero lo siguiente nos da mas posibilidades en el lenguaje, asi como cuando tengamos que manejarnos con GUIs, podremos hacerlo mejor que solo utilizando funciones, ya veremos bien en los ejemplos de uso, y seguro quedaremos convencidos de su importancia. PROGRAMACION ORIENTADA A OBJETOS Si el que lee, como yo, no ha tenido experiencia con OOP (abreviaremos asi Programacion Orientada a Objetos), algunos ideas que usemos le pareceran extraños al inicio, pero tenemos buenas noticias, en python OOP es mucho mas sencillo que en otros lenguajes como C++ o Java, por lo cual, tendremos una primera experiencia en OOP con el lenguaje mas sencillo para esto. Tambien hemos visto que los objetos que ya existen en python, tienen metodos especiales para ellos, si tenemos una string, hay metodos especiales, que se usan con ellas, si creamos un tipo de objeto, tambien podemos crear metodos especiales para tratar ese tipo de objeto, y luego llamarlos en forma muy similar a como lo hemos visto hasta ahora, ya veran que no es el leon tan feo como lo pintan, con ejemplos todo se va aclarando. De cualquier manera iremos paso a paso porque es un tema que hay que entender bien, y si tiene que llevar varias partes no importa, pero que quede bien claro es lo mas importante. TRABAJANDO CON CLASS La declaracion class es utilizada para realizar el trabajo que venimos comentando de la OOP, con ella crearemos las clases, seran nuevos tipos de objetos y le podremos agregar metodos que trabajan con ellos, la declaracion class tiene estas caracteristicas. .Cada declaracion CLASS crea una nueva clase. .Cada vez que una clase es llamada se genera una nueva instancia de esa clase. .Estas instancias son automaticamente linkeadas a la clase que pertenecen. Ya saben que a mi los ejemplos me gustan mas que las palabras y aclaran mejor, vayamos a los ejemplos y ahi iremos apuntando comentarios sobre los mismos. Bueno vamos por partes como dijo JACK, empecemos definiendo una clase sencilla sin ningun metodo, y luego creando instancias de la misma clase.
Esta es la clase mas sencilla existente y realmente se parece mucho a un funcion, ya complicaremos de a poco el tema, vemos que con class al inicio se define un tipo de objeto de clase C1, esa sera la clase, y luego con a =C1() creamos una instancia de la clase C1. Este objeto no hace realmente mucho por su sencillez, jeje consideremos a la variable a como si fuera un objeto de los que conocemos (string, tuplas, etc), este sera de clase C1, creada por nosotros, y lo unico que podemos hacer con el sera según como esta definida, usar el atributo interno nombre, que me devolvera la string 'pepe' en una asignacion interna de la misma, si vemos el resultado de los print .
Recordamos como cuando teniamos una variable de string, por ejemplo al escribir el nombre de la variable y punto a continuacion, WING nos sugeria los posibles metodos para ese tipo de objeto, incluso tuvimos un ayudante maravilla que nos aclaro el funcionamiento de muchos de esos metodos, para los diferentes objetos que vimos. En este caso es similar, solo al tipear a y el punto, WING nos sugerira los atributos o metodos que estan definidos en el class, para el tipo de objeto que acabo de instanciar, en este caso el unico atributo existente es el llamado nombre. Alli vemos en rosado el resultado de print a, como a es una instancia de la clase C1, en este caso no devuelve ningun resultado, es como si en a =C1 ( ) estamos instanciando a la variable a y luego al usar el atributo nombre con a.nombre, nos devuelve la string 'pepe'.
Si tuvieramos dentro de la clase mas asignaciones como en el ejemplo siguiente:
Vemos que al escribir a y el punto, nos ocurre lo mismo que cuanto escribiamos una variable que contenia una string y apretabamos el punto a continuacion, nos salian los metodos y atributos que le podiamos aplicar al tipo de objeto string, lo mismo ocurria con tuplas, listas, diccionarios, cuando tipeabamos una variable de un objeto y el punto a continuacion, se desplegaba un menu que nos sugeria los metodos y atributos que podiamos usar en dicho objeto, aquí ocurre algo similar, ahora tenemos dos atributos posibles para elegir, apellido y nombre. La variable a es una instancia de C1 y al apretar el punto me saldran los posibles atributos y metodos a aplicarle, la diferencia que en este caso los metodos y atributos los cree yo mismo, en vez de depender de metodos preprogramados.
Vemos que una vez que una variable se instancia, hereda todos los metodos y atributos que se definieron en la clase, podemos tener varias variables diferentes instanciadas a la misma clase, si agrego.
Vemos que ambas variables al instanciarse heredan los mismos metodos y atributos, definidos en el class. Agreguemos una metodo dentro del class para ver como vamos poco a poco complicando el tema.
Este metodo que parece asustar mucho porque tiene muchos self dentro es lo mismo que cuando escribiamos funciones y la haciamos de esta forma:
O sea cuando colocamos funciones dentro de clases, se utiliza por convencion, colocar un primer parametro self, que en realidad no significa que le pasemos un parametro mas, ese no se utiliza, quedando el siguiente como el primer parametro que recibira valores. Por lo demas si queremos que las variables se puedan acceder desde fuera del metodo y de la clase, tenemos que agregarle el prefijo self a las variables que se leeran desde fuera, en este caso, si vemos el ejemplo completo al apretar el punto luego de la a, me aparece la variable TE, fuera de la clase.
Si le quito el self veamos que ocurre
Vemos abajo que si llamamos a la funcion dentro del class como en este caso si llamamos al metodo con el argumento 'Rogelio', esto solo asigna a la variable self.TE =43659990.
Vemos que cometi un error en los if, y siempre devolvera usuario desconocido, porque al salir de todos los ifs, esta la asignacion directa de self.TE='usuario desconocido', corrijamos esto
Ahora esta mejor con if y elif si se da cualquiera de las dos primeras condiciones asignara el numero de telefono correpondiente y si no se da ninguna ira al else donde asignara usuario desconocido.
Si pongo un Breakpoint en el input (), y voy a la pestaña DEBUG PROBE y tipeo a para ver cuanto vale.
Devuelve >>> a
Vemos que a quedo instanciada, pero como ya le pase el valor del argumento de entrada que fue 'Rogelio', la variable interna TE, quedo con el valor que se le asigno, por ejemplo en este caso 43659990, si vuelvo a llamar a la variable a, en el mismo DEBUG PROBE para probar.
La siguiente vez que la llamo, con un nombre nuevo por ejemplo 'Jose' , busca en la lista y como no encuentra, asigna a self.TE='usuario desconocido', lo cual podemos acceder desde fuera, llamando nuevamente a a.TE y viendo que ahora cambio su valor a 'usuario desconocido' La variable a, que fue la que lo instancio, ya dijimos que es una instancia de la clase, y esta instancia, queda con ese valor internamente, si llamamos a a.TE, me devolvera usuario desconocido, pero que ocurre con b, que pertenece a la misma clase.
Vemos que b, a pesar de ser una instancia que pertenece a la misma clase, porque fue instanciado con b.C1(), no tiene internamente el mismo valor que a, posee las mismas propiedades, y los mismos metodos y atributos internos, pero la variable a, al haber llamado al metodo con un argumento, pudo asignar un valor a la variable TE, mientras que b, como no llamo al metodo, tiene esa posibilidad, pero no lo ha hecho, por lo tanto b.TE da error. Esto es importante entenderlo a y b son instancias del mismo tipode clase, es como si fueran las variable de='90' y uy='70', ambas son strings y tienen las mismas propiedades pues pertenecen al mismo tipo de objeto y podre realizar con ellas operaciones similares y aplicarle metodos similares, pero tienen distinto valor, las strings son diferentes, en el caso de a y b es similar, son el mismo tipo de instancias de C1, pero una tiene un valor en TE y la otra aun no tiene, porque como depende de los metodos que usemos en cada una, valdran diferente.
Y la prueba final es que para a y b la variable TE puede tener diferentes valores
Alli vemos que a.TE y b.TE tienen diferente valor y se aclara el hecho de que a y b sean instancias del mismo tipo de clase, en este caso C1, pero la misma variable interna tiene un valor diferente para cada una, porque cada variable abre una instancia diferente de la clase, por eso cuando se realiza la instancia es como si la clase se repitiera una vez por cada instancia que se le realiza.
Y vemos que realmente es asi, la instancia de a, se ubica en C355d0 en la memoria de mi maquina, y la instancia de b se ubica en C35710, es como si hubiera dos copias de la misma clase en diferentes posiciones de memoria, por eso asi se entiende que la variable interna TE, para una instancia sea diferente de la misma variable TE, en la otra instancia, pues a pesar de tener las mismas instrucciones, estan repetidas en posiciones diferentes, y se la llama diferente según sea a o b, la que la llama.
Por lo tanto en una clase, vemos que las variables internas, son relativas a la instancia desde cual se la llama, y que a.TE es diferente a b.TE, es muy importante que quede eso en claro, en el caso de las funciones no era asi, la diferencia es bien obvia, una variable en una funcion siempre vale lo mismo no hay diferentes instancias de la funcion para tener diferentes valores. Si uno quiere en el ejemplo anterior, que un metodo este seteado en todas las instancias al inicio en forma automatica, eso se hace con el metodo __init__.
Veamos la diferencia, cambiamos el nombre de la funcion interna que se llamaba telefono por __init__ que es un nombre especial que le dice a python que debe ejecutar automaticamente ese metodo, cada vez que se crea una nueva instancia, para pasarle valores al metodo en la misma instanciacion, sin tener que recurrir a llamadas extra. De esta forma para crear las instancias puedo hacerlo y a la vez pasarle valores al metodo, lo cual no podia hacer antes con el metodo telefono.
SOLO DOS PASOS:1)INSTANCIAR + LLAMAR AL METODO + PASAR VALOR y 2) IMPRIMIR Aquí creo la instancia, y al pasarle un valor al metodo , obtengo las dos instancias de la clase a y b igual que antes en un paso menos, eso si el parametro si uso __init__ sera obligatorio ya que lo necesitara para ejecutar por primera vez el metodo, en el caso anterior, como el metodo no se ejecutaba bastaba instanciar la clase con a= C1( ) sin pasarle parametros, pues la llamada al metodo se hacia en el paso subsiguiente. Recordemos como era antes:
EN TRES PASOS:1)INSTANCIAR, 2)LUEGO LLAMAR AL METODO y PASAR VALOR y 3)LUEGO IMPRIMIR Tenia que crear las instancias, y alli no podia pasar valores lo debia hacer en dos pasos, uno para instanciar a y b, y otro para llamar al metodo telefono en ambas instancias, y pasarle el valor que definiria en ambas el valor de a.TE y b.TE, en el caso del nuevo ejemplo, los dos pasos primeros se resumen en uno solo, como el metodo se ejecuta al instanciar a y b directamente, en el mismo paso debo pasarle el argumento que determinara a.TE y b.TE
Agreguemos un segundo metodo llamado pomelo, cuyo argumento sea el precio del kilo de pomelos, veo que mientras no llamo al metodo pomelo, no hay diferencia con el ejemplo anterior. Vemos que el intento de instanciar a la nueva variable c en la misma forma que a y b, da un error
Y es logico ya que c, usa el metodo __init__, el cual se ejecuta y el metodo pomelo no se ejecuta solo, de esta forma c, se convierte en una nueva instancia de la clase C1, el 500 que le pase, lo tomo como argumento del metodo __init__, y definio el valor de c.TE como usuario desconocido, pero para poder definir la variable valor debemos ejecutar el metodo pomelo, que como no es el metodo inicial init, no esta asignada y hay que hacerlo separadamente, llamando por el nombre de la misma o sea corregimos el error y llamamos a la instanciacion de c con el parametro 'Rogelio', para el metodo __init__, y luego debemos llamar al metodo pomelo, lo hacemos con c.pomelo (500).
Alli vemos que el hecho de haber puesto un metodo inicial obligatorio que se autoejecuta, de un parametro como es __init__, nos obliga a crear las instancias en vez de con c= C1() como la vez anterior, con un argumento al menos, ya que si no dicho metodo , no tendria el argumento para ejecutarse en forma obligada al realizarse la instancia, por lo demas, una vez creada la instancia y teniendo ya c definida, puedo llamar al metodo pomelo con un argumento de 500 y imprimir el valor de la variable valor para c. c.valor=500 por supuesto como a y b son tambien instancias de la misma clase que c, puedo llamar a pomelo desde ellas.
Alli esta a.valor =200 y b.valor=300 ya vimos que cada instancia de la clase, obtendra diferentes valores para cada variable, ya que esta instanciada en diferentes partes de la memoria . O sea que cuando tenemos un metodo __init__ ,estamos obligados a pasarle los parametros necesarios para que se ejecute, en el ejemplo el parametro de __init__ era uno solo ya que self, no se cuenta, asi que el argumento usuario era obligatorio de pasar, y al pasarselo, ya creamos la instancia y a la vez asignamos los valores internos para la misma. Por lo tanto aprendimos a usar el metodo __init__ que es tambien llamado el metodo constructor, pues siempre se ejecuta automaticamente cuando se realiza instancia de la clase, por supuesto su colocacion es opcional, pero si no lo colocamos, deberemos realizar como en el primer ejemplo, llamadas para instanciar primero, y luego recien en un segundo paso, pasar los valores necesarios, para asignar lo que con __init__ se realiza en un solo paso. Bueno creo que como incio al tema CLASES y OOP vamos bien, aun hay mucho por contar espero que esta parte le sea clara, asi podemos seguir adelante con clases en la parte 30. Hasta la parte 30 Ricardo Narvaja
30-APRENDIENDO PYTHON DESDE CERO (Clases-subclases) Seguiremos profundizando el tema de clases y OOP, con ejemplos y veremos como construir subclases, para facilitar la programacion. Existe clases y subclases, como veremos en el siguiente ejemplo donde creamos una nueva clase llamada Empleados, con metodos para aplicar a los objetos de esa clase, que estan definidos dentro del class.
Alli vemos la clase Empleados, para todos los empleados de la empresa, por ejemplo, tiene metodos para hallar su sueldo, si tiene bonos, promociones y el porcentaje de sueldo que cobrara cuando se jubile, es un ejemplo practico. Al hacer Pepe = Empleados () Estamos creando una instancia de esta clase, para el nombre Pepe que es un empleado de esa empresa, yo siempre en mi cabeza para no confundirme, nunca olvido que al crear una instancia, lo que hacemos es crear la variable Pepe,y la misma sera del tipo de objeto Empleados, y tal como si fuera cualquier tipo de objeto string, tablas, listas, etc, tendra metodos que le podremos aplicar a ese tipo de objetos, en este caso tiene metodos para hallar el sueldo, bonos, jubilacion, etc, para cualquier instancia de esta clase. (cualquier otro empleado de la empresa por ejemplo) Eso es lo que hacemos en la siguiente linea, al llamar al metodo computarSueldo (),el mismo me devolvera en la variable sueldo el valor del sueldo de Pepe, el cual ya podemos imprimir en la linea subsiguiente.
Esta clase Empleados es la clase general que abarca a todos los empleados de esa empresa, pero que pasa si existe una sublase por ejemplo los Ingenieros, que tienen otro sueldo, pero el resto de las prestaciones (bonos, promociones,etc) son similares, podemos crear una subclase de Empleados llamada Ingenieros, que herede todos los metodos anteriores y solo modificar el metodo que es diferente para ese tipo especial de empleados veamos.
Alli vemos la clase Ingenieros que al poner entre parentesis Empleados, heredara todos los
metodos de la misma, salvo el caso del metodo computarSueldo (), que tendra una definicion propia, al estar definida nuevamente dentro de la subclase Ingenieros. Quiere decir que si instancio a Jose en la clase Ingenieros y pregunto por su sueldo con el metodo computarSueldo (), me devolvera en la variable sueldo el resultado 3000,
Vemos que al instanciar a Jose como Ingeniero, la variable sueldo, toma el valor que se definio alli en la subclase, ahora que pasa con los otros metodos que no estan definidos en Ingenieros, preguntemos por las promociones.
Vemos que como la clase Ingenieros no tiene una definicion propia para el metodo promocionar, hereda la definicion general de la superclase Empleados, de la cual deriva. Tambien si quisieramos imprimir los salarios de todos, los trabajadores
Lo que hago es crear una instancia de las clases, para cada empleado, si es Ingeniero, creo la instancia con dicha clase, y si es empleado lo hago con la superclase Empleados. Pepe como es empleado lo instanciare como Pepe=Empleado (), Jose como es ingeniero , lo instanciare como Jose=Ingenieros() y asi a todos, luego hare una lista con todos los empleados, que la llamare compania, y al final en un for, en el cual la variable emp toma el nombre de cada uno de los campos de la lista, cada uno es una instancia, entonces dentro del for, le aplico a cada uno el metodo computarSueldo() con lo cual devuelve en la variable sueldo, el correspondiente el cual imprimo. El problema que se me ocurre es que aquí no tenemos una forma de imprimir el nombre de cada empleado, porque Pepe, es una variable, que es una instancia y si mando imprimirla, me saldra como todo objeto instanciado.
Debemos recordar que Pepe es el nombre de una variable, siempre cuando tengo dudas lo llevo al caso de las strings, si asigno nombre='ADRIANA', si imprimo print nombre, no saldra impreso la palabra nombre, si no su contenido que es 'ADRIANA', en el caso de Pepe es lo mismo al mandar imprimir print Pepe, no saldra el nombre impreso Pepe, si no su contenido que es la instanciacion del tipo de objeto que es, si le aplico alguno de los metodos de su clase, ahi podre imprimir un resultado. Es bueno entender esa diferencia que Pepe es el nombre de una variable que asignamos a un tipo de objeto creado por nosotros en la clase, y su contenido es al cual podemos acceder e imprimir, o trabajar con el.
Alli tuvimos que hacer una lista con las strings de los nombres de los empleados, para poder imprimir sus nombres ya que si mandamos imprimir los nombres de variables, se imprimira su contenido,ahora con la lista, podremos imprimir la string correspondiente a cada empleado, y al lado su sueldo que lo hallara mediante el metodo computarSueldo() de la clase correspondiente.
Otra forma un poco mas compleja pero que funciona es hacer un diccionario con cada nombre de los empleados cada key sera la string del nombre de cada empleado, y como valor asociado si es ingeniero 'I' o si es empleado 'E'.
Alli vemos le ejemplo realizado con un diccionario, en este caso es: lista= {'Pepe':'I','Jose':'E','Pirulo':'I','Jorge':'E','Salvador':'E'} De esta forma emp tomara los nombre de las keys del diccionario, por ejemplo la primera vez dentro del for , valdra emp='PEPE' y alli mismo el if detecta que es ingeniero en este caso, porque lista [emp]= 'I' , y como es ingeniero, salta al else donde se instanciara dentro de la clase Ingenieros con a=Ingenieros (emp) que es similar a a=Ingenieros ('PEPE'), quedando a siendo una instancia de la clase Ingenieros, al cual se le pueden aplicar los metodos de la misma, por eso a continuacion se llama al metodo a.empleado, que imprime el nombre 'PEPE' y luego al otro metodo a.sueldo que imprime '3000' que es su sueldo. Vemos algunas caracteristicas curiosas de este ultimo ejemplo, usamos el metodo constructor __init__ , podemos instanciar y llamar al mismo tiempo pasandole en un solo paso el nombre del empleado en a=Empleados(emp) y a=Ingenieros (emp), si no hubieramos usado el metodo __init__ que se ejecuta en la instanciacion, tendriamos que haber realizado la instanciacion asi x=Empleados() o x=Ingenieros() y en el siguiente paso como no hay ningun metodo contructor se debe pasarle el nombre del metodo al que llamaremos pues son todos iguales, asi que en la siguiente linea sera
x.computarSueldo (yyy) donde llamara al metodo por primera vez con el nombre del empleado (yyy) por ejemplo y asignara las variables internas del mismo y al final un tercer paso print x.sueldo Para imprimir el sueldo del empleado Creo que esta bien claro la diferencia de cuando usa __init__ o no, al usarlo, al ser el metodo inicial y contructor, evitamos un paso, porque python sabe que en la instancia se lo llamara a dicho metodo, y no a otros, por lo cual se ejecuta en primera instancia pasandole valores, si no existe habra que hacerlo en dos pasos, para instanciar y luego elegir un metodo y pasarle valores. La propiedad que se aplica al metodo computarSueldo (), que existe en ambas clases, porque una lo hereda de la principal se llama polimorfismo, ya que la forma de trabajar del metodo dependera de la instancia que lo llame, en este caso si es de la clase Empleados, funcionara de una forma y si es de la clase Ingenieros, funcionara de otra. Lo interesante de trabajar con clases es que en este ejemplo el codigo de Empleados puede haber sido escrito por otra persona, quiere decir que con solo importarlo a una clase nueva, podemos hacer cambios, y crear nuevas subclases, que funcionaran como la original, salvo los cambios que nosotros introdujimos en la subclase, a la cual le podemos agregar metodos nuevos o modificar los existentes. Si alguien programo la clase empleados y la puedo importar, solo mi codigo se reducira a class Ingenieros (Empleados): def __init__ (self,name): self.empleado=name def computarSueldo(self): self.sueldo=3000 Y luego las llamadas a las distintas clases, de esta forma no tendre que reescribir la clase principal que puede ser compleja, y yo la utilizare y podre adaptarla a mis necesidades modificando la subclase solamente o agregando mas según necesite.. Bueno creo que es suficiente por esta vez seguiremos profundizando con clases, en la parte 31. Hasta la parte 31 Ricardo Narvaja
31-APRENDIENDO PYTHON DESDE CERO (Clases-Detalle) Ahora que hemos visto un paneo general sobre como trabajan las clases, vamos a tratar de ir profundizando mas en detalle sobre su funcionamiento. Ya sabemos de verlo en la parte anterior, que las clases son como fabricas, que proveen un comportamiento por default para los objetos instanciados en ellas, estos objetos son los objetos reales que el programa procesa y cada uno tiene acceso a los metodos y atributos, que la clase les provee y heredan de ella. Una diferencia, muy importante entre los modulos importados y las clases, es que podemos crear multiples instancias de las clases, pero solo una copia de los modulos, por eso es que necesitamos hacer reload de un modulo, si lo llamamos nuevamente para que se ejecute como cuando lo importamos. Veamos mas ejemplos que tanta palabra sin ver ejemplos, a veces puede marear:
Alli tenemos la definicion de una clase llamada FirstClass, esta clase no existira, hasta que no se ejecute el class, de la misma forma cuando vimos la parte de funciones y como solo existian cuando se ejecutaba el def, y no antes. Aquí es similar al llegar al class y ejecutarlo existira un nuevo tipo de clase, cuyo nombre esta en el
header de la misma, a continuacion de la declaracion class. Luego vienen usualmente los metodos internos definidos por varios def, ellos definen el comportamiento de la clase, estos def se comportan iguales que las funciones que vimos externamente, cuando se ejecutan los def se crean los metodos internos, pero tiene que haber una llamada a ellos o caller, para ingresar y ejecutar las funciones en si. Asi que solo de ejecutar la primera vez el class, tenemos creado la nueva clase y los nombres de los metodos internos, que fueron creados mediante def. Lo que no tenemos ya que no se han ejecutado estos metodos aun es la asignacion de las variables internas como self.data ya que eso ocurrira recien cuando haya una instanciancion y se llame al metodo setdata. Como vemos, a continuacion se realiza la instanciacion de las variables X e Y X=FirstClass () Y=FirstClass () Estas dos instancias nuevas que se crearon comienzan vacias, al no haber dentro de la clase un metodo __init__ o contructor que se ejecute automaticamente, pues lo que ocurrira que las variables X e Y, estan preparadas para acceder a los metodos de la clase, pero aun no lo han hecho, por lo cual estan vacias. El proximo paso seria usar estas instancias y llamar a los metodos internos de la clase agreguemos eso a nuestro programa.
Alli llamamos al metodo setdata pasandole un solo argumento, pues es un metodo que tiene el argumento self que no se cuenta, y el argumento value, que hay que pasarle un valor, de esta forma el metodo al llamarlo, asigna a la variable self.data el valor que acabamos de pasarle, y como dicha
variable como comienza con self, estara accesible para poder leerlo fuera de la clase. Realmente la explicacion del self es sencilla, siendo metodos que trabajan sobre instancias de la clase, necesitan saber que instancia las llamo, y por eso el self cuando llamo con X.setdata ('Pirulo') Realmente la X es el self, si vemos dentro del metodo. def setdata(self, value): self.data = value lo que implicitamente le estamos pasando es def setdata(X, 'PIRULO'): X.data = 'PIRULO' Vemos que el self lo que hace realmente es decirle a la clase con que instancia esta trabajando y redirigirla a la misma. De cualquier forma este mecanismo es automatico asi que no hay que precuparse por el, solamente recordar que una funcion dentro de una clase, lleva un argumento automatico que es el self, que no hay que pasarselo. Bueno en el momento que se llama a X.setdata ('Pirulo') Como la instancia X, no posee un metodo propio llamado setdata, python sigue el link a la clase y busca si la clase a la cual corresponde la instancia X, lo tiene y en ese caso lo ejecuta, si no existe dara error. Esta es la forma en que las instancias heredan los metodos de la clase, y la forma en que python busca en la clase a la que pertenece si hay metodos con ese nombre alli definidos.
Ahora para la otra instancia, Y, llamamos tambien al metodo setdata, el cual para la misma seteara la variable self.data con el valor '56', no hay nada oscuro aquí como vimos el self corresponde al nombre de la instancia que lo llama, por lo cual realmente cuando llamo X, lo que seteo fue X.data='Pirulo' y cuando lo llamo la instancia Y, lo que seteo fue Y.data='56' lo cual podemos comprobar si imprimimos esas variables.
Alli vemos que para cada instancia, la variable data fue asignada con diferente valor, por el funcionamiento del self, el cual le dio a la clase, la posibilidad de saber que instancia llamo a la misma, y redirigirla a asignar la variable con el prefijo X o Y según sea la instancia que la llamo. Tambien veremos, que no hay limitacion de tipo de variables que podemos ingresar, mientras que en X.data en este ejemplo es una string , para demostrar podriamos cambiar Y.data a un numero, por ejemplo.
No hay limitacion en una clase sobre el tipo de argumento que se le puede pasar, la limitacion solo ocurrira si realizamos una operación incorrecta a una variable que no lo permite, mas adelante o le trato de aplicar un metodo que no existe para ese tipo de objeto. Tambien es de notar que como en python una vez que una variable se asigna comienza a existir, en las clases tambien es asi, una vez que las variables X.data e Ydata existen podemos cambiarles el valor con una asignacion directa fuera de la clase.
Alli lo vemos fue necesario llamar a la funcion setdata para crear las variables X.data e Y.data para cada instancia, pero una vez creadas esas variables pueden seguir siendo utilizadas en el programa fuera de la clase, y asignadas perfectamente con nuevos valores. Tambien aunque es poco usado podemos asignar nuevas variables a una instancia,fuera de la clase.
Hemos visto que las instancias heredan sus atributos de las clases y estas pueden heredar atributos de una superclase, al colocar entre parantesis en su header el nombre de la superclase a la cual hacemos referencia, esto lo hemos hecho en la parte anterior: class salir (Aire): La nueva clase salir heredara los atributos de la superclase llamada Aire. De esta forma cuando python intente buscar un atributo de una instancia de la clase salir, primero buscara en ella, y si no esta en ella, como segunda opcion buscara en la superclase Aire, de esta forma los cambios que realizamos en la clase salir que es la que estamos definiendo, siempre quedan activos, ya que de existir el mismo atributo en la clase y en la superclase, siempre busca desde la menor y subiendo hasta la mayor, por lo cual tomara la que yo cree, en vez de la de la superclase. Vamos a un segundo ejemplo que dependera del anterior usaremos la clase ya creada FirstClass como superclase y creamos otra que heredara sus atributos. class SecondClass (FirstClass): De esta forma la nueva clase SecondClass, hereda todos los atributos y metodos de la clase FirstClass, alli define nuevamente el metodo display.
class SecondClass (FirstClass): def display (self): print 'Valor actual = %s'% self.data Vemos que usara una propia definicion del metodo display, en el cual imprimira la string 'VALOR ACTUAL= ' y por medio de un format string ingresara self.data para que se imprima. Luego se crea una instancia para la nueva clase llamada Z. Z= SecondClass () Dicha instancia heredara los metodos de la superclase FirstClass, pero en el caso del metodo display, usara el propio ya que al estar en ambos, siempre usa el de ranking menor, que es el que estamos trabajando nosotros. Luego puedo llamar al metodo setdata Z.setdata ('UY') el cual realizara la asignacion interna de Z.data lo cual imprimo finalmente
Alli vemos como mediante el format string imprimio el contenido de Z.data usando el metodo display de SecondClass, en ves de el de la superclase FirstClass. Es bueno saber que todos los cambios que yo realice a traves de una subclase, como en este caso, no afectaran a la superclase, si en este ejemplo vuelvo a realizar una nueva instancia de la superclase FirstClass.
Vemos que en una nueva instancia de FirstClass sigue funcionando como antes, me imprime al llamar al metodo display, el valor de la variable Z.data en este caso, sin poner 'VALOR ACTUAL=' como si lo hace con las instancias de SecondClass. Quiere decir que cuando tengo una herencia de clases, según donde instancie la variable, accedere a los metodos de la superclase, o a los metodos de la subclase creada por mi, tengo las dos posibilidades a mano de trabajar con ambas a la vez. Tambien debemos saber que si la clase esta en otro modulo, podemos importarla a traves de su nombre pongamos por ejemplo que FirstClass estuviera en un modulo externo al que estoy trabajando. Importandola con el from from modulename import FirstClass class SecondClass(FirstClass): def display(self): ...
#Importo la clase que esta incluida en modulename # Luego puedo usar el nombre de la misma directamente
De esta forma puedo utilizar clases que se encuentren en otros modulos, importarlas usarlas direcamente o crear subclases donde modifico o agrego metodos, a mi voluntad. Si la importo con import import modulename # Accedo a todo el modulo class SecondClass(modulename.FirstClass): # necesito usar modulename.FirstClass def display(self): ...
En el caso de importar todo el modulo ocurre lo mismo que con los nombres de funciones, ya hemos visto que debemos agregarle delante el nombre del modulo en la forma modulename.FirstClass mientras que haciendolo con from podia usar directamente FirstClass import person x = person.person( )
# Importo modulo # uso clase con el nombre del modulo delante.
from person import person x = person( )
# Uso from. # Uso directo el nombre de la clase.
Esto es similar a lo que ocurria importando funciones de modulos ya lo hemos visto con detalle y aquí es similar. Bueno aquí termino esta parte 31 seguiremos avanzando en el tema clases profundizando paso a paso hasta que este bien claro. Hasta la 32 Ricardo Narvaja
32-APRENDIENDO PYTHON DESDE CERO (Clases- Sobreescribiendo operadores de python para nuestra clase) Una de las caracteristicas especiales de las clases, es que podemos cambiar el comportamiento de los operadores de python para que funcionen en forma diferente para una clase. Si vemos metodos con nombres similares a __X__ (como por ejemplo __add__, __mul__, __sub__, etc), estos metodos reemplazaran en este caso, la forma de sumar o de multiplicar para las instancias de nuestra clase, haciendo que lo hagan en una forma especial y acercandose mas a los objetos precostruidos que trae python, que tienen sus propias reglas para realizar las operaciones, nuestra clase tambien las tendra si las definimos. Usemos como base. el script que teniamos de la parte anterior si no lo tienen lo copian de aquí: class FirstClass: def setdata(self,value): self.data=value def display (self): print self.data X=FirstClass () Y=FirstClass () X.setdata ('pirulo') Y.setdata ('56') class SecondClass (FirstClass): def display (self): print 'Valor actual = %s'% self.data print X.data print Y.data En el wing lo vemos tambien:
Si lo corremos recordamos que:
En X.data e Y.data quedan los valores 'pirulo' y '56' los cuales son impresos. Hare una tercera subclase : class ThirdClass(SecondClass): Esta nueva clase importara todos los metodos de SecondClass, que a su vez importa la superclase FirstClass y los metodos que esta tenga. class ThirdClass(SecondClass):
def __init__(self,value): self.data=value En este caso cuando realicemos una instancia a traves de la clase ThirdClass, al tener un metodo constructor o __init__ , podremos pasarle el valor en el mismo momento de la instanciacion como ya vimos. Si instanciamos una variable llamada M en esta tercera clase. M=ThirdClass ('ojo') Le tenemos que pasar el parametro obligatorio del metodo constructor, en este caso el paso la string 'ojo'.
Alli esta al argumento obligatorio del metodo constructor, de esta forma, en el mismo momento de instanciar ya hemos asignado a la variable data, que sera M.data en esta instancia, dado que self, corresponde al objeto M, si imprimo este valor.
Hasta aquí nada nuevo solo una nueva clase que al tener el metodo constructor, hace en dos pasos, lo que antes teniamos que hacer en tres pasos. Todo esto ya lo sabemos, tambien vemos que si llamamos al metodo display () que fue definido en la SecondClass, imprime el valor actual de la variable self.data o M.data que es la string 'ojo'.
Ahora le agregamos un metodo llamado __add__, que es un metodo especial como todos los que tienen esta forma, __ X__
Alli vemos que con el metodo __add__ , podremos cambiar la forma de sumar o concatenar las instancias de ThirdClass, el metodo devuelve mediante el return el valor self.data de forma que directamente se pueda asignar a una variable en este caso b, el resultado de la suma o sea si
self.data en el caso de M es M.data valia 'ojo' y le sumo '89' con el comando. b=M+'89' Como M es una instancia de ThirdClass, al tratar de usar el simbolo +, en vez de sumar directamente lo que hace es llamar al metodo de esa clase, que se llama __add__ y pasarle los argumentos y en este caso realizar la suma con el valor que teniamos guardado y devolver el valor mediante el return, alli veo que realizo la suma en la forma definida por mi.
Ahora que pasa si defino la suma en forma diferente a mi gusto.
La suma estara definida según lo que yo haga dentro del metodo __add__, lo mismo puedo hacer y cambiar la multiplicacion.
Vemos que cada vez que una instancia de la clase ThirdClass use el asterisco llamara al metodo __mul__ y se realizara la operación en la forma que yo decida, si cambio el metodo cambiaran la forma de multiplicarse , despues de todo los creo yo y les otorgo las caracteristicas que quiero jeje.
Alli vemos que el asterisco de la multiplicacion llama al metodo __mul__ pasadole como argumentos el self que es el nombre de la instancia del objeto que estamos en este caso M, y el parametro other que sera lo que queremos multiplicarle. Los metodos con nombres especiales como __add__, __mul__, y el mismo metodo constructor __init__ son heredados al importar una clase y siguiendo el rango de busqueda que ya explicamos siempre desde la clase mas baja subiendo hasta la mas alta ira buscando y usara siempre el mas proximo a la mas baja clase en el cual exista. Quiere decir que si yo importo una clase que tiene un metodo propio para sumar y no me gusta, en mi subclase puede definirlo de nuevo y usara el mio al ser el de rango mas bajo. Vemos que en el ejemplo que di, tanto en el metodo __add__ como en __mul__ se me ocurrio que devuelvan un valor para asignarlo a una variable, pero si quiero puedo quitar los returns y el nuevo valor quedara almacenado en la variable M.data o self.data en forma generica para cualquier instancia y podre imprimirlo mediante el metodo display() que posee el SecondClass . Porque necesitamos cambiar las operaciones para las instancias de una clase? Bueno eso queda enteramente en nuestra necesidad y es una opcion mas, si no definimos operaciones para nuestra clase, al tratar de concretarlas dara un error al tratar de realizar operaciones que no existen, pero no siempre es necesario que las instancias de una nueva clase, puedan multiplicarse, sumarse, o realizar operaciones entre si, recordemos que poseen metodos creados por nosotros que realizan operaciones tambien en ellos, por lo cual, si uno lo necesita los crea y si no, no lo usa, es simple.
Aquí en la tabla, vemos los metodos especiales que podemos utilizar para definir operaciones para nuestra clase, tal como lo hicimos para __add__ y __mul__, aunque no creo que sea necesario especificar y hacer ejemplos para cada uno, pues con una breve busqueda, se puede aplicar lo que ya hemos visto en __add__ y __mul__ a todos los otros. Bueno creo que en la parte siguiente ya terminaremos el tema clases, y sera el fin de esta primera etapa, luego tendremos un breve descanso y comenzaremos la segunda etapa con programacion ya sabiendo toda la base que hemos visto en esta primera etapa. ricnar
33-APRENDIENDO PYTHON DESDE CERO (Clases-Finalizando la primera etapa) Vamos a ir viendo algunas definiciones y ejemplos que nos quedaron para terminar el tema de clases y terminar la primera etapa del curso, luego de un descanso empezaremos la segunda etapa con programacion en python directamente sobre la base de lo que ya vimos. Metodos Hemos utilizado la palabra metodo y la aplicamos a las funciones creadas especialmente para manejar instancias de nuestras clases, realmente se comportan igual que una funcion cualquiera, la unica pequeña diferencia y crucial, es que el primer argumento de un metodo (el famoso self) siempre recibe el nombre de la instancia al cual le queremos aplicar dicho metodo, salvo eso el funcionamiento es similar a una funcion, que ya hemos visto. De forma tal que si yo llamo a un metodo que esta dentro de una clase lo hare con. instancia.methodo (args) como en el ejemplo de la parte pasada haciamos X.setdata ('pepe') Llamabamos al metodo setdata (), correspondiente a la instancia X, pasandole el argumento 'pepe'. Y esa forma de llamarla es traducida por python a: class.method (instancia, args) o como en el ejemplo FirstClass.setdata (X,'pepe') ya sabemos que python conoce que la instancia corresponde a cierta clase, como en el ejemplo, X, corresponde a la clase FirstClass ya que fue instanciada en ella cuando hicimos X=FirstClass () de esta forma python buscara el metodo setdata(), en esta clase, y si no lo halla seguira buscando en superclases superiores, de no hallarlo en todas las clases superiores, dara error. Recordamos en el ejemplo anterior, cuando llamabamos a M.display (), para imprimir el valor de M.data, imprimia la string 'ojo', pero tambien podriamos llamarlo en esa otra forma. FirstClass.setdata (X,'pepe') Si pongo un breakpoint en el input(), para luego de llamar al metodo display () cambiarme a la pestaña DEBUG PROBE y probar si funciona.
Vemos que display solo tiene el argumento self, por lo cual solo debemos pasarle el nombre de la instancia, y imprimira el valor de self.data correspondiente a la misma, en este caso M.data, veamos.
Alli vemos que tenemos el mismo resultado que cuando llamamos con M.display(), pero en el caso de esta ultima opcion es mucho mas breve pues ya tiene implicita que pertenece a M con lo cual la llamada es mas simple. Herencias El arbol de herencia esta contruido de la siguiente forma: 1) Tenemos por ejemplo dos superclases S1 y S2 creadas con sus propios class. .class S1: ........ .class S2: ........ 2) En la definicion de una subclase llamada X, quiero heredar de las dos superclases, por lo tanto debere colocar el nombre de las mismas entre parentesis, por ejemplo. .class X (S1,S2) 3) Y por ultimo al realizar la instanciacion de un objeto perteneciente a esta clase, heredara los metodos de la clase X y de las superclases S1 y S2. Podemos ver esto en este grafico
Este grafico esta dividido en 2 partes a la derecha las instrucciones o declaraciones en el programa y a la izquierda a que parte del arbol de herencia pertenecen. Vemos en la linea superior en la parte celeste, a la izquierda la creacion de las superclases S1 y S2, y a la derecha las instrucciones de declaracion de las mismas class S1: y class S2:. Luego en el siguiente nivel en rojo, vemos la clase X, y su creacion mediante la instrucción class X (S1,S2) donde hereda los metodos y atributos de las superclases y en el tercer nivel en amarillo vemos la instanciacion de un objeto con la instrucción object=X(), con el cual object pasa a ser una instancia que hereda todo de las clases X y las superclases S1 y S2, finalmente como ultimo nivel estan las llamadas a los metodos internos de la clase X, por medio de object.attr ya que attr en el ejemplo es el nombre de un metodo de la clase X. Este grafico es muy importante entenderlo y cada vez que se trabaja con clases tenerlo en mente, para no confundirse, si es necesario venir a mirarlo cuando hay dudas. Veamos el siguiente ejemplo con diferentes comportamientos de clases, todas las subclases heredan de la clase SUPER y realizan alguna accion de la cual deriva su nombre. Aquí esta el codigo completo luego miraremos parte por parte que hace cada clase y lo tracearemos.
CLASE SUPER class Super: def method(self): print 'in Super.method' def delegate(self): self.action( ) Lo primero es que existe una superclase llamada Super ( el nombre obviamente se debe a que es la SUPERCLASE jeje), la cual tiene un metodo llamado method(), incluido, que imprime cuando se lo llama la string 'in Super.method' y otro metodo llamado delegate() que cuando lo llamamos lo que hace es llamar a un segundo metodo llamado action() que debe estar definida en algun lugar si no dara error, ya veremos donde. CLASE INHERITOR class Inheritor(Super): Pass La segunda clase que encontramos se llama Inheritor y lo que hace es importar todo de la superclase llamada SUPER y ejecutar un pass o sea nada, es una instrucción que no realiza ninguna accion, el nombre INHERITOR significa HEREDERA, y eso hace hereda y nada mas.
CLASE REPLACER class Replacer(Super): def method(self): print 'in Replacer.method' La siguiente clase se llama REPLACER o REEMPLAZADORA ya que lo que hace es reemplazar el metodo llamado method(), por uno propio, de esta forma se utlizara este, en vez del metodo del mismo nombre de la superclase Super, imprimiendo un mensaje propio. CLASE EXTENDER class Extender(Super): def method(self): print 'starting Extender.method' Super.method(self) print 'ending Extender.method' Esta clase Extender, agrega un par de strings, al incio 'starting Extender.method', luego llama al metodo original de la clase Super y luego imprime 'ending Extender.method', alli vemos porque se denomina EXTENDER pues le agrega un par de prints al original sin anularlo. CLASE PROVIDER class Provider(Super): def action(self): print 'in Provider.action'
Y la ultima es la clase llamada PROVIDER o sea la clase proovedora, ya que posee el metodo metodo action () que necesita la clase Super. Traceemos el programas con STEP INTO y paramos en la primer instrucción.
Al ir apretando F6 va definiendo las clases y saltando por encima de ellas,
Una vez que pasa por todos los class y define todas las clases una a una, llega a este codigo que esta justo debajo de la definicion de todas las clases, veamos que hace
El codigo tiene un if que chequea que el script corra como main o modulo principal y no sea importado, si es importado no estaremos en __ main__ y no se ejecutara lo que esta dentro del if, asi que no hay problema alli parados miramos en el stack data.
Vemos que __name__ vale __main__, asi que no hay problema, estamos ejecutando este script como principal y entrara dentro del if.
Vemos que dentro hay un for donde una variable x tomara el valor de las tres clases que estan dentro de una tupla, la primera vez que se ejecute, sera x=Inheritor En la siguiente linea imprime el valor de la clase, como si imprimimos x, nos dice que es una clase y nada mas, al imprimir x.__name__, podemos imprimir el nombre de la misma.
Alli vemos en el DEBUG PROBE lo que dijimos.
Bueno luego realiza una instancia klass= x() instanciara la variable klass, a la clase Inheritor y luego llamara a klass.method () Con lo cual llamara al method () de la clase Inheritor, pero dicha clase no tiene dicho metodo , asi que lo buscara en su clase superior Super y usara ese, imprimiendo.
Si traceo con F7 veo que salta alli directamente al metodo de la clase Super.
Una vez que imprime el cartel 'in Super.method', que nos indica que se imprimio en la clase Super, y ademas vemos por que se llama clase Inheritor ya que hereda pero no cambia nada, usa lo de la superclase sin tocarlo , luego vuelve al for para que x tome el segundo valor de la tupla que es Replacer.
Vemos que ahora hara lo mismo solo que klass se instanciara en la clase Replacer
Y alli llamar al method () correspondiente a la clase Replacer.
Si apreto f7
Como Replacer tiene su propio method () no tiene que buscar mas arriba en el arbol de herencia y imprime el cartel propio de la clase Replacer, ademas por eso la clase se llama Replacer pues reemplaza el cartel que tenia la superclase por el suyo propio.
Alli lo hizo y volvio al for para la ultima vez, en este caso x tomara el valor de Extender, y klass de nuevo se instanciara como de esa clase y llamara al method() de la misma.
Al apretar F7 saltara al propio metodo de Extender el cual imprimira carteles propios y el de la clase Super tambien
Y luego volvera para imprimir el ultimo cartel en Extender.
Si apreto f7
Veo que Extender se llama asi porque no solo imprimio el cartel de la clase Super, si no que extendio con carteles propios antes y despues del original.
Vemos que al salir del for, aun instanciara un objeto en la clase Provider que no habia usado.
Y con ella llamara a al metodo delegate () de la clase Provider si apreto f7.
Como Provider no tiene metodo delegate() lo buscara en su SUPERCLASE llamada Super, pero casualmente el metodo delegate () de la misma, lo que hace es llamar a un metodo llamado action() que no tiene la clase SUPER, si apretamos f7,
Vemos que vuelve a la subclase Provider que fue la que habia llamado a delegate(), y alli encuentra el metodo action(), donde imprime el cartel de la clase Provider, vemos aquí que el nombre Provider es porque proveyo de un metodo faltante que necesitaba la clase SUPER y de alli su nombre jeje.
Y termina jeje Creo que este ejemplo con sus vueltas y traceando es muy instructivo y espero que deje mas en claro el tema de clases, con esto damos por terminada la primera etapa y espero que los que esten atrasados en este tiempo de descanso se pongan al dia, asi podemos entrar con fuerza en la parte de programacion real en python . Hasta la segunda etapa Ricardo Narvaja
Ejercicios - Repaso 1 Estos ejercicios de repaso corresponden a todo lo explicado anteriormente. En el apéndice Libro de estilo puedes consultar cómo deben llamarse los ficheros, dónde guardarlos, etc.
repaso1_01 Escribe un programa que pida un número de días, horas, minutos y segundos y calcule cuántos segundos son en total. Convertidor a segundos Dime un número de días: 365 Dime un número de horas: 5 Dime un número de minutos: 48 Dime un número de segundos: 45 365 días, 5 horas, 48 minutos y 45 segundos son 31556925 segundos
repaso1_02 El calendario musulmán empezó en el año 622 del calendario gregoriano y el año musulmán es algo más corto que el año solar. Por ello, una relación aproximada entre un año del calendario musulmán y un año del calendario gregoriano es: 33g = 32m + 20526. Escribe un programa que convierta años entre ambos calendarios. Convertidor entre calendarios Dime un año: 2002 El año 2002 del calendario gregoriano es aproximadamente el año 1423 del calendario musulmán. El año 2002 del calendario musulmán es aproximadamente el año 2563 del calendario gregoriano.
repaso1_03 Escribe un programa que pida dos palabras y diga si riman o no. Si coiciden las tres últimas letras tiene que decir que riman. Si coinciden sólo las dos últimas tiene que decir que riman un poco y si no, que no riman. Ripios Dime una palabra: madre Dime otra palabra: renombre Las palabras madre y renombre riman un poco. Ripios Dime una palabra: calamar
Dime otra palabra: palomar Las palabras calamar y palomar riman. Ripios Dime una palabra: pegajoso Dime otra palabra: calle Las palabras pegajoso y calle no riman.
repaso1_04 Escribe un programa que pida tres números y que diga de cada uno si es el más grande, el más pequeño o el de enmedio. Comparador de números Escribe un número: 2000 Escribe un número: 1980 Escribe un número: 2500 2000 está enmedio 1980 es el más pequeño 2500 es el más grande Comparador de números Escribe un número: 25 Escribe un número: 25 Escribe un número: 12 25 es el más grande 25 es el más grande 12 es el más pequeño Comparador de números Escribe un número: 7 Escribe un número: 7 Escribe un número: 7 7 es el más grande 7 es el más grande 7 es el más grande
repaso1_05 Escribe un programa que pida el valor mínimo, el valor máximo (mayor que el inicial) y el número de valores intermedios y escriba la lista de valores enteros correspondiente:
Sucesión entera Dime el valor inicial: 10 Dime el valor final (mayor que el inicial): 25 Dime el número de valores intermedios (sin contar los extremos): 2 La lista correspondiente es [10, 15, 20, 25]
repaso1_06 Escribe un programa que pregunte cuántos números vas a introducir, pida esos números y escriba si has escrito alghún número par. Dime cuántos números vas a escribir: 5 Dime el número 1: 17 Dime el número 2: 5 Dime el número 3: 7 Dime el número 4: 22 Dime el número 5: 19 Has escrito algún número par
repaso1_07 Escribe un programa que te pida una palabra o número y te diga si es o no palíndroma o capicúa: Dime algo: 12321 123321 es capicúa o palíndroma Dime algo: sometemos sometemos es capicúa o palíndroma
repaso1_08 Escribe un programa que te pida una palabra y cree una lista con las letras de esa cadena: Dime algo: Una prueba La lista creada es [ 'U', 'n', 'a', ' ', 'p', 'r', 'u', 'e', 'b', 'a']
repaso1_09 Escribe un programa que te pida una frase y una vocal y cambie todas las vocales de la frase por la vocal (una forma de hacerlo es convertir la frase en una lista y hacer el cambio en la lista. Dime algo: tengo una hormiguita en la barriga
Dime una vocal: a La frase es ahora: tanga ana harmagaata en la barraga
repaso1_10 Escribe un programa que te pida una frase y te diga si es palíndroma (una forma de hacerlo es convertir la frase en una lista, eliminar los espacios y comparar) Dime algo: salta lenin el atlas salta lenin el atlas es palíndroma Dime algo: dabale arroz a la zorra el abad dabale arroz a la zorra el abad
Ejercicios - Repaso 2 Estos ejercicios de repaso corresponden a todo lo explicado anteriormente. En el apéndice Libro de estilo puedes consultar cómo deben llamarse los ficheros, dónde guardarlos, etc.
repaso2_01 Escribe un programa que calcule la letra del NIF a partir del número del DNI. La letra se obtiene calculando el resto de la división del número del DNI por 23. A cada resultado le corresponde una letra: 0=T; 1=R; 2=W; 3=A; 4=G; 5=M; 6=Y; 7=F; 8=P; 9=D; 10=X; 11=B; 12=N; 13=J; 14=Z; 15=S; 16=Q; 17=V; 18=H; 19=L; 20=C; 21=K; 22=E. Dime tu DNI: 19901130 Tu NIF es: 19901130 - N
repaso2_02 Escribe un programa que convierta números inferiores a 5000 escritos con cifras árabes en números escritos con números romanos. El valor de los diferentes números romanos es el siguiente: I=1, V=5, X=10, L=50, C=100, D=500, M=1000. Para hacer más fácil el ejercicio, puedes utilizar la notación romana antigua, en que podía haber hasta cuatro símbolos iguales seguidos. Dime un número: 3799 En números romanos, 3799 se escribe MMMDCCLXXXXVIIII
repaso2_03 Escribe un programa que calcule en qué día y mes cae el Domingo de Pascua. Para calcularlo, utiliza el método de Butcher (publicado en 1876), que se muestra a continuación aplicándolo al año 2002. Operación Cociente Resto 1. Divide el año por 19 a=7 2. Divide el año por 100 b = 20 c=2 3. Divide b por 4 d=5 e=0 4. Divide (b+8) por 25 f=1 5. Divide (b+f+1) por 3 g=6 6. Divide (19a+b-d-g+15) por 30 h=7 7. Divide c por 4 i=0 j=2 8. Divide (32+2e+2i-h-j) por 7 k=2 9. Divide (a+11h+22k) por 451 m=0 10. Divide (h+k-7m+114) por 31 n = 3 p = 30 El mes y día se calculan a partir de n t p. Si n es 3, el Domingo de Pascua cae en marzo, y si n es 4, cae en abril. El número del día es p+1. Por tanto en el año 2002, el Domingo de Pascua fue el 31 de
marzo. Dime un año: 2003 El Domingo de Pascua del año 2003 es el 20 de abril.
repaso2_04 Escribe un programa que pida dos años y calcule el número de años bisiestos que hay entre esos dos años (incluyendo los dos años) y el número de días total entre esos dos años (incluyendo los dos años). Para saber cuando un año es bisiesto, consulta el ejercicio ifelse1_03. Dime un año: 1800 Dime otro año: 1900 Entre 1800 y 1900 (ambos incluidos) hubo 24 años bisiestos y en total 36889 días. Dime un año: 1900 Dime otro año: 2000 Entre 1800 y 1900 (ambos incluidos) hubo 25 años bisiestos y en total 36890 días.
repaso2_05 Escribe un programa que te permita jugar a una versión simplificada del juego Master Mind. El juego consistirá en adivinar una cadena de números distintos. Al principio, el programa debe pedir la longitud de la cadena (de 2 a 9 cifras). Después el programa debe ir pidiendo que intentes adivinar la cadena de números. En cada intento, el programa informará de cuántos números han sido acertados (el programa considerará que se ha acertado un número si coincide el valor y la posición). Dime la longitud de la cadena: 4 Intenta adivinar la cadena: 1234 Con 1234 has adivinado 1 valores. Intenta adivinar la cadena: 1243 Con 1243 has adivinado 0 valores. Intenta adivinar la cadena: 1432 Con 1432 has adivinado 2 valores. Intenta adivinar la cadena: 2431 Con 2431 has adivinado 4 valores. Felicidades
repaso2_06 Escribe un programa que sea capaz de jugar al Master Mind con las reglas del ejercicio anterior. Al principio, el programa debe pedir la longitud de la cadena (de 2 a 9 cifras). Después, el programa debe ir intentando adivinar la cadena de números. Dime la longitud de la cadena: 4 Piensa una cadena de 4 cifras distintas. ¿Es 2431 ? Dime cuántas cifras he acertado: 1
¿Es 1324 ? Dime cuántas cifras he acertado: 2 ¿Es 4321 ? Dime cuántas cifras he acertado: 0 ¿Es 1234 ? Dime cuántas cifras he acertado: 4 Gracias por jugar conmigo.
Siguiente lección: ¡No hay más lecciones! Autor: Bartolomé Sintes Marco Última modificación de esta página: 1 de diciembre de 2002
2DA ETAPA DEL CURSO DE PYTHON -PARTE 1- INTRODUCCION-LA PRIMERA TAREA En esta segunda etapa, comenzaremos paso a paso a programar en python según las bases que hemos visto en la etapa anterior, que nos servira para poder acceder a la potencia de uno de los lenguajes mas flexibles que existen. Por supuesto nos seguiremos basando en libros, puesto que yo estoy aprendiendo al igual que los que leen, y si algo se de python, esta al nivel de lo que acabamos de terminar, asi que para este nivel que vamos a encarar necesitare mas paciencia del lado de los lectores,y bueno, necesitare mas ayuda de los libros, en este caso el libraco de Programming Python sera la inspiracion, como fue el de Learning Python para la etapa anterior. Como los dos libros son del mismo autor, Mark Lutz, cuando comenzas a leer Programing Python el mismo autor menciona, que para encarar el libro, hay que tener muy en claro el anterior, el Leraning Python que te da la base de todo, ahora este segundo sera la expansion y puesta en practica de lo ya visto hasta ahora. LA PRIMERA TAREA Nuestra primera tarea sera hacer un programa completo con GUI, que maneje una base de datos de todos los empleados de una compania en forma profesional, y que dado que es hecha por nosotros se debera adaptar como un guante a las necesidades que tenemos, si dependieramos de un software comercial hecho por otros es poco probable, que el programa existente haga lo que necesito exactamente, y puede ser extremadamente complejo, por abarcar cosas que no necesito, de forma que haremos nuestro programa, a nuestra medida, que funcionara si queremos en una consola de comandos, pero tambien le agregaremos una GUI, para que quede mas moderno, y podamos usarlo en ambas formas. Por lo demas en este programita inicial, usaremos todo lo que vimos en la etapa anterior, usara listas, tuplas, diccionarios, clases, OOP y metodos, importaciones y la mar en coche jeje, y probaremos todas las posibilidades para hacerlo, veremos los pro y los contra de cada forma, y nos iremos decantando hacia la solucion final y mejor para nosotros. Cuando uno lee el libro del que estamos siguiendo el curso, parece que el autor nunca se decide sobre cual sera la version final del programa, pero lo que realmente hace es probar de todas las formas posibles para practicar, y ir viendo cual es la mejor y los pro y los contra de cada una. Como todo programa complejo que uno conoce nunca se escribe entero de una sola vez, si no que se va escribiendo la idea principal, y luego se le van agregando partes y funcionalidades hasta hacerlo a nuestro gusto, asi haremos, comenzaremos pensando que es lo que necesitamos saber sobre cada empleado y probando diferentes posibilidades. USANDO LISTAS En el Wing o en el Editor de python que quieran podemos ir escribiendo y explicando a la vez. Lo primero sera crear listas con los datos de cada empleado, del tipo bob = ['Bob Smith', 42, 30000, 'software'] sue = ['Sue Jones', 45, 40000, 'music']
La variable bob sera una lista y por ejemplo, tendra como primer campo, el nombre y apellido completo, el segundo campo la edad, el tercer campo el salario, y el cuarto sera el area en la que realiza su labor.
Ahi vemos que ademas de tipear el codigo, cambie a la pestaña interactiva o python shell para ir probando a ver que resultado vamos obteniendo. Alli tipeo las dos lineas para probar.
Alli las tipee, ahora ya sabemos por la etapa anterior del curso, que si quisiera un dato de la lista puedo pedirlo con indices, como vimos, pruebo alli.
Al tipear bob [0], me devuelve el primer campo de la lista correspondiente a bob que es su nombre y apellido, y sue[2], me devolvera el salario de sue, o sea 40000, como hemos llamado a los dos, separados por comas, el resultado me lo devuelve en una tupla de dos items. Procesar datos en esta forma es muy simple, si quisiera el apellido solo de bob y no el nombre tipeo en la shell.
Recordemos que split (), dividia una string, en este caso al no aplicarle argumentos la divide donde encuentra espacios, pero si le pongo indices en este caso [-1], lo que hara sera un slice o rebanada de todas las strings posibles, desde el final ya que usamos indices negativos, o sea que como la ultima string, si contamos de atrás para adelante es -1, pues al poner como subindice ese valor mostrara el apellido.
Lo mismo si quisiera darle un aumento de sueldo a sue, por ejemplo un 25% podria asignar alli mismo en la shell.
Al hacer sue[2] *=1.25 estoy realmente multiplicando su sueldo por 1.25 y guardando el resultado en la lista, de esta forma le agregue un 25% de aumento en una sola linea jeje. Asi que vemos que la forma de procesar los datos es sencilla, y la base de todo programa es la sencillez, pero sigamos adelante comparando soluciones. Por supuesto hasta ahora solo tenemos dos variables del tipo lista, y no una database asi que si armamos una lo haremos asi:
De esta forma ya nuestro script tiene una lista llamada people que incluye todos los empleados, cada uno de ellos a su vez, es una lista con sus datos si corremos el script hecho hasta ahora vemos que imprime los datos de cada empleado encolumnados, ya que dentro del for, la variable people tomara los valores bob y sue, e imprimira su contenido.
Hasta aquí todo simple y sencillo, sigamos adelante.. Si ponemos un breakpoint en el input y hacemos click en debug para que pare alli.
Cambiemos a la pestaña DEBUG PROBE para seguir probando.
Vemos que desde la variable people, dominamos los datos manejando el indice, en este caso poniendo como indice uno, tengo los datos de sue, incluso si quiero solo el nombre le coloco doble subindice.
Alli mismo en el DEBUG PROBE podemos comprobar que usando un for, puedo imprimir los apellidos
La variable person tomara en cada ciclo del loop, los valores de la lista people, sera primero bob, y en el siguiente ciclo sue, y al ponerle como vimos el subindice [0], leera solo el primer campo que es el nombre y apellido, y al hacer split como ya explicamos con el indice slice con el valor (-1), rebanaremos solo el apellido el cual se imprime, ademas como somos geniales, a cada persona en el campo [2] que es el salario le incrementamos un 20%, jeje podemos comprobarlo alli mismo.
Como yo probando, ya ejecute varias veces este mismo for dentro del PROBE, y como los cambios y asignaciones que alli hago afectan al programa y se van acumulando, en mi caso incremente mucho mas que el 20%, lo malo es que es solo durara hasta que reinicie el script, asi que cuando reinicie el mismo perderan el aumento jejeje, era solo en chiste. Si vuelvo a ejecutar el for les doy un 20% mas jeje
Tambien con un for alli mismo podria imprimir los salarios para ver los aumentos que otorgue
Tambien ya que estamos en el DEBUG PROBE podemos probar ciertas partes que luego si nos gustan, agregaremos al programa, por ejemplo si quiero generar una lista con todos los sueldos que pago, llamare a esta variable pays, y luego obtendre la suma de todos los sueldos que pago.
Usando map () como vimos en la parte 25 de la etapa anterior, nos evitara un for, aplicandole a cada campo de people, la funcion que esta definida como primer argumento de map, por lo tanto esto seria equivalente a
Como vemos si usabamos for teniamos que definir antes la lista vacia, y luego apendear a pays2 en este caso, en cada ciclo, el valor de x[2] que es el salario de cada empleado, creo que con map es mas sencillo, en una sola linea se soluciona todo. pays = map((lambda x: x[2]), people) Esa sola linea realiza el mismo trabajo que las tres lineas que usa el for, creo que es mas comoda, una vez que uno la sabe usar. Existe una forma simplificada que no hemos visto para hacer listas y es la siguiente: pays3 = [person[2] for person in people]
Esta forma aprovecha que people es una lista, y al hacer person [2] for person in people, lo que hara es que person tome todos los valores de la lista people, elija el campo 2, y arme otra lista con el, en este caso asignandola a pays3, bueno son distintas formas para hacer lo mismo. Tambien se puede aprovechar eso para realizar la suma de todo lo que se paga
Aquí usamos sum (), que sumara todos los campos de la lista que creamos, seria similar a
O sea sumara todos los campos de la lista que se creo con el metodo anterior tambien se puede hacer usando el metodo de maps.
El resultado es similar.
Tambien si quisieramos agregar datos de algun empleado nuevo. people.append(['Tom', 50, 0, None]) agregamos a la lista un empleado nuevo.
ETIQUETANDO Ahora el siguiente paso son las etiquetas, porque dentro de un programa, no ayuda que se busque el campo 2 de una lista, es un numero magico que dificilmente lo podamos relacionar con pagos y no recordaremos que indicaba, asi que crearemos etiquetas para los indices.
Vemos que es sencillo, usando la funcion range, le asigno a la variables NAME, AGE y PAY los numeros 0,1 y 2 de esta forma si llamo a bob [NAME] sera similar a llamar a bob [0]. Esto soluciona parcialmente el problema, porque a pesar de que tengo los indices con los nombres correspondientes, esta asignacion de indices, puede cambiar con el tiempo y requerira un mantenimiento extra del programa, por ahi el indice 2, paso a ser la antigüedad, y tengo que actualizar la asignacion de indices, asimismo tampoco podria imprimir, por ejemplo AGE y la edad al lado, puesto que AGE es una variable y no imprimira su nombre si no su contenido, que sera 1.
Vemos que si quiero imprimir AGE, 42, no puedo, pues se imprimira el contenido de AGE que es 1, y eso es un problema, deberemos realizarlo de otra forma. Si nosotros cambiaramos las listas a esta forma >>> bob = [['name', 'Bob Smith'], ['age', 42], ['pay', 10000]] >>> sue = [['name', 'Sue Jones'], ['age', 45], ['pay', 20000]]
>>> people = [bob, sue] Donde cada campo de la lista es otra lista, el problema se solucionaria parcialmente porque podria imprimir como en el caso anterior AGE,42, pero aun tendriamos que usar indices numericos, solo le agregamos un nivel mas de indices. >>> for person in people: print person[0][1], person[2][1]
# name, pay
Bob Smith 10000
Sue Jones 20000 Y ademas lo que queremos es usar indices que nos indiquen que significa cada campo por lo tanto lo mejor que podemos hacer, es probar usar diccionarios. USANDO DICCIONARIOS Si los datos de cada empleado los organizo en diccionarios de esta forma. bob = {'name': 'Bob Smith', 'age': 42, 'pay': 30000, 'job': 'dev'}
sue = {'name': 'Sue Jones', 'age': 45, 'pay': 40000, 'job': 'mus'}
Salvo el tema que los diccionarios no tienen orden, el tema de los indices estaria resuelto ya que la key y cada valor asociado del diccionario los puedo imprimir y ademas los indices ya no son numericos.
Ahi mismo cuando estoy parado en el breakpoint si voy al DEBUG PROBE puedo probar que puedo imprimir el nombre y el salario por ejemplo y utilizo indices que tienen relacion con lo que averiguo y esto me otorga mas claridad en el manejo de datos. Tambien el apellido lo averiguo facil, averiguando el nombre por medio de la key name y luego usando split (), para rebanar el apellido con el indice -1.
Y aumentar el sueldo tambien es sencillo, aquí aumento el 10% facilmente.
Y disminuir el sueldo no es muy popular pero tambien se puede jeje, al dividir por 1.10.
Una forma de llenar un diccionario campo por campo seria sue = {} sue['name'] sue['age'] sue['pay'] sue['job']
= = = =
'Sue Jones' 45 40000 'mus'
De esta forma se asigna campo por campo de un diccionario.
Dado que sue se inicializa como diccionario vacio, el resto son asignaciones directas, creando las keys, y asignando los valores correspondientes a la misma. Otra forma es usar la funcion zip, si recordamos como funciona L1 = [1,2,3,4] L2 = [5,6,7,8] Combinara ambas listas, creando una nueva lista. zip(L1,L2) [(1, 5), (2, 6), (3, 7), (4, 8)] Bueno combinando dos listas con zip y luego usando dict crear un diccionario seria asi.
Luego usando dict, creariamos un diccionario y lo asignariamos a la variable sue.
LISTA DE DICCIONARIOS Bueno sea cual sea la forma de construir los diccionarios de las que vimos, finalmente tendriamos que hacer una base de datos llamada people, que sera una lista cuyos campos seran diccionarios, y podemos acceder a los datos e imprimirlos, a traves de los indices, que seran las keys de los diccionarios.
Todo lo que vimos con indices numericos y listas al incio obviamente se puede hacer tambien con diccionarios, usando las keys, que es mucho mas facil de manejar e identificar. Veamos en la ventana DEBUG PROBE parados en el breakpoint como hacer con diccionarios algunas de las cosas que habiamos hecho con listas. Obtener una lista con los nombres es similar, en este caso la variable names, sera una lista con los nombres de todos los empleados.
Tambien usando map y una funcion lambda, como primer argumento, se puede asignar a una variable names. La variable x tomara todos los valores de people y se le aplicara la funciona lambda, de forma de ir agregando a la lista cada nombre (bob ['name'],sue ['name'], etc)
Como cuando usabamos listas, si quiero imprimir los apellidos, y dar un aumento de sueldo del 10%.
Verificamos que aumento un 10% a cada empleado el salario.
Puedo hallar la suma de todos los pagos usando la funcion sum().
Sigamos probando con diferentes posibilidades, a ver cual nos resulta mas comoda para nuestro programa, probaremos la estructura anidada de datos como la que vemos aquí.
DICCIONARIOS CON DATOS ANIDADOS bob2 =
{'name': {'first': 'Bob', 'last': 'Smith'}, 'age': 42, 'job': ['software', 'writing'],
'pay': (40000, 50000)} Puedo probar esta estructura alli mismo en el debug probe sin reiniciar escribo.
Vemos que ahora las consultas pueden ser mas concretas, en el caso de job o trabajo, la key job, tiene como valor asociado una lista que especifica mejor en que trabaja, dicha lista se puede hacer tan grande como uno quiera, para agregar mas detalles, sobre el tipo de trabajo que realiza, en el caso del pago o pay, vemos que se va guardando los salarios que percibio, de forma de ver como fue aumentando y el ultimo, es el actual, la edad o age seguira siendo un solo valor y el nombre o name, tiene otro diccionario que incluye una subkey last para el apellido y first para el nombre. A simple vista parece complejo, pero no lo es, la forma de acceder a los datos es simple, y siempre las keys nos ayudan sobre lo que deseamos preguntar. Por ejemplo tipeamos alli mismo consultas en el DEBUG PROBE
Vemos que al preguntar por bob2 ['name'], me devuelve nombre y apellido y ademas me muestra las keys, para poder consultar por solo el apellido, asi que la consulta generica me guia hacia una consulta mas especifica como la de solo el apellido, agregando ['last']. Tambien podemos averiguar facil el ultimo sueldo de bob con el subindice -1.
O el anterior con -2, y asi sucesivamente si hubiera mas. Si quisieramos saber los trabajos de bob por ejemplo.
Si bob se especializa y aprende algo nuevo se lo podemos apendear.
Ahi bob se especializo en cableado y por eso se lo agregamos con append, en esta forma un empleado puede tener diferente cantidad de especialidades que otro, sin problemas. >>> bob2 {'job': ['software', 'writing', 'wiring'], 'pay': (40000, 50000), 'age': 42, 'name': {'last': 'Smith', 'first': 'Bob'}} Vemos que es una alternativa posible de usar, aunque para hacerla crecer necesitamos append, de cualquier manera es una alternativa mas que hemos visto, por ahora volvamos al diccionario flat sin
anidamiento de datos.
DICCIONARIO DE DICCIONARIOS Lo que si podemos hacer y puede ser util, para el caso de datos en forma de diccionario, es crear un diccionario vacio con el nombre db y alli hacer lo siguiente: db={} db ['bob']=bob db ['sue']=sue Al tipear db nos devolvera db {'bob': {'job': 'dev', 'pay': 33000.0, 'age': 42, 'name': 'Bob Smith'}, 'sue': {'job': 'mus', 'pay': 44000.0, 'age': 45, 'name': 'Sue Jones'}} Vemos que tenemos otra forma mas de acceder a los datos, bob y sue siguen siendo diccionarios como antes y nos permiten acceder a la info, pero ambos estan dentro de un diccionario mayor llamado db (por database) que resume toda la info en un solo diccionario mayor. Asi podemos llamar a todos los datos desde la misma database db.
Asi podemos unificar todos los datos, los pedidos de informacion, las asignaciones, centralizadas en una unica database, que incluye todos los diccionarios de cada empleado, y a la cual se puede modificar, y ademas cada vez que modifiquemos aquí tambien por ser shared objects se modificaran los diccionarios de cada empleado, veamos si cambio de sueldo en db para sue.
Vemos que el aumento se concreto tanto en la databse general como en el diccionario sue.
De esta forma tenemos el control centralizado, pero a su vez cada cambio que hagamos afectara a los diccionarios individuales de cada empleado lo cual es deseable, asi se mantiene la info actualizada. Vemos ademas otra ventaja, si usabamos los diccionarios de cada empleado por separado, necesitabamos por ejemplo para hallar el pago de sue, un for, para buscar dentro de todos los diccionarios y lograr imprimir el pago de sue, ahora solo con un doble indice, podemos imprimir su pago sin tener que usar un for.
Mientras que antes teniamos que usar for para acceder desde people. for person in people: print person ['name'],person ['pay'] de esta forma imprimia los sueldos de los empleados y se complicaba mucho.
Creo que la database centralizada como diccionario es algo mucho mas comodo. Sabemos que siempre el primer indice sera siempre el nombre del empleado, y el segundo la caracteristica que queriamos preguntar (pay, job, age, name), de esta forma evitamos for y loops de mas, para averiguar datos solo con un doble indice. Quiero saber la edad de bob db ['bob'] ['age']
Sencillo simple y directo al higado jeje. Para hacer un loop que imprima todos los nombres por ejemplo, se puede usar un for facilmente en las nuevas versiones de python se puede hacer directamente asi.
La variable key tomara todos los valores de las keys del diccionario db o sea bob y sue, imprimira la key, luego la flecha, y luego imprimira el nombre y apellido correspondiente llamando con el doble indice como vimos antes.
Vemos que los for en este caso, solo se usan para realizar listas de datos lo cual es logico por otra parte, pues al tener que imprimir mas de un dato, de diferentes empleados tendremos que recorrer toda la estructura para buscarlos e imprimir, pero bueno, eso es obvio, al menos para hallar un dato directo de un empleado, ya lo podemos hacer en forma simple y sin for. Tembien podemos imprimir solo los apellidos y dar un aumentito jeje
Ya le aumente tantas veces y se fue acumulando que llego a 55000, jeje.
Sabemos que la funcion values (), nos muestra los valores relacionados de un diccionario sin las keys, en una lista. >>> db.values( ) [{'job': 'dev', 'pay': 36300.0, 'age': 42, 'name': 55000.000000000007, 'age': 45, 'name': 'Sue Jones'}]
'Bob
Smith'},
{'job':
'mus',
'pay':
O sea que si hago un for para que una variable llamada record, tome todos los valores de db.values() accedere directamente a estos dos campos de esta lista, cada uno es un diccionario asi que si le paso su key interna como indice y tambien me dara el valor correspondiente. for record in db.values( ): print record['pay'] Como la variable record tomara el valor del diccionario de cada empleado, al pasarle una key de ese diccionario me dara el valor correspondiente, en este caso al pasarle la key ['pay'], me dara el salario de los empleado al loopear en el for. En esta forma de trabajar con diccionarios, si quiero agregar un nuevo empleado podria hacerlo facilmente de la siguiente forma. Como db es un diccionario cuyas keys principales son el nombre de los empleados en este caso bob y sue, si agregamos uno nuevo.
db['tom']={'name': 'Tom', 'age': 50, 'pay': 0, 'job': None} Agregamos un empleado y su diccionario nuevo, ahora tipeo db para verificar.
>>db {'bob': {'job': 'dev', 'pay': 30000, 'age': 42, 'name': 'Bob Smith'}, 'sue': {'job': 'mus', 'pay': 40000, 'age': 45, 'name': 'Sue Jones'}, 'tom': {'job': None, 'pay': 0, 'age': 50, 'name': 'Tom'}}
Veo que el diccionario del nuevo empleado se agrego, si lo quiero mas sofisticado, puedo usar la funcion dict para crear el diccionario del empleado de una tupla existente y agregarlo de la misma forma. db['tom'] = dict(name='Tom', age=50, job=None, pay=0) Esto sera similar a agregar el diccionario, solo cambia la forma de generarlo.
Vemos que al igual que para los otros empleados accedo a los datos por un indice que es el nombre del empleado y luego si quiero algun otro dato, pongo un segundo indice con el dato pedido.
Por supuesto la funcion keys (), me da las keys de la database db, que son los nombres de los empleados.
Como vemos, el mostrar las distintas opciones que podemos usar para el programa, nos hace repasar lo que ya hemos visto en la etapa anterior.
Por supuesto si aplico len () al diccionario db , me dira cuantos campos posee y logico posee tres keys principales y sus valores asociados (que tambien son diccionarios), pero para el len, lo que cuenta es el nivel superior por lo tanto db es un diccionario con tres keys principales que son bob, sue y tom, y los valores asociados a estos.
INICIALIZANDO LOS DATOS
Bueno, ya vimos diferentes formas de manejar los datos, debemos inicializarlos y luego hallar una forma de guardarlos, para poder modificarlos o leerlos y poder tenerlos archivados. Crearemos un modulo que se llamara initdata.py, en este momento dejaremos de probar en el DEBUG PROBE o en el SHELL y escribiremos directamente en la parte del WING o programa que usemos, en la zona de escribir scripts.
Alli vemos el codigo, resume lo que vimos anteriormente, cada empleado tiene un diccionario con sus datos, y la database es la variable db que es tambien un diccionario, este metodo de trabajo es llamado DICCIONARIO DE DICCIONARIOS.(incluye un diccionario principal con diccionarios de datos dentro) Al final hay un if que testea si esto se esta ejecutando como script principal y en ese caso, imprimira los nombres de cada empleado y sus datos, en cambio si es importado como modulo, como __name__ no sera igual a __main__, no entrara en el if y no imprimira nada. Si corro este script como principal, deberia imprimir al estar en __main__.
Perfecto, imprime si lo corremos como script principal.
En los sistemas windows, no necesitamos tipear python, delante del nombre del archivo, si las variables de entorno estan bien configuradas, y estamos en el path correcto, solo con tipear el
nombre del script correra, alli tipee initdata.py y listo ya tenemos la salida con los datos de cada empleado. -----------------------------------------------------------------------------------------------------------------------MODULO initdata.py # initialize data to be stored in files # records bob = {'name': 'Bob Smith', 'age': 42, 'pay': 30000, 'job': 'dev'} sue = {'name': 'Sue Jones', 'age': 45, 'pay': 40000, 'job': 'mus'} tom = {'name': 'Tom',
'age': 50, 'pay': 0,
'job': None}
# database db = {} db['bob'] = bob db['sue'] = sue db['tom'] = tom
if __name__ == '__main__':
# when run as a script
for key in db: print key, '=>\n ', db[key] -----------------------------------------------------------------------------------------------------------------------Bueno ahora que tenemos los datos inicializados tenemos que guardarlos en un archivo, eso lo veremos en la parte 2 que esta ya se hizo bastante larga.
Hasta la proxima Ricardo Narvaja
2DA ETAPA DEL CURSO DE PYTHON -PARTE 2- GUARDANDO LOS DATOS Bueno ya tenemos un modulo hecho y guardado, nuestro initdata.py que terminamos y explicamos en la parte anterior. Ahora estudiaremos y realizaremos un modulito para guardar en un archivo, y poder leer, los datos que tenemos en el modulo initdata.py que es nuestra database realmente. Bueno crearemos un nuevo modulo llamado make_db_files.py en el WING, sabemos como hacerlo vamos a NEW, creamos un modulo vacío, y luego lo guardamos con ese nombre.
Bueno lo primero que hacemos es crear cuatro variables, y les asignamos las strings que usaremos en el modulo. La variable dbfilename como su nombre lo indica, tendrá el nombre del archivo , y luego tenemos tres strings, para que se vea mas elegante que se llaman ENDDB y ENDREC para marcar cuando termina de imprimir los datos de un empleado y el final de toda la database y la flecha es para que se vea mas lindo, la idea es que cree un archivo así.
Vemos la strings ENDREC al final de los datos de cada empleado y al final de toda la database, esta el ENDDB para marcarlo, y además las flechitas que decoran un poco los datos para que se lean mejor. Bueno no es muy difícil hacer un script que guarde esto, lo haremos paso a paso, haremos dos funciones una llamada storeDbase y otra llamada loadDbase que harán el trabajo, por supuesto storeDbase guardara los datos y loadDbase los leerá, pero vamos paso a paso.
Aquí tenemos antes que nada la comparación en la cual entrará si funciona como modulo principal, en ese caso, importará la database db y la guardará a través de la función storeDbase, para que yo pueda llamar en forma separada al script y cuando lo haga este guarde los datos directamente, lógicamente el script tiene ambas funciones para guardar y cargar, que pueden ser importadas y llamadas desde otros módulos. Como en el script anterior chequea que __name__ sea __main__ lo que significa que el script esta siendo ejecutado directamente si es tasi, importa la database db del modulo initdata, y luego lo guarda a traces de la función llamada storeDbase.
Allí vemos la función que guardara los datos, vemos que tiene dos argumentos, pero uno solo es obligatorio que es db o sea la database que guardara, el otro es un argumento que tiene un valor por default, o sea que si no le pasamos valor, lo guardara al archivo con el nombre que tenga la variable dbfilename, y si queremos podríamos llamar a la función pasándole otro segundo argumento desde algún lugar del programa, para que guarde con otro nombre los datos, pasándole el segundo argumento diferente al valor que tiene por default. dbfile = open(dbfilename, 'w') La función aquí crea o abre el archivo para escribirlo, y crea un objeto archivo para ello en la variable dbfile.
Colocamos en el WING como archivo para debuggear o DEBUG MAIN FILE a make_db_files.py y luego ponemos un breakpoint allí como se ve en la imagen.
Vemos que cuando para ya creo el archivo vacío para escribir en el
Recordemos que db es un diccionario que tiene tres keys principales que son los nombres de los empleados bob, sue y tom.
Si queda alguna duda siempre se puede ir al DEBUG PROBE y mirar .
Allí están las keys marcadas, ya que estamos aquí podemos ver como curiosidad, que hay una pestaña en el WING que me muestra el CALL STACK o sea de donde fue llamada la función en donde estamos ahora, similar al call stack de ollydbg para los que lo conocen.
O sea lo que significa esa información es que nosotros estamos en la linea 16 del script, pero esta función en que entramos fue llamada desde la linea 26 de make_db_files.py exactamente en la instrucción storeDbase (db) que fue donde llamo a la función y entro a la misma.
Allí la variable key, tomara el primer valor que sera bob, que es una de las keys del diccionario, y luego por medio del comando print >> la guardara al archivo ya sabemos que >> desvía la salida de un print de la consola, a guardar en el archivo que esta abierto. No debemos asustarnos por este comando, cuando lo vemos debemos pensar que lo que imprimiría en la consola, lo guardara en el archivo asi. print >> dbfile, key Si quiero saber que guardara, escribo en el DEBUG PROBE print key
Y me mostrara lo que escribe al archivo de salida. print >> dbfile, key Lo que hace es imprimir eso mismo en el archivo al cual el objeto dbfile apunta, en este caso al archivo, que tendrá el nombre dado por la variable dbfilename o sea people-file Quiere decir que por ahora estaría guardado 'bob', aunque aunque no se puede ver hasta que no se cierre el archivo, si lo abro se vera vacío porque esta en uso, pero mirando en el DEBUG PROBRE como he hecho puedo saber lo que va escribiendo. Luego tenemos un for que imprimirá el resto de los datos, en este caso de bob, por si no recuerdan lo que hace el metodo items(). .items() devuelve una lista de tuplas (clave, valor) de todo el contenido del diccionario. ejemplo: >>> palabra = {'hola':33, 'chau':5} >>> palabra.items() [('hola', 33), ('chau', 5)] Por lo tanto en nuestro caso devolverá una lista de tuplas, del contenido del diccionario bob.
Asi que entonces el for lo que hace es crear dos variables que tomaran los valores de cada key y valor asociado de esta tupla. for (name, value) in db[key].items( ): O sea que el name o nombre en el primer ciclo del loop sera job y dev, ya que son el primer par de la tupla.
el valor sera
print >> dbfile, name + RECSEP + repr(value) Ahora llegamos al print que enviara mas datos al archivo, por supuesto el >> dbfile, es para desviar al archivo, lo que imprimirá sera el contenido de name luego la flecha RECSEP (recordemos en la definición de variables RECSEP = '=>')y luego repr(value). Las funciones str () y repr (), son muy parecidas
Vemos que repr () le agrega las comillas, nos devuelve la string en la que puede convertir un objeto, salvo muy pocas excepciones es muy parecida a str(). Bueno podemos verificar en el DEBUG PROBE lo que guardara. print
name + RECSEP + repr(value)
aquí se ve la diferencia de cuando lo haces con str()
No agrega las comillas y queda mas feo jeje, por eso usa repr().
Una vez que realiza todo el loop y imprime todo el diccionario de bob, salta aquí
Donde imprime ENDREC, como habíamos visto lo usaríamos para separar entre los datos de los distintos empleados, luego repite el for para todos los otros empleados y cuando termina salta a
Donde escribe el ENDDB o sea el final de la database y cierra el archivo, sale de la función, y vuelve al caller donde imprime 'STORED'.
Ahora podemos verificar si el archivo que se creo, es como pensamos, podemos abrirlo ya que ya cerramos con close(), en nuestro script.
Vemos que todo salio como lo ideamos, escribió los ENDREC, luego de que escribiera los datos de cada empleado y ENDDB al final de escribir toda la database. Bueno la primera parte del modulo ya esta ahora solo queda la función para leer que sera un poco mas compleja. (mas?, jeje) Y si es un poco mas compleja, esta función no se ejecutara cuando el modulo corra como main, ya que en ese caso solo guarda los datos, pero la función puede ser importada por otros módulos para leer los datos.
Aqui vemos la función completa que carga los datos y reconstruye la database, como vemos los ENDDB y ENDREC no eran solo cosméticos, son usados en la función para leer datos mientras no se alcance ese valor, igual lo explicaremos en detalle a continuación. Podemos debuggearla, aunque no tengamos hecha una llamada a la misma, se la colocaremos provisionalmente para probar como funciona.
Le agregamos allí al final una llamada a la función, a continuación de que guarde e imprima 'STORED' para probar si funciona y debuggear, luego borraremos estas lineas que son de test, la función sera llamada desde otros módulos. Bueno coloquemos un breakpoint allí y corramos hasta que llegue a la llamada a la función y luego ingresemos a la misma con f7.
Vemos que abre el archivo para lectura, y crea la variable dbfile que sera el objeto archivo, correspondiente al archivo con el nombre dbfilename el cual esta puesto como argumento por default, pero también se podría pasar otro nombre como argumento para leer otro archivo creado con este mismo formato.
Bueno luego de abrir el archivo y crear el objeto dbfile, importa el modulo sys.
Bueno python provee tres objetos standard, que manejan las entradas y salidas algo vimos en la etapa anterior pero antes de continuar, aclararemos bien el punto, así no quedan dudas sobre stdin, stdout y stderr y luego continuaremos con la tarea asignada. El primero stdin es un objeto, que maneja los ingresos de las entradas de caracteres o input que se envían al interprete, stdout recibe los mensajes de salida producidos por print y stderr, recibe los mensajes de error.
import sys def gets(): text = "" while 1: c = sys.stdin.read(1) text = text + c if c == '\n': break return text a=gets() print a Allí vemos un trozo de código de ejemplo que utiliza sys.stdin con el método read para recibir lo que se tipea en la consola e ir agregándolo a una variable text, saldrá del loop cuando apretemos enter. La función raw_input lee una linea de texto de la variable stdin. s = raw_input("type something : ") print "You typed '%s'" % (s,) Alternativamente stdin, stdout y stderr pueden ser reemplazados por otros objetos solo con una
asignación, por lo tanto en el programa cuando vemos. sys.stdin = dbfile Significa que el objeto dbfile, tendrá las mismas características de stdin de recibir lo que se tipea, además de ser un objeto archivo que esta abierto. Por lo tanto eso es lo que hace allí:
Luego crea un diccionario vacío db={} para ir guardando lo que lee. La siguiente linea key = raw_input( ) es como si hiciéramos un raw_input que espera la string que tipeamos, en este caso, lo que hace es como esta desviado, leerá del archivo dbfile, la primera string que encuentre, veamos si es así.
Vemos que asi fue al desviar dbfile a stdin, se comporta el archivo como si tipearamos una string en la consola, y la primera string que hay es 'bob', la cual se asigna a la variable key. Luego hay un WHILE , el cual se repetirá mientras que key sea diferente de ENDDB o sea mientras no termine la database ya que ENDDB estaba al final de la misma en el archivo, seria la condición de salida.
O sea que se repetirá hasta que llegue allí al final.
Crea otro diccionario vacío llamado rec y en la siguiente linea hace raw-input de la próxima string del archivo y la asigna a la variable field.
Allí vemos que toma la string “job =>'dev'”y la asigna a field. Ahora hay un segundo WHILE que se repetirá mientras field sea diferente de ENDREC o sea mientras estemos en los datos del primer empleado en este caso 'bob'. name, value = field.split(RECSEP) aquí lo que hace es dividir la string “job =>'dev'”, en dos usando como separación la flecha o sea que dividira lo que esta a ambos lados de la flecha y lo asignara en este caso name= 'job' y value='dev', veamos si es así.
Esto fue lo que ocurrió
Aunque value tiene “”dev”” con doble string pero eso lo solucionamos facil. rec[name] = eval(value) Allí crea el primer campo en el diccionario rec, con la key que esta en name o sea 'job' y el valor relacionado el que sale de eval (value) lo cual le quitara las comillas repetidas y le dejara una sola como corresponde.
Allí vemos como va creando el diccionario, luego actualizara el valor de field con la siguiente sring que lee de raw_input y creara el segundo campo, y asi consecutivamente hasta que encuentre el final de los datos de bob cuando field= ENDREC, allí sale y asigna a db [key] o sea db['bob'] el diccionario rec que creo con los datos del mismo, y vuelve a buscar el siguiente empleado y así hasta que llena todo db vemos cuando termina.
Y llega al print 'LOADED' al salir de la función podemos ver en el DEBUG PROBE como quedo db.
Vemos que quedo como queríamos y armo la database de acuerdo a lo que leyó del archivo que teníamos guardado sin problemas.
Le borramos esas dos lineas que solo las colocamos para probar y ya tenemos terminado el modulo que lee y guarda los datos en un archivo. MODULO make_db_files.py ---------------------------------------------------------------------------------------------------------------------dbfilename = 'people-file' ENDDB = 'enddb.' ENDREC = 'endrec.' RECSEP = '=>' def storeDbase(db, dbfilename=dbfilename): "formatted dump of database to flat file" dbfile = open(dbfilename, 'w') for key in db: print >> dbfile, key for (name, value) in db[key].items( ): print >> dbfile, name + RECSEP + repr(value) print >> dbfile, ENDREC print >> dbfile, ENDDB dbfile.close( ) def loadDbase(dbfilename=dbfilename): "parse data to reconstruct database" dbfile = open(dbfilename) import sys sys.stdin = dbfile db = {} key = raw_input( ) while key != ENDDB: rec = {}
field = raw_input( ) while field != ENDREC: name, value = field.split(RECSEP) rec[name] = eval(value) field = raw_input( ) db[key] = rec key = raw_input( ) return db
if __name__ == '__main__': from initdata import db storeDbase(db) print 'STORED' Hasta la parte siguiente donde veremos mas módulos de nuestro programa los que iremos agregando y explicando paso a paso. Ricardo Narvaja
03-2DA ETAPA DEL CURSO DE PYTHON-INTRODUCCION-MODULOS COMPLEMENTARIOS Bueno ya explicamos 2 de los módulos principales del programa ahora agregaremos algunos mas que hacen falta para complementar el funcionamiento, creemos uno nuevo en el WING y pongamosle como nombre dump_db_file.py.
Vemos que es un modulo que cuando se lo llama importa la función loadDbase de make_db_files y como la misma retorna un valor que es la database, lo asigna a la variable db. Luego imprime las keys y sus valores relacionados y además tiene un print como ejemplo para ver si muestra bien los datos por ejemplo el nombre de 'sue', si todo esta bien armado, lo debería mostrar correctamente veamos. Ejecutemoslo, le agregue un input () al final para que no se cierre, pero como fue desviado el input me da error, así que lo ejecuto desde una consola.
Vemos que imprimió correctamente todo y mostró como ejemplo el nombre de sue.
Como vemos es sencillo, lo que hace es agregar la llamada a la función loadDbase e imprimir la salida . El siguiente modulo es para updatear datos o modificarlos veamos es pequeño también.
Vemos que mediante loadDbase lee la database archivada, y luego realiza modificaciones a la misma, allí como ejemplo le aumenta el 10% a 'sue' del salario y cambia el nombre a 'Tom', veamos si funciona.
Alli updatee los datos, para verificar puedo llamar al script anterior dump_db_file.py ya que ese mostraba el contenido de la database a ver si cambio.
Allí están las modificaciones si comparamos con la imagen de arriba, vemos que cambio el nombre y apellido de Tom y el sueldo de Sue. Bueno este esquema que hemos realizado funciona pero tiene muchas limitaciones, por un lado tiene que leer toda la database entera solo para mostrar un valor y tiene que escribir toda la databse entera cuando haces un update, creo que podemos considerar lo que hemos hecho hasta aquí como una primera aproximación y mejorarlo. Otra falla de nuestro programita es que si llega a aparecer el carácter => entre los datos el script fallara pues dividirá allí los mismos, cuando los lee, seria bueno tener una herramienta general ya preparada para trabajar con datos de python que puedan ser guardados a un archivo en un solo paso. Por lo demás vemos que para hacer modificaciones debemos cambiar un modulo, lo cual no es practico para usar en el día a día. Eso lo veremos en la parte siguiente por ahora terminamos la parte inicial de nuestra tarea, ahora en la siguiente mejoraremos e inclusive le agregaremos al final una GUI para interactuar con los datos, ya verán. Hasta la parte 4 Ricardo Narvaja
04-2DA ETAPA DEL CURSO DE PYTHON-INTRODUCCION-USANDO PICKLES El módulo pickle implementa un algoritmo básico pero potente que serializa objetos Python casi arbitrarios. Esto se define como el acto de convertir objetos a una cadena de bytes y viceversa. Como el modulo pickle conoce como reconstruir el objeto a partir de la cadena de bytes, podremos reconstruirlo fácilmente y nos servirá para guardar y carga la database. Si recordamos lo complejo que fue realizar el modulo make_db_files.py, con sus dos funciones, y debuggearlo para entenderlo veremos que el manejo de los mismos datos usando pickles es mucho mas sencillo. aquí vemos como era el viejo make_db_files.py
Crearemos un nuevo modulo que se llamara make_db_pickle.py, y sera muy sencillo y breve.
Vemos que el funcionamiento es muy sencillo, importa la database db existente y importa el modulo pickle, luego abre el archivo `people-pickle' para escritura, y con el método dump, que solo hay que pasarle dos argumentos, los datos de la database db, y el segundo donde los guardara, y con eso ya crea el archivo en su propio formato, luego se cierra el archivo con close(), vemos que de esta forma nos evitamos de chequear separaciones y fines de datos, al usar pickle, el se encarga de todo y con los métodos que tiene incluidos, podemos recuperar los datos sin tener que volvernos locos. Veo que si ejecuto este modulo, crea el nuevo archivo database.
Allí esta si veo el formato no es tan inteligible como el de texto que nosotros habíamos realizado, miremos un poco.
Allí esta, importa poco que nosotros entendamos la forma en que lo guarda, pickle lo comprende y usando sus métodos podremos manejar la información fácilmente. Este script make_db_pickle.py realiza el mismo trabajo que el make_db_files.py y ahora haremos la versión de dump_db_file.py con pickles se llamara dump_db_pickle.py, creamos un nuevo script vacio y le ponemos dicho nombre.
Vemos que es muy sencillo este modulo, importa pickle, abre el archivo para lectura y con el método load (dbfile) que posee pickle, lee y reconstruye el archivo y lo asignamos a la variable db y lo imprimimos fácilmente, igual que antes, si corremos este modulo desde linea de comandos.
Allí vemos el resultado, la database db fue fácilmente reconstruida e imprimimos los datos igual que antes sin tener que complicarnos tanto, usando los métodos que ya vienen incluidos para pickle, el cual se ocupa por nosotros de guardarlo en un formato, y levantar los datos del mismo. El ultimo que queda reemplazar es el modulo update_db_files.py por el update_db_pickle.py, recordemos que este era el modulo encargado de actualizar datos. Creamos el modulo nuevo con dicho nombre.
Vemos que es muy sencillo de manejar, pues importamos pickle, abrimos el archivo y con el método load de pickle, cargamos los datos a la database db, y cerramos el archivo, luego updateamos los datos y al final reabrimos el archivo para escritura, y con el método dump de pickle lo guardamos y cerramos el archivo, veamos si funciona.
Allí vimos que primero realizamos el update de los datos, otorgando el aumento de sueldo y cambiando el nombre y apellido de Tom y luego llamando al modulo dump_db_pickle.py los muestra y vemos el aumento y el nombre y apellido de Tom cambiados. Vemos que en esta versión no solo simplificamos los módulos, si no que los datos se escriben y leen en forma muy sencilla, aunque aun tenemos el problema que al actualizar tenemos que escribir nuevamente toda la database al archivo, lo cual para databases muy extensas puede ser molesto, una forma de evitar eso serian estos ejemplos que guardan las databases de cada empleado en un archivo pkl. Para ello puedo cerrar todas las otras pestañas que tenia abiertas porque total si algún modulo las usa, aunque estén cerradas las llamara, solo dejare abierta la initdata.py, que es la base y las nuevas que vaya creando, en este caso make_db_pickle_recs.py.
Vemos que este modulo importa los diccionarios de initdata y en un for la variable key toma los valores de los nombres de los empleados, y la variable record el diccionario con los datos de cada uno. Luego se abre un archivo cuyo nombre se compone del nombre del empleado que esta en la variable key y a eso se le agrega la extensión pkl. En la siguiente linea con el método dump de pickle se crea un archivo para cada empleado, y luego se cierra.
Allí esta uno de los archivos ahora nos quedan actualizar los otros dos módulos dump y update, el modulo dump se llamara dump_db_pickle_recs.py.
Lo que hace es importar pickle y glob, si quiero al ayuda en WING sobre glob puedo poner el
cursor encima y apretar f4.
O sea que devuelve una lista con los archivos de extensión pkl que encontró, y filen ame tomara uno a uno los nombres de los archivos pkl, en el flor. Luego abre el archivo y con el método load de pickle lo carga a una variable record que imprime, junto con el nombre del archivo, luego abre el archivo de sue, y con load devuelve la database de sue, y alli mismo le aplica la key ['name'], para imprimir el nombre, veamos si funciona. Ya habiendo ejecutado make_db_pickle_recs.py antes y estando creados los tres archivos ahora ejecutamos dump_db_pickle_recs.py en una consola.
Allí vemos la salida en este caso, como vemos muestra el nombre del archivo y su contenido, ahora faltaría el modulo update, este se llamara update_db_pickle_recs.py. Vemos en la imagen que ahora para actualizar los datos de Sue, solo es necesario, abrir el archivo correspondiente a sus datos y actualizarlo, no toda la database.
Allí abre sue.pkl ylo carga con el método load de pickle, y luego al campo pay le incrementa el 10 % y le aplica el método dump de pickle para guardarlo, veamos si funciona.
Allí vemos en la consola, como actualizamos los datos con el modulo update nuevo y como los vemos con el nuevo modulo dump. En la siguiente parte veremos un nuevo método para guardar, que mejorara lo que hemos hecho hasta ahora. Hasta la parte 5 Ricardo Narvaja
05-2DA ETAPA DEL CURSO DE PYTHON-INTRODUCCION-USANDO SHELVES o ESTANTES Utilizar pickes como vimos en la parte anterior, es muy extendido en muchísimas aplicaciones en python, muchas aplicaciones usan pickling de objetos python a través de la red . Siempre que nuestro sistema no tenga limitaciones en cuanto a la cantidad de archivos que podemos crear, pickles, nos ayuda a que cuando tengamos que updatear los datos de un solo empleado como en nuestro ejemplo, no tengamos que guardar toda la database nuevamente y solo cambiar el archivo correspondiente a dicho empleado. Si nosotros queremos mejorar este sistema python nos ofrece un sistema de manejar archivos por medio de keys llamado “shelves”, o mas o menos traducido seria “estantes”. El comportamiento de shelves se semeja bastante a los diccionarios, pero a diferencia de estos persiste aunque cerremos el programa ya que se guarda en un archivo, además como nos da un acceso por keys a los records guardados, no es necesario manejar una fila por record, todo se accede a través de cada key tal cual se hace en los diccionarios. La interfase de shelve es tan sencilla de manejar como pickle y muy parecida a los diccionarios en los ejemplos veremos que el manejo es muy fácil.
En el wing cerremos todas las pestañas y dejemos solo la initdata.py y agreguemos una nueva que se llamara make_db_shelve.py.
Vemos que al abrirlo con shelve.open al nuevo archivo 'people-shelve', directamente la variable db se convierte en un diccionario abierto, que cualquier asignación que le hagamos como en este caso db ['bob']=bob, como db es un diccionario, pero lo que asigna lo guarda en el archivo al momento
de hacer close(). Por supuesto trajimos los datos de bob y de sue, los cuales incluimos en el diccionario mediante la asignación tal cual un capo de un diccionario, con su key.
Allí corrí desde la consola este modulo y veamos si creo el archivo.
Allí esta vemos que crea un solo archivo, veamos si se ve el contenido con el bloc de notas arrastrándolo allí. (por supuesto podemos hacer esto si ya lo cerramos con close() no antes)
Vemos que el contenido no es entendible fácilmente, pero no importa ahora sigamos con los otros dos módulos, para poder imprimir y ver el contenido hacemos el segundo modulo dump_db_shelve.py.
Vemos que imprimirlo es muy sencillo, ya que solo con el método open de shelves, ya tenemos abierto db como si fuera un diccionario, leemos del archivo y allí haciendo un for imprimimos las keys y el valor asociado db [key] y además como ejemplo imprimimos también el nombre de sue por medio de las keys correspondientes, probemoslo ejecutando desde una linea de comandos.
Vemos que el manejo es mas sencillo que pickles, si sabemos manejar diccionarios, manejaremos shelves de la misma forma, solo hay que usar el método open de shelves para abrirlo, cambiar lo que deseemos en el diccionario o asignar lo que deseemos y luego cerrarlo con close. Ahora el ultimo modulo de shelves es el update, también es muy sencillo lo llamamos update_db_shelve.py
Aquí importamos a tom de initdata porque no lo habíamos agregado en el archivo, solo a bob y a sue, asi que lo agregaremos aquí, luego importamos shelve abrimos con open de shelve y queda en
db el diccionario, alli leemos los datos de sue, luego mediante la key llamada ['pay'], le aumentamos el 50 % y luego volvemos a asignar a db ['sue'] con su nuevo valor updateado , luego asignamos un nuevo empleado con db ['tom']=tom y cerramos, si lo ejecutamos desde la linea de comandos.
Primero hacemos el update y luego para ver el resultado ejecutamos el dump, y vemos que agrego a tom y aumento el sueldo de sue en un 50%, jejeje. Bueno hemos visto diferentes formas de hacer lo mismo, y hemos llegado al uso de shelves que es bastante interesante aunque aun tiene un problema con el uso de shelves dar aumentos y imprimir un apellido se realiza con código como este. >>> import shelve >>> db = shelve.open('people-shelve') >>> bob = db['bob'] >>> bob['name'].split( )[-1] 'Smith' >>> sue = db['sue'] >>> sue['pay'] *= 1.25 >>> sue['pay'] 75000.0 >>> db['sue'] = sue >>> db.close( )
# get bob's last name
# give sue a raise
Lo cual es sencillo para programas con databases pequeñas y programas cortos, ahora si esto lo realizamos en un programa largo y tenemos este tipo de código suelto por ahí, sera difícil localizarlo, sera como un código mágico disperso por ciertas partes del programa. Otro problema que tienen los diccionarios es que si cambian los tipos de datos por algún cambio en la compañía, que varíe la forma de manejarlos, el programa tiene que ser modificado completamente sin la posibilidad de ser flexible a cambios o a updatearlo según las necesidades. Es entonces cuando OOP o programación orientada a objetos viene en nuestra ayuda, en las siguientes partes veremos como implementarlo con OOP para nuestro programa. Hasta la parte 6 Ricardo Narvaja
06-2DA ETAPA DEL CURSO DE PYTHON-INTRODUCCION-USANDO OOP Realmente usar OOP trae innumerables ventajas, entre ellas la facilidad de customización, sabemos que podemos importar clases y cambiar o agregar métodos o atributos en ellas sin problemas, que siempre tomara nuestros cambios, respetando la clase original, y podremos usar tanto la clase original o la customizada como vimos en los ejemplos de la etapa 1. Como vimos, una clase puede heredar o ser heredada, esto permite muchos cambios y personalizaciones interesantes, le quita rigidez al programa y veremos que es muy sencillo. De todo lo que hemos visto hacia atrás solo utilizaremos shelves para guardar en el archivo, ya que vimos que es la forma mas sencilla y cómoda y con la cual podemos cambiar y asignar nuevos datos fácilmente. Cerramos todas las pestañas existentes en el WING ya que crearemos lo necesario desde cero, y creemos un nuevo script que se llamara person_start.py.
Lo primero es crear una clase llamada Person, que tiene un método constructor __init__ que se ejecutara cuando se instancien objetos de esta clase. class Person: def _ _init_ _(self, name, age, pay=0, job=None): self.name = name self.age = age self.pay = pay self.job = job Allí vemos que cuando se realice la instancia, como es el método constructor y se ejecutara, habrá que pasarle los argumentos que pide para ello, algunos son obligatorios, otros no, así como esta definida, los obligatorios son los dos primeros, el nombre name y la edad age (recordemos que el self no se cuenta), y luego hay dos mas que son el pago pay que tiene un valor por default que es
cero, y el trabajo o job cuyo valor por default es None ( por supuesto si pasamos otros valores al instanciar, reemplazaran los que trae por default). Así que podemos debuggear clickeando en STEP INTO, a ver como funciona y de paso practicar un poco. No nos olvidemos de en PROJECT, agregar el archivo nuevo y hacer click derecho SET AS MAIN DEBUG FILE, si no quedara debuggeando el anterior, aunque hayamos sacado la pestaña de la vista.
Allí esta apretemos f7, para ingresar a tracear.
Estoy en el class ahora apreto f7.
Sigo
Como habíamos visto una vez definida la clase, no entra a ejecutar dentro de la misma, eso solo se hará cuando se instancien objetos de esa clase, ahora testea si el script esta siendo ejecutado como principal, o sea si __name__ es igual a __main__ entra dentro del if, a realizar el trabajo de instanciar.
Aquí tenemos la primera instanciación, será por lo tanto bob una instancia de la clase Person, al apretar f7, ira a ejecutar el método constructor __init__, pasándoles los argumentos ('Bob Smith', 42, 30000, 'sweng'), si apreto f7 nuevamente.
. Luego el método __init__ asigna a bob.name (pues self corresponde a la variable bob en este momento) el argumento 'Bob Smith', Por lo tanto serà bob.name='Bob Smith', lo mismo asigna bob.age=42, bob.pay=30000 y bob.job='sweng'. Cuando termina las asignaciones sale del método y vuelve a la siguiente instrucción como se ve
aquí en la imagen siguiente.
Comprobemos en el stack data si las asignaciones fueron hechas en la forma que dijimos.
Vemos que todo es correcto, ahora hará lo mismo con sue, la variable sue sera una instancia de la clase Person y asignara los valores de sue.age, sue,name, sue.job y sue.pay de la misma forma, así que no necesitamos entrar a ver lo mismo, así que hacemos un step por encima o STEP OVER con f6, el que quiera puede entrar a tracearlo con f7 vera lo mismo que cuando lo hicimos con bob.
Luego imprime el nombre de bob o sea bob.name y el pago de sue,o sea sue.pay
Esto solo lo realiza cuando el modulo es ejecutado como main, si el modulo es importado, no hará este trabajo de dentro del if. Quiere decir que este modulo me creara las clases, y me instanciara cada empleado en la clase Person, teniendo una vez ejecutado, los datos ya guardados en los atributos de la clase, en este caso, como ejecutar el método constructor es obligatorio, sabemos que en las variables internas estarán los datos de cada empleado.
Y luego ya saliendo del if o sea siempre que se ejecute, sea importado o como main, imprimirá el apellido de bob, ya que hace split para separar el apellido del nombre, y aumenta el pago de sue nuevamente en un 10%.
Allí le dio el aumento de sueldo y termina creo que es sencillo, jeje. Otra de las mejoras que tenemos usando clases. es la posibilidad de usar un valor por default, para ciertos datos, como en este caso el pago y el trabajo, mientras que con diccionarios eso no se puede realizar. Nuestra primera versión del modulo Person que acabamos de hacer, y llamamos person_start.py a pesar de que implementa clases, solo tiene atributos o variables internas, pudiendo tener mas métodos que realicen ciertas acciones que nosotros estamos realizando fuera, de esta forma sera mucho mas fácil llamar a estos métodos, por ejemplo podemos agregarle un método para aumentar sueldo y otro para separar el apellido. Veamos esta nueva versión mejorada, la llamaremos person.py, así que quitamos la anterior de la vista jeje cerrando la pestaña.
Vemos que no ha cambiado mucho, al igual que antes el método constructor, cuando se realiza la instanciación, se ejecutara y asignara los datos a las variables internas self.name, self.age etc, pero ahora además de eso tenemos, dos métodos mas uno llamado lastName(), que nos realizara el split que antes hacíamos fuera y mediante el return nos devolverá el apellido, que podremos asignar a una variable o imprimirlo. El otro método es giveRaise(), a este hay que pasarle un solo argumento, que es el porcentaje que queremos aumentar, así la cuenta de multiplicar self.pay por 1.0 + el porcentaje a aumentar, se realizara dentro de este método, y no devolverá valores, pues ya asigna dentro del mismo el nuevo valor aumentado. Vemos que abajo, para probar que funcionan estos métodos que agregamos, hay un print bob.lastName(), que imprimirá el apellido de bob y un sue.giveraise (.10), que otorgara un 10 % de aumento a sue, y se imprime luego el valor del pago de sue para corroborar. Probemoslo.
Vemos que funciono, imprimió el apellido de bob y el salario aumentado de sue, usando los métodos que agregamos, ya vemos que de esta forma, consultar datos sera mas sencillo, y podremos agregar los métodos, que queramos que manejen los datos en la forma que se nos ocurra, para llamarlos desde cualquier punto del programa, facilitando el trabajo. Es mucho mas sencillo y claro, agregar métodos que puedan ser llamados desde cualquier punto del programa, que una lógica harcodeada, que nadie recordara y puede estar en cualquier parte del programa sin control. AGREGANDO HERENCIAS Realizaremos otro modulo diferente que agregara algunas cualidades importando el anterior, asi que dejamos el anterior en una pestaña y agregamos otra nueva en este caso la llamamos manager.py. Con este explicaremos como , sin cambiar la clase original Person, podemos customizarla a nuestra medida para una cierta clase especial de empleados en este caso los managers.
Vemos que este modulo importa la clase Person del modulo anterior, y luego crea una subclase llamada Manager que heredara todos los métodos y atributos de esta, pero en este caso, los que
sean instanciados como Manager, usaran el método giveRaise () propio de esta subclase, agregando un bonus, o sea que a los mánager, además de computarsele el aumento como a todo el resto de los empleados tienen un bonus de 0.1 mas de aumento que el resto. También vemos que cuando se ejecute este modulo como principal se instanciara a Tom como Manager, pasando los argumentos necesarios para el método constructor obligatorio que hereda de la clase Person, y luego imprime el apellido de Tom, usando el método lastName() de Person, y da el aumento usando su propio método, en el cual incluye un bonus para los empleados instanciados como managers. Corramoslo para ver si funciona.
Perfecto, otorgó el aumento con bonus para el manager Tom, usando su método y imprimió su apellido usando el método que heredó de Person. Aquí vemos que la clase Manager fue agregada en un modulo aparte, pero podría haber sido agregada en el mismo modulo person.py, no es necesario que una clase tenga modulo propio, pero como es una customización, quedo mas claro explicarlo en esta forma. Como esta subclase tiene su propio método para los empleados que son managers, a los cuales les otorga el famoso bonus, el resto de los empleados como bob y sue que no son managers, continuaran cobrando el aumento según la clase en la cual se instanciaron o sea en Person y no tendrán bonus (jejeje), esto es el famoso polimorfismo de python, giveRaise() dará diferentes aumentos según donde se instancio el objeto que esta llamando al método, los empleados que son instanciados como Person, giveRaise() no les dará bonus y a Tom y a los managers que se instancien en la clase Manager si les dará. Vemos que igual hemos cometido redundancia en nuestro código, realizando dos veces el calculo del aumento cuando ya existía un método que lo hacia, lo mas lógico hubiera sido en Manager, llamar a ese método existente. class Manager(Person): def giveRaise(self, percent, bonus=0.1): Person.giveRaise(self, percent + bonus) De esta forma cuando se llama al método giveRaise() de la clase Managers, en vez de realizar nuevamente la cuenta, solamente le suma el bonus a la variable percent y llama al método existente de la clase Person.
Sabemos que podemos llamar a un método de una clase en particular, con el formato Nombre de la clase.METODO (args) De esta forma podemos llamar a ese método en particular, luego de modificar el argumento agregando el bonus. MAS DIVERSION DE OOP Aclaro que esta parte la veremos solo como diversión, luego continuaremos usando los módulos person.py y manager.py que hicimos hasta aquí. Podemos sobrescribir __str__ de forma que cuando se mande a imprimir el nombre de alguno de los empleados, ya se imprima con un formato en particular, veamos. Recordamos que los métodos __XX__ servían para definir y sobrescribir comportamientos de python para los objetos standard, para que en nuestra clase se comporten de otra forma, aquí haremos que el imprimir una string sea customizado. El método __str__, define el aspecto que quiere tener un objeto si se le trata como una cadena, y al enviarlo a imprimir, lógicamente lo estamos tratando como cadena, y se vera como definamos dentro del mismo. class Person: def __str__(self): return ' %s>' % (self.__class__.__name__, self.name) tom = Manager('Tom Jones', 50) print tom
# prints: Tom Jones>
Vemos que al agregarle el método __str__, cuando mando a imprimir el nombre de tom, directamente el método me devuelve Tom Jones>, que es la forma en que se verá. self.__class__.__name__ Esta variable me devuelve el nombre de la clase que se esta ejecutando, en este caso Manager y en este caso tom.name me devolverá el nombre y apellido de tom, y por medio de los format string se reemplazan los %s, como siempre para armar el formato de salida. También vemos que cuando instanciamos a tom, no pasamos el trabajo que realiza pues como es manager o sea no trabaja, jeje chiste , digo que trabaja de eso, o sea de manager y asignarle otro trabajo es redundar. Así que podemos crear un propio método constructor para los managers, que directamente customice eso. class Manager(Person): def _ _init_ _(self, name, age, pay): Person._ _init_ _(self, name, age, pay, 'manager')
Vemos que ahora al instanciar un manager, usara su propio método constructor en vez del de Person, aunque lo que hará sera directamente agregarle el parámetro 'manager' siempre, y llamar al método constructor de la clase Person con los mismos datos, solo agregando manager como job.
Para demostrar como quedarían en un modulo estas ideas, crearemos un modulo llamado peoplealternative.py que no usaremos finalmente, pero nos sirve para probarlas.
Allí vemos la definición de la superclase Person que como antes, tiene su método constructor que asigna las variables, y los métodos lastName(), para devolver el apellido y el método giveRaise() para dar aumentos. Ademas le agregamos el método __str__, que customiza los prints.
Luego esta la subclase Manager que hereda la clase Person y tiene su propio método constructor ya vimos que agrega job='manager' antes de llamar al método constructor de Person. También Manager tiene su propio giveRaise(), que agrega el bonus antes de llamar a giveRaise() de Person. Luego están las instancias de bob y sue en la clase Person y Tom en la clase Manager, vemos que allí en la instanciación de Tom no hay que pasar el trabajo o job, pues lo agrega su propio método constructor. Luego imprime sue, el sueldo de sue y el apellido de sue. Y finalmente luego hace un for donde la variable obj, toma los nombres de los objetos bob, sue y tom , y a cada uno le da un aumento del 10%, y imprime el contenido de obj, por supuesto a tom le dará el bonus al realizar el aumento. Probemos a ver si funciona.
Vemos que la forma de imprimir funciono customizada ya que al mandar imprimir aquí.
Al imprimir sue, lo hace en la forma customizada Sue Jones: music, 40000> Luego imprimirá sue.pay que es 40000, y el apellido que es Jones, y luego las otras tres lineas son las formas customizada de imprimir bob, sue y tom, al cual vemos que le pone Manager.
Vemos también que a tom le otorgo mas del 10% de aumento general gracias al bonus otorgado a los managers, este ultimo modulo solo lo hicimos como demostración de las posibilidades que podríamos agregar, y divertirnos un rato y practicar, así que continuaremos trabajando con los que teníamos hechos person.py y manager.py que son mas simples y cumplen bien su cometido. GUARDANDO LOS DATOS Para guardar los datos, haremos un nuevo modulo make_db_classes.py que hará ese trabajo y seguiremos usando los módulos person.py y y manager.py, así que los abrimos nuevamente.
Allí vemos que el nuevo modulo importara shelve, para guardar el diccionario como habíamos visto, y importa las clases Person y Manager de los módulos correspondientes, recordemos que habíamos hecho una clase Person que era la superclase y la clase Manager hereda de esta sus atributos. Allí estaba la clase Person
Y aquí estaba la clase Manager
Así como las habíamos creado inicialmente, sin ninguna floritura y en módulos separados.
Allí realiza la instanciación de bob y sue en la clase Person y tom en la clase Manager pasándole los datos necesarios. Luego abre la database db con el método open() de shelve creando el archivo para guardar, y como es un diccionario le agrega los campos db ['bob']=bob y repite para cada empleado, armando los diccionarios de la database, y luego cuando esta todo armado, cierra el archivo con close(), quedando guardado en el archivo los datos de los empleados.
De esta forma los datos son guardados en forma de diccionario, pero a diferencia de como lo hacíamos anteriormente, el valor asociado a la key con el nombre del empleado, es una instancia de la clase Person o Manager según el caso. Probemoslo a ver si funciona.
Luego de correrlo vemos que se creo el archivo de datos, para ver el contenido, nos falta un modulo que imprima los datos al que llamaremos dump_db_class.py.
Vemos que también es muy sencillo, importa shelve, y luego abre con open() de shelve el archivo y
lo asigna a una variable db. Y luego imprime en un for las keys, el nombre y el pago. Luego ya fuera del for, imprime luego el apellido de bob y el de tom, probemos.
Allí imprime lo que esperamos.
Nos quedaba un modulo mas que era el update, lo llamaremos update_db_class.py.
También es muy sencillo, importa shelve, luego abre el archivo con el open() de shelve, y lo asigna a una variable llamada db que sera el diccionario, y luego lee los datos de sue, y le da un .25 de aumento (recordar que solo era necesario pasarle la parte decimal ya que el 1.0 estaba dentro del método giveRaise(), luego lee los datos de tom y le da un 20 por ciento, y cierra el archivo guardando los cambios, veamos si funciona.
Allí ejecute el update y luego use el dump para ver si funcionó el aumento, lo cual se verifico perfectamente. En la parte 7 seguiremos explorando mas posibilidades para nuestro programa. Hasta la parte 7 Ricardo Narvaja
07-2DA ETAPA DEL CURSO DE INTERFASES-CONSOLA INTERACTIVA
PYTHON-INTRODUCCION-AGREGANDO
Bueno creo que el manejo de datos es en este momento óptimo,el uso de diccionarios que se guardan con shelves para manejar las databases, es muy potente y flexible y usar clases para manejar los datos es muy flexible, pero el programita, aun no tiene una interfase interactiva para trabajar con el usuario, que pueda consultar o updatear los datos por medio de la interfase. En esta parte veremos como agregar una consola interactiva como primer paso, en las partes siguientes haremos una GUI, una interfase por medio de un web server, y otros ejemplos mas, para ir paso a paso aprendiendo interfases alternativas. Hagamos un nuevo modulo que llamaremos peopleinteract_query.py esta sera una primera versión muy sencilla.
Aquí lo tenemos, iremos explicando como trabaja. Primero que nada importa shelve import shelve Luego crea una tupla cuyos campos son strings con los nombres de las variables de datos, 'name', 'age', 'job' y 'pay'. fieldnames = ('name', 'age', 'job', 'pay') Luego por cuestiones puramente cosméticas para ordenar las impresiones, hace un for donde a la variable f, le otorgara el valor de cada una de las variables de esta tupla llamada fieldnames Luego sacara el largo de cada una y mediante la función max (), obtendrá el largo máximo de esas strings, esto veremos mas adelante, que solo lo hace para cuando realicemos la impresión, que no se vea desalineada. maxfield = max(len(f) for f in fieldnames) esto funciona en forma similar a
maxfield=0 for f in fieldnames: if len(f)>maxfield: maxfield=len(f) O sea hallara el largo de todos los items de la tupla y guardara el máximo largo en maxfield, en este caso guardaría 4, ya que es el largo de 'name', que es el mayor o sea maxfield sera 4, al terminar el for. La siguiente linea abre con el método open() de shelve, el archivo llamado class-shelve, que si recordamos, guardaba en la parte 6, los datos, esta imagen es de la parte 6.
Allí recordamos que se guardaban los datos en un archivo llamado class-shelve, que por supuesto tiene todos los datos guardados y ahora lo volvemos a abrir para recuperar lo guardado. db = shelve.open('class-shelve') Por lo tanto en db tenemos la database que supimos construir en la parte anterior, recordemos que era un diccionario, con las keys con el nombre de cada empleado y su valor asociado una instancia de Person o Manager según el caso.. En la siguiente linea el programa pregunta por medio de raw_input(), por una key de la database, para que el usuario del programa tipee el nombre del empleado del cual desea ver sus datos y guarda lo tipeado en la variable key. while True: key = raw_input('\nKey? => ') Luego pone una condición de salida ya que si la variable key es falsa, sera porque no se tipeo una string o sea se apreto ENTER, en este caso sale del while por medio de un break.
if not key: break Luego con un try, prueba si puede leer datos relativos a esa key, si existe el empleado que se tipeó, no habrá problema, si se tipeó cualquier cosa, como la key no existe se generara una excepción, pero como estamos en un try el programa no se cerrara, es una excepción controlada por nosotros, en ese caso ira a except donde imprimirá que esa key no existe. try: record = db[key] except: print 'No such key "%s"!' % key Si la key existe en la database, en record tendremos el valor asociado de la key que tipeo el usuario. Luego finalmente en un for usa ljust() que es justificación izquierda como argumento le pasa el largo máximo de las keys a imprimir, luego imprime la flecha, y luego mediante la función getattr() que devuelve el contenido de un atributo A este método se le debe pasar como argumentos la instancia de la cual queremos averiguar el valor de un atributo y una string con el nombre de un atributo como las que están en la tupla fieldnames. for field in fieldnames: print field.ljust(maxfield), '=>', getattr(record, field) Como field tomara los valores de los campos de la tupla fieldnames que son strings, esto nos obliga a usar getattrib(), para hallar el valor de algún atributo, ya que es un método al cual se le pasa como argumento los nombres de atributos en forma de string, en cambio si hiciéramos record.field daría error, ya que el contenido de field es la string 'name' y no la variable name como se necesita para esto ultimo. El que tiene ganas de ver como lo debuggeamos, porque le quedo alguna duda, puede mirar lo que sigue, el que entendió como funciona y no necesita, puede saltear lo siguiente hasta el titulo siguiente UN PASO MAS. DEBUGGEANDO Agregamos el modulo y lo colocamos como MAIN DEBUG FILE.
Allí saltee con f6 el import para no entrar en la ejecución del mismo, y luego vemos la asignación de la tupla llamada fieldnames, con los campos que son las strings con los nombres de las keys de datos en la database. Al apretar F7
Tenemos que apretar varias veces f7, porque hay un for implícito en la linea, hasta que lee todas las keys y con max() determina el máximo largo entre todas, vemos que luego de ejecutarse, la variable maxfield vale 4, como habíamos visto.
Llegamos a cuando abre el archivo.
Aquí debemos saltear con f6, si no nos dará error al entrar dentro del shelve, vaya a saber porque, posiblemente no se pueda interrumpir ese proceso y al tracear dentro crashea o algo así, por lo tanto lo salteamos con f6.
Allí llegamos al raw_input(), para no hacer lío con la entrada y que nos crashee, mejor que tracear es poner un breakpoint en la linea siguiente o sea en el if, y darle DEBUG.
En la pestaña DEBUG I/O tendremos que tipear lo que el programa esta preguntando, el nombre del empleado que queremos averiguar datos.
pues tipeemos como primer caso una key valida, pongamos por ejemplo bob.
Luego de esto para en el breakpoint y el if chequea si hay una string allí, si no fuera así porque se apreto ENTER, saldría con el break, como la hay, va al try a intentar leer los datos de bob.
Mediante db ['bob'] asigna a la variable record la instancia de la clase Person que permite manejar los datos del mismo.
Para que vean que la database db esta armada como diccionario, podemos tipear en el DEBUG PROBE db y veremos, el diccionario con sus keys que son los nombres de los empleados y los valores asociados que son instancias de Person, o de Manager según donde se instancio el empleado.
>>> db {'bob': , 'sue': , 'tom': } o. Bueno continuemos, pasamos el record con f6.
Llegamos el else, al no cumplirse la condición que if not key:break.
Vemos que record es una instancia de la clase Person que en esta caso corresponde al empleado bob ya que se tipeo bob al ingresar los datos. Luego llegamos al ultimo for donde imprimirá todos los datos de bob.
La variable field tomara los nombres de los campos de la tupla, será las strings 'age', 'job', 'pay' y 'name' Aplicar al imprimir el método ljust (), lo que hace es una justificación izquierda con el largo máximo de estas strings que estaba guardado en maxfield, para que no queden mal alineadas, si le quitáramos el ljust(maxfield), veamos como imprime.
Allí vemos que queda desalineado, ya que como name tiene cuatro caracteres y age, tiene tres, queda desalineado, usando el método de ljust(maxfield), siempre usara cuatro lugares de justificación para todos y luego dejara un espacio y colocara la flecha alineada.
Nos quedaba ver lo que imprime después de la flecha, que usa getattr(). Esto lo realiza porque no podemos hacer record.name por ejemplo, porque en record tenemos la instancia , pero el contenido de la variable field es la string 'name' y no la variable name, de esta forma al tratar de imprimir me da error, miremos en el DEBUG PROBE.
Vemos que si tipeamos record.name funciona correctamente, pero name es una variable, en este caso, no una string por eso funciona, esta variable provenía de cuando se realizo la instancia en make_db_clases, lo que estamos usando nosotros no es esta variable sino la string 'name' que salio de la tupla filenames. Como siempre marcamos la diferencia no es lo mismo una variable que una string, no es lo mismo la variable name que puede tener asignado un valor, que la string 'name', esta no es una variable, y solo se puede usar para hallar el valor de un atributo si usamos el método getattr().
O sea que toma el valor de field, que es la string 'name' y se fija si la instancia tiene algún atributo que tenga ese mismo nombre y devuelve el valor correspondiente a bob.name o en este caso seria record.name, pues los datos de bob, están temporalmente en record. Bueno repetirá lo mismo e imprimirá todos los datos de bob alineados, como hemos visto, si lo ejecutamos en una consola.
Allí vemos que tipeamos bob y nos muestra sus datos, tipeamos sue, y nos muestra también, luego apretamos ENTER y sale, nuestra pequeña consola interactiva funciona jeje. Vemos que si tipeamos el nombre de un empleado que no existe, por ejemplo pepe, prueba con el try y da error y va el except nos muestra el mensaje NO SUCH KEY “pepe” o lo que hayamos tipeado.
UN PASO MAS Daremos un paso mas y con este módulo que usaremos a continuación podremos realizar updates interactivos lo llamaremos peopleinteract_update.py.
Hasta el else funciona similar que el anterior, pregunta el nombre de un empleado, para ver los datos de quien quieres actualizar o leer, eso se guarda en la variable key, si apretas ENTER, directamente se ejecuta un break y se sale, si ingresas una string en vez de chequear con un try como el anterior, directamente con un if se fija si lo que tipeaste es alguna de las keys de db y lee el valor asociado de esa key, que es la instancia de ese empleado y lo guarda en la variable record, si lo que tipeaste no es el nombre de ningún empleado,va al else y realiza una instancia en la clase Person que tiene dos campos obligatorios que son name y age, ya que los otros dos tienen valores por default, colocando como nombre y edad, el signo de pregunta.('?')
Vemos que en el caso de que tipeaste un nombre que no existe, record se instancia en la clase Person, con el nombre y edad con signos de interrogación record.name = '?'. Después en el for, la variable field, tomara los valores de la tupla fieldnames. Se asignara a una nueva variable curval, el resultado de usar el método getattr() sobre record, field, o sea que buscara el atributo correspondiente en record, y nos devolverá su valor que lo asignaremos a curval, en este caso.
Como field es 'name', la primera vez que cicla en el for, pues lo que hace es devolverme el valor de record.name que es '?', como ya vimos uso getattr (), ya que no puedo usar record.name directamente ya que el name que sale de la tupla fieldnames es una string, pero dicho método me resuelve esto como ya vimos.
newtext = raw_input('\t[%s]=%s\n\t\tnew?=>' % (field, currval)) En la siguiente linea, me mostrara los datos, en este caso el nombre y el valor que es '?'
Te dice que el nombre es '?' y que le pongas un nombre, tipearemos Juan Perez.
Allí testea si tipeaste una string que estará en newtext, y si es así, con el método setattr() que es el opuesto a getattr(), permite asignarle el valor a un atributo, teniendo una string con su nombre, en este caso tiene tres parámetros, la instancia,la variable field cuyo valor es la string 'name', y el nuevo texto que tipeamos que lo halla con eval (newtext).
Si tipeo Juan Perez sin comillas no funciona, porque eval() espera una string para evaluar si no dará error.
Así funciona, si no usáramos eval() , no responde, bueno eso si que no se porque es.
Vemos que mientras sean strings lo que tipeamos (tengan comillas) funciona, y luego con db[key], que es igual a db['Juan'], en mi caso actualizara la database. Y al cerrar la misma con close() el shelve lo guardara actualizado. Si hubiera sido el caso de que hubiéramos tipeado el nombre de algún empleado que existe en la database, seria lo mismo, solo que en vez de mostrarme '?', me mostraría el valor actual y si lo quiero actualizar le escribo el nuevo y si tecleo ENTER no lo actualiza ya que el if newtext si no es una string la que tipeaste, saltea sin actualizar.
Vemos que al tipear sue, como reconoce que es una empleada, que es una key que existe en la database, entra y asigna a record la instancia con sus datos.
Luego sera similar, me muestra su nombre como no lo quiero cambiar apreto ENTER.
Luego la edad y apreto ENTER
Así hasta que llego al salario de sue, que actualizare a 55000, jeje. esta sue siempre esta bien acomodada, jeje.
Veo que si ejecuto nuevamente el modulo interact query el aumento quedo guardado
Lo mismo que el nuevo empleado Juan.
Así que funciono, con este nuevo modulo, podemos actualizar los datos en nuestra consola interactiva que para eso la hicimos jeje, y quedaron los mismos guardados gracias a shelves.
Bueno aquí termina este capitulo de la creación de una consola interactiva en la siguiente parte veremos como agregar una GUI a nuestro programa. Arrivederci hasta la próxima. Ricardo Narvaja
08-2DA ETAPA DEL CURSO DE PYTHON-INTRODUCCION-INTRODUCCION A TKINTER Hemos visto como hacer una consola interactiva en python ahora le agregaremos una sencilla GUI; por ahora empezaremos desde lo mas sencillo que es usando Tkinter. Existen muchas opciones de GUI (Tkinter, wxPython, PyQt, PythonCard, Dabo), empezaremos por Tkinter ya que es la mas sencilla y viene incluida en python, además de que un programa con Tkinter funciona en cualquier sistema, ya que es como el oficial o el standard de facto, jeje aunque quizás no sea el mas bonito, pero bueno, se empieza por el principio y el principio siempre es lo mas sencillo, mas adelante usaremos y veremos como programar con otras GUI. Por lo demás no profundizaremos completamente aun en el funcionamiento de Tkinter, mas vale veremos ejemplos sencillos que nos ayudaran a armar la GUI para nuestro programa, algunas cosas las supondremos ya que no profundizaremos completamente, lo cual haremos mas adelante cuando veamos el capitulo correspondiente a GUIs, por ahora es una introducción suave, para que vayamos conociendo del tema, y podamos aplicarlo en nuestro programa. NUESTRO PRIMER PROGRAMA CON TKINTER Creamos un modulo nuevo en WING, y le ponemos como nombre tkinter001.py.
Allí esta, luego lo explicamos si lo corremos veremos:
Es de mencionar que la consola que se ve detrás si lo corremos en Windows queda horrible, es muy fácil de eliminar, solo cambiando la extensión de nuestro script a pyw, arranca sin consola debajo, veamos si le pongo de extensión pyw.
Vemos que no sale la consola de comandos detrás y queda mejor, de cualquier manera, ahora que ya sabemos como hacer para que no aparezcan las consolas detrás, continuaremos haciendo el tute con extensión .py, porque estamos aprendiendo y en la consola que esta detrás si nos equivocamos aparecerá el error que cometimos, si no, no nos enteramos de nada, es bueno siempre cuando uno hace un programa con GUI, cambiarle la extensión a pyw, cuando ya esta terminado, así siempre tenemos los mensajes de error posibles en la consola, mientras lo vamos escribiendo. Así que volvamos a ponerle la extensión py, y continuemos. Vemos una simple ventana con el texto SPAM con los botones de minimizar, maximizar y cerrar, realmente el código es similar a este, que es mas fácil de entender.
Vemos que instancio una variable pepe, en la clase Label, y le paso el parámetro del texto que quiero colocar en el label, y luego a esta instancia pepe le aplico el método pack(), al cual tengo llamar para que lo arme. El que corrimos primero es similar solo que realiza el trabajo instanciando en una forma implícita. Realiza la instanciación implícitamente sin asignar, que como no se llamara de ningún otro lugar podemos usarla, y a esa instanciación le realiza el llamado al método pack () a continuación. Label(text='Spam').pack( ) Donde Label (text='Spam'), es la instancia implícita, y a ella se le aplica el método pack(), es como cuando llamamos a imprimir de esta forma Asignando: a=5 +40 print a o sin asignar: print (5 +40) En esta segunda forma resolvemos el paréntesis e imprimimos sin asignar el resultado, y en la llamada a instanciar es similar, lo hace sin asignar esa instancia a una variable, en forma implícita, y al resultado de esa instanciación implícita, le aplica allí mismo el método pack(). Y bueno la explicación del funcionamiento es sencilla en la primera linea importamos el modulo Tkinter, y luego usamos su clase Label, instanciamos en ella.
Vemos que la clase Label hereda los atributos y métodos de la clase Widget y allí nos aclara que esta clase puede mostrar texto y bitmaps. Esta GUI no es la mas útil que se halla creado pero demuestra como funciona TKINTER, y construye una ventana completamente funcional, vemos que los botones de maximizar y minimizar así como los de cerrar funcionan perfectamente.
Allí vemos la ventana MAXIMIZADA, luego la minimizamos y se achica perfectamente y se cierra cuando apretamos el botón cerrar, o al apretar el botón del medio vuelve a su tamaño normal. Vayamos lentamente complicando las cosas pongamos un botón y un messagebox que aparecerá cuando se clickea en el mismo, este nuevo script se llamara tkinter101.py.
Aquí vemos la definición de la clase Tk Symbol: Tk
Defines: class Tk class Tk(self, screenName=None, baseName=None, className='Tk', useTk=1, sync=0, use=None) Return a new Toplevel widget on screen SCREENNAME. A new Tcl interpreter will be created. BASENAME will be used for the identification of the profile file (see readprofile). It is constructed from sys.argv[0] without extensions if None is given. CLASSNAME is the name of the widget class. Bueno lo que quiere decir todo esto, es que es la ventana principal de la aplicación. Definimos una función reply(), para que ejecute el comando cuando apretamos el botón, y esta función usa dentro showinfo de tkMessageBox que importamos al inicio. Le pasamos como parámetros el titulo, el mensaje que tendrá, y listo. Luego se crea una instancia de la clase Tk() llamada window y luego una instancia de la clase Button llamada button, a la cual hay que pasarle como argumentos para el botón, el nombre de la instancia de la ventana a la cual pertenecerá, luego el texto del botón, y la función que tendrá el comando que se ejecutara al apretar, en este caso reply. De la misma forma que usando Label, a la instancia botón hay que aplicarle el método pack() para que lo construya y luego llamamos a mainloop(), que pondrá en funcionamiento el loop de los eventos, necesario para que se muestre la ventana. También se podría realizar la instancia para el botón, en forma implícita, funciona perfectamente, en una sola linea se instancia y se llama al método pack() sin asignar nada.
Veamos si funciona:
Al apretar el botón se ejecuta la función reply.
Y aparece el messagebox con el texto y titulo que deseamos. USANDO OOP PARA GUIs Un primer paso para usar OOP sera este ejemplo
Como vemos es el mismo caso anterior, lo único que creamos una clase llamada GUI que importa a Tk, en vez de instanciar directamente en Tk, lo hacemos por herencia, GUI sera una subclase de Tk, así que no hay problema y dentro creamos un método llamado reply para que se ejecute al apretar el botón, el resto del script es similar, salvo que para llamar al método, como instanciamos window, en dicha clase, habrá que hacerlo con window.reply. En el siguiente agregamos un método button dentro de nuestra clase, es similar al anterior solo que en vez de pasarle el nombre de la instancia, habrá que reemplazarlos, por el self, ya que dentro de la clase, ya vimos que self, tomara el valor de la instancia que esta ejecutando, de esta forma cuando desde fuera llamamos a window.button(), estamos asignando a self, el nombre de la instancia window.
Nuestro próximo ejemplo se llamara tkinter102.py, lo vemos aquí a continuación, donde usaremos OOP en forma un poco diferente en la conformación de nuestra GUI.
Vemos que creo una clase llamada MyGui que es una subclase de Frame o sea heredara sus atributos y métodos. Nuestra clase tiene un método constructor __init__ y un método reply() para manejar lo que ocurre cuando se apreta el botón, por lo demas las instancias se realizan dentro del if que esta debajo que solo se ejecuta si es usado como script principal, y no como importado, allí se instancia window en la clase MyGui, la cual como tiene un método constructor es llamado obligatoriamente: Dentro del mismo se llama al init de la clase Frame, donde se le pasa el parent de la ventana, aunque en este caso es none, porque usa el parámetro por default, mas adelante nos servirá. Realmente Frame es una subclase de Widget, si marcamos en el WING, la palabra FRAME y hacemos click derecho GOTO DEFINITION nos lleva a donde esta definida esta clase.
Vemos que hereda de Widget, la aclaración marcada, nos comenta que con esta clase, estamos preparados para trabajar con parents, aunque aun no lo usemos.
Luego allí mismo dentro del init se crean los botones, instanciando button en la clase Button, pasándole los parámetros del botón y luego llama al método pack() para construirla. Para terminar llama a mainloop() para poner en funcionamiento el loop que recibirá los eventos, veamos como corre.
Vemos que es similar al anterior pero al usar OOP el código es mucho mas organizado y manejable,
los métodos son llamados luego de la instanciación en forma mas clara. Anteriormente no teníamos que llamar a la instancia de la ventana y packearla, en este caso vemos que llama a window.pack(), realmente Frame necesita packear ya que como puede importar otros widgets a través del parámetro parent, hay que armar todo en el pack final, veremos en el siguiente ejemplo como aprovecharlo.
Vemos que a reply, como es un método de la clase MyGui, debemos colocarle el self delante, para que pueda ser utilizado y así la clase sepa que instancia lo esta usando. La ventaja de ser una subclase de Frame es que se convierte en un componente al cual podemos agregar todos los widgets que esta clase crea y adosarlos a nuestra GUI, el siguiente ejemplo muestra en la practica como se hace eso, creamos un modulo nuevo llamado attachgui.py, que importara y usara la clase MyGui que cree en el modulo anterior. Allí vemos que al inicio luego de realizar la importación del modulo Tkinter, importa de tkinter102, la clase MyGui que yo cree.
Hasta aquí vemos que lo que hace es crear dos instancias una de Tk(), llamada mainwin, y la otra llamada popup que se instancia en la clase TopLevel (). Realmente la instanciada en Tk() sera la ventana principal y la instanciada en TopLevel sera un popup, nos damos cuenta de esto, porque el mainloop esta aplicado a mainwin que es la instancia de Tk(), o sea es la que maneja el loop de los eventos. Symbol: Toplevel Likely type: class Toplevel class Toplevel(self, master=None, cnf={}, **kw) Construct a toplevel widget with the parent MASTER.
Si miramos que dice sobre TopLevel, vemos que es el widget de mas alto nivel, por ejemplo para diálogos..
Allí le agregamos la linea que instanciara implícitamente en la clase MyGui, usando como argumento popup. Recordemos que MyGui tenia un argumento no obligatorio que tenia un valor por default (parent=None), pero en este caso al instanciar, le pasamos el argumento que sera el widget popup, obviamente usara este widget como parent.
Le pasara el argumento popup al método constructor, que llama al constructor de Frame.
Y lógico atachearà popup, mientras la vez anterior que usamos MyGui, este parámetro estaba en None, ahora le pasamos el valor de lo que queremos atachear a nuestra clase y lo agregara. Veamos como cambia, ejecutemoslo.
Veamos si cambio la ventana que usa MyGui.
Vemos que es parecido a la vez anterior, solo que ahora agrego el botón que dice PRESS que tenia la clase que cree MyGui y que cuando lo apreto sale el messagebox que habíamos armado dentro de nuestra clase.
Así que vimos como macheamos a un widget hecho por nosotros. Otra cosa que vemos es que cuando mandas a packear un botón o label, al pasarle el argumento SIDE=RIGHT o LEFT lo coloca a la derecha o a la izquierda de la ventana)
Por supuesto de esta forma atacheamos a nuestro widget pero también podríamos usar los widgets que Frame trae incluidos, en este ejemplo solo mostramos como agregarle el nuestro, mas adelante veremos otras posibilidades. También como MyGui es una clase, se puede customizar, eso lo veremos en el siguiente script que llamaremos customizegui.py.
Aquí se define una nueva clase CustomGui que hereda de MyGui, pero a su vez modifica el reply para que cuando se apriete el botón, muestre un mensaje diferente, veamos si funciona.
Funciona por supuesto, en este caso no usa el atacheado anterior, si no simplemente la clase MyGui original y lo único que cambiamos fue el customizado del messagebox que ahora dice Ouch!. A pesar de que estas son pequeñas GUIs nos dan la idea de que las mismas se pueden heredar, y
customizar fácilmente, mas adelante veremos con mas profundidad el tema GUI, por ahora solo nos estamos iniciando para poder agregar una GUI sencilla a nuestro programa. Veamos el ultimo ejemplo de esta parte, veremos como agregarle a la GUI la entrada de datos por medio de teclado. El ultimo ejemplo lo llamaremos tkinter103.py, creamos un nuevo modulo.
Bueno obviamente para que se muestre el archivo icono, debemos tenerlo en la misma carpeta en este caso se llama 'py-blue-trans-out.ico' (en Linux no acepta la extensión ico, hay que usar png). El que no tiene ese archivo usa otro archivo ico que tenga, y le cambia el nombre en el script y listo, como yo lo tengo ahí esta, espero acordarme de ponerlo adjunto dentro del paquete del tute.
Cada vez le agregamos mas detalles, luego de instanciar top en la clase Tk, y de llamar al método title(), para colocarle el titulo 'Echo', agregamos el icono llamando al método iconbitmap(), que buscara en la carpeta donde esta corriendo el script.
El titulo lo colocamos como vimos con top.title('Echo')
Luego coloca el Label 'Enter your name', donde se le pasa como argumento como ya vimos el nombre de la instancia de la ventana, luego el texto y en este caso como tercer argumento cuando lo va a packear SIDE=TOP para que lo coloque arriba. Aquí realizamos una instancia implícita de la clase Label como vimos anteriormente. Label(top, text="Enter your name:").pack(side=TOP)
Si uno mira dentro del modulo Tkinter y busca TOP
Vemos que el parámetro SIDE es para eso cuando packeas algo para agregarlo al widget, le pasas con el la posición donde se ubicará. Luego tenemos para ingresar el texto ent = Entry(top)
Entry es una clase, y hay que instanciar una variable en ella en este caso se llama ent y el argumento, debe ser opcional, para el __init__ de la misma.
ent.pack(side=TOP) btn = Button(top, text="Submit", command=(lambda: reply(ent.get( )))) Luego packea ent y en la siguiente linea le agrega al botón, que como vemos como comand directamente llama a la función reply, a la cual le pasa la entrada de texto que hicimos y por medio de un format string, incluirá lo que tipeamos en el showinfo.
Si lo ejecutamos
Bueno Tkinter tiene muchisimas mas posibilidades que esto y se estudiaran mas adelante, pero con lo visto hasta aquí alcanza como una introducción para una GUI para nuestro programa, la idea es realizar algo asi.
En la parte siguiente lo implementaremos usando lo que hemos visto hasta ahora. Hasta la parte 9 Ricardo Narvaja
09-2DA ETAPA DEL CURSO DE PYTHON-INTRODUCCION-AGREGANDO GUI NUESTRO PROGRAMA
A
Como ya dijimos, mas adelante se profundizara el tema de las GUIs por ahora se hizo un paneo sobre algunas posibilidades de Tkinter, para entender y agregar una GUI a nuestro proyecto, si alguna parte aun no se entiende perfecto, bueno, en el capitulo dedicado exclusivamente a GUI lo veremos mas a fondo. Si alguna parte no queda perfectamente clara, tengan en cuenta que en el libro sobre el cual nos basamos ni trata de explicar la GUI, solo la da y dice que en el capitulo correspondiente lo explicara, así que si algo no queda perfectamente claro, bueno, ya lo veremos en su momento a no deprimirse. Nuestra modulo para la GUI se llamara peoplegui.py y bueno trataremos de entender un poco como funciona, aunque en el libro en el que nos basamos, lo pone y dice que en el capitulo correspondiente a GUIs lo explicara mas adelante, aquí solo lo quiere usar para el programa.
Bueno miremoslo paso a paso, debemos recordar crearlo en la misma carpeta donde esta la base de datos guardada en el archivo shelve que hicimos en las partes anteriores, asi puede leer los datos guardados. from Tkinter import * from tkMessageBox import showerror import shelve shelvename = 'class-shelve' fieldnames = ('name', 'age', 'job', 'pay') Bueno esta primera parte no tiene mucho secreto, lo que hace es importar Tkinter, y de tkMessageBox importar showerror que mostrara el messagebox correspondiente a error, luego importa shelve que usábamos para guardar y recuperar los datos, y luego inicializa la variable shelvename con el nombre del archivo que es 'class-shelve' y la tupla fieldnames con los campos que son strings con los tipos de datos a solicitar. Luego vienen los def aunque como sabemos estas solo definen sus funciones, no entra a ninguna de ellas hasta que no es llamada especialmente como ya vimos. db = shelve.open(shelvename) window = makeWidgets( ) window.mainloop( ) db.close( ) Aquí luego de todos los def, lee el archivo donde tiene los datos con shelve.open, luego llama a la funcion que definimos MakeWidgets(), y crea el mainloop() que manejara los eventos, cuando se salga del mismo se cerrara el archivo de datos con db.close().
Veamos ahora la función MakeWidgets() que es la que crea nuestra GUI mas en detalle. def makeWidgets( ): global entries window = Tk( ) window.title('People Shelve') form = Frame(window) labels = Frame(form) values = Frame(form) labels.pack(side=LEFT) values.pack(side=RIGHT) form.pack( ) entries = {} for label in ('key',) + fieldnames: Label(labels, text=label).pack( ) ent = Entry(values) ent.pack( ) entries[label] = ent Button(window, text="Fetch", command=fetchRecord).pack(side=LEFT) Button(window, text="Update", command=updateRecord).pack(side=LEFT) Button(window, text="Quit", command=window.quit).pack(side=RIGHT) return window Dentro de la misma podemos reconocer ciertas cosas window = Tk( ) window.title('People Shelve') La instanciación de window en la clase Tk() que sirve para crear las ventanas principales de un programa, y luego window.title ('People Shelve'), como habíamos visto le pone el titulo a la ventana.
Luego vemos que form = Frame(window) Hace una instancia de Frame a la cual llama form, a la cual como vimos en attachgui, le pasa el nombre de la instancia de la GUI de nuestro programa, de manera de poder manejar o atachear todos los widgets de Frame en nuestra GUI.
labels = Frame(form) values = Frame(form) labels.pack(side=LEFT) values.pack(side=RIGHT) form.pack( ) entries = {} luego vemos que realiza dos instancias de Frame llamadas labels y values, para poder colocar los nombres y las cajas donde tipearemos los datos
labels.pack(side=LEFT) values.pack(side=RIGHT) Vemos que al packearlo los labels los coloca a la izquierda y los values a la derecha, como vemos en la imagen. for label in ('key',) + fieldnames: Label(labels, text=label).pack( ) ent = Entry(values) ent.pack( ) entries[label] = ent Luego realiza un for, para recorrer los campos de fieldnames, a los cuales le agrega key, o sea que label, tomara los valores key, name, age, job, pay. Label(labels, text=label).pack( ) Luego dentro del for, le coloca los nombres a los labels, en esta instrucción y los packea. ent = Entry(values) Dentro del for, va asignando los valores que lee que tipeamos nosotros y lo packea. ent.pack( )
Luego los va armando en el diccionario entries, con la key que tiene el label correspondiente. entries[label] = ent Recordemos que la primera vez que loopea sera entries[key]=NOMBRE DEL EMPLEADO QUE BUSCAMOS (bob, sue, tom) la segunda vez entries [name]=NOMBRE Y APELLIDO DEL MISMO y asi ira armando un diccionario con lo que tipeamos en entries. Button(window, text="Fetch", command=fetchRecord).pack(side=LEFT) Button(window, text="Update", command=updateRecord).pack(side=LEFT) Button(window, text="Quit", command=window.quit).pack(side=RIGHT) return window Luego crea los tres botones FETCH, UPDATE Y QUIT
Y a cada uno le coloca como command la función que define mas abajo, en el caso del botón FETCH ejecutara la función fetchRecord, el botón UPDATE ejecutara updateRecord y el botón QUIT, ejecutara window.quit, que no hay que definirla pues directamente cerrara la ventana. Bueno entonces cuando clikeemos en el boton FETCH vendremos aquí def fetchRecord( ): key = entries['key'].get( ) try: record = db[key] # fetch by key, show in GUI except: showerror(title='Error', message='No such key!') else: for field in fieldnames: entries[field].delete(0, END) entries[field].insert(0, repr(getattr(record, field))) Vemos que en FETCH lo que hará sera tomar la key que tipeamos, y tratara de abrir el diccionario database db que tenia los datos que leyo del archivo, y ver si ese nombre corresponde a algún
empleado, si no es así mostrara el error de que no existe esa key guardada en la database.
En cambio si tipeamos una key que existe como en este caso sue.
Al apretar FETCH me llenara con los datos que tiene de la database.
Por lo tanto cuando la key es correcta salta al else aquí for field in fieldnames: entries[field].delete(0, END) entries[field].insert(0, repr(getattr(record, field))) aquí entries[field].delete(0, END)
como entries son las entradas de texto que hicimos nosotros, si la key es correcta, borra todo lo que tipeamos en los otros campos, para luego en la siguiente linea colocar las correctas que lee de la database, ya que record tiene los datos que leyo del archivo correspondientes a sue
Recordemos que record es una instancia de la clase Person. record = db[key]
La misma recordamos que estaba en person.py y que todos los nombres de los empleados bob, sue y tom, y los que vengan se instanciaran o en esta clase o en la clase Manager
Que era una subclase de Person, que heredaba todos sus atributos, salvo que les daba un bonus de aumento por ser managers jeje.
Recordamos tambien que repr(getattr(record, field) Lo que hacia era devolvernos el valor de un atributo de una clase, en este caso, field, tiene las strings correspondientes a los datos, por eso debemos usar getattr ya que no podemos llamar a dichos atributos con una string, ya lo vimos en la parte anterior eso. Si haciamos:
No habria problema, ya que record es una instancia de la clase Person, y name es un atributo de la misma, pero aquí es diferente.
Vemos que field tiene la string 'name', y no el nombre de la variable, por lo cual si llamara a record.field.
Nos dara error ya que field es la string 'name' y no la variable name, por eso siempre utiliza getattr que es una funcion que de una string, trata de ver si una instancia tiene un atributo que coincida con dicha string, y nos devuelve el valor si la halla. Bueno eso es en cuanto a la función fetchRecord, la otra es la función updateRecord. def updateRecord( ): key = entries['key'].get( ) if key in db.keys( ): record = db[key] # update existing record else: from person import Person # make/store new one for key record = Person(name='?', age='?') # eval: strings must be quoted for field in fieldnames: setattr(record, field, eval(entries[field].get( ))) db[key] = record Vemos que si la key que tipeamos existe if key in db.keys( ):
Lee a record los datos de esa key, por supuesto record sera una instancia de Person o de Manager según el empleado y los guarda luego del else: ya que al mismo no entra pues al ser una key que ya existe lo salteara, y viene aquí. for field in fieldnames: setattr(record, field, eval(entries[field].get( ))) db[key] = record Donde con la funcion setattrib (), colocara nuevos valores a los atributos de la key existente en record y lo guardara en el archivo con db[key]=record, en el otro caso que fuera una key nueva entra al else: else: from person import Person # make/store new one for key record = Person(name='?', age='?') # eval: strings must be quoted for field in fieldnames: setattr(record, field, eval(entries[field].get( ))) db[key] = record Donde vemos que crea un nuevo empleado instanciando record en Person con el nombre '?' y edad '?' y luego actualizara los datos de la misma forma leyendo lo que tipeamos que esta en entries y luego lo guardara en el archivo. Vemos que la GUI a pesar de ser sencilla provee sin tipear en la consola la posibilidad de ver los datos de cualquier empleado, de updatear los mismos y de agregar un nuevo empleado y sus datos sin problema.
Alli el empleado TomTom no existe, al apretar FETCH, me da error pero si lo quiero agregar pongo sus datos y apreto UPDATE.
Luego de agregarlo junto a sus datos y apreto FETCH.
Los rellenara nuevamente con los valores guardados
Y si quiero aumentar el sueldo de sue, tipeo sue para ver sus datos y clickeo FETCH.
Ahora cambio y pongo 60000 y clickeo update. Ahora si borro el dato para ver si quedo guardado y apreto FETCH.
Lo rellena con el dato actualizado
Bueno vemos que nos estamos familiarizando con un tema difícil como son las GUIs, y lentamente vamos entendiendo, nuestro programita tiene una primera versión de una GUI sencilla pero funcional Esta GUI tiene miles de posibilidades de mejorar, lo cual haremos mas adelante cuando hayamos estudiando el tema de las GUIs mas en profundidad. Hasta la parte 10 Ricardo Narvaja
10-2DA ETAPA DEL CURSO DE PYTHON-INTRODUCCION- AGREGANDO UNA INTERFASE WEB Como hemos visto hacer una GUI ayuda a manejar mas rápidamente los datos, también el hecho de poder acceder a los mismos a través de la web, es muy útil, de esta forma solamente con un browser, se podrá acceder a ellos, sin necesidad de que la persona que navega tenga python instalado, solo la maquina que tiene el server con los datos, tendrá que tener python. Por lo demás hay muchas y sofisticadas formas de acceder a datos por medio de la web, existen programas que simplifican la tarea como Zope, Plone, Twisted, CherryPy, Webware, Django, TurboGears, mod_python y Quixote, por ahora nosotros veremos ejemplos lo mas simples posibles que podemos manejar en python y codearemos un simple script CGI. Como en el caso de las GUIs este es un primer paneo sobre el tema WEB SITE asi que a no asustarse mas adelante veremos detalladamente en los capitulos correspondientes a esos temas, solo estamos explorando un poco si algo nos queda en duda ya lo despejaremos mas adelante. LAS BASES DE CGI Hacer scripts CGI en python es sencillo siempre que uno tenga un leve conocimiento de HTML, URLs y el modelo cliente/server de la WEB, de cualquier forma como ya dijimos no asustarse, en este caso y en el caso de las GUIs, mas adelante se realizara un estudio mas detallado del tema para profundizar. Hacemos un archivo de texto que tenga el siguiente código y le cambiamos el nombre a cgi101.html y lo guardamos en la misma carpeta donde están los scripts anteriores. Interactive Page Enter your name: Por supuesto debemos cambiarle la extensión a html para poder ser ejecutado en un navegador, si lo ejecuto con doble click.
Se abre la pagina que nos pregunta que pongamos un nombre, y si apretamos enviar tratara de abrir el archivo cgi101.py y como este no existe dará error, pero ya vemos como va la cosa.
Aqui pone el titulo de la pagina Interactive Page Define el form, el método que sera POST y la acción que sera arrancar el cgi101.py Luego tiene la entrada de datos Enter your name: Submit significa que enviara los datos, no hay problema si no entendemos a full el código HTML mas adelante se vera mas en profundidad, vemos eso si, que llama al archivo cgi101.py ya que si lleno los datos y apreto ENVIAR, me da error al tratar de ejecutar ese archivo que no existe aun.
Si apreto vemos que busca el archivo y como no lo halla dará error.
Creo el archivo cgi-bin\cgi101.py o sea lo coloco dentro de una carpeta llamada cgi-bin, que estará dentro de la carpeta donde están el resto de los scripts que creamos de nuestro programa, allí lo colocamos.
Creamos un modulo llamado webserver.py (aunque python ya tiene modulos prehechos para web
servers)
webserver.py ###################################################################### # implement HTTP web server in Python that knows how to run server# side CGI scripts; serves files/scripts from current working dir; # Python scripts must be stored in webdir\cgi-bin or webdir\htbin; ###################################################################### webdir = '.' port = 80
# where your html files and cgi-bin script directory live # default http://localhost/, else use http://localhost:xxxx/
import os, sys from BaseHTTPServer import HTTPServer from CGIHTTPServer import CGIHTTPRequestHandler # hack for Windows: os.environ not propagated # to subprocess by os.popen2, force in-process if sys.platform[:3] == 'win': CGIHTTPRequestHandler.have_popen2 = False CGIHTTPRequestHandler.have_popen3 = False os.chdir(webdir) # run in HTML root dir srvraddr = ("", port) # my hostname, portnumber srvrobj = HTTPServer(srvraddr, CGIHTTPRequestHandler) srvrobj.serve_forever( ) # run as perpetual demon
Allí tenemos un webserver que solo mientras lo mantengamos corriendo al script sin cerrarlo, funcionara # implement HTTP web server in Python that knows how to run server# side CGI scripts; serves files/scripts from current working dir; # Python scripts must be stored in webdir\cgi-bin or webdir\htbin;
Vemos vemos que aclara que los scripts de python deben estar guardados en una carpeta llamada cgi-bin, dentro del directorio donde esta ejecutándose el modulo del webserver, allí en el mismo directorio debe estar el archivo html a ejecutar, si no igual el script tiene para configurar un path diferente, y para cambiar el port. webdir = '.' # where your html files and cgi-bin script directory live port = 80 # default http://localhost/, else use http://localhost:xxxx/ Como yo coloco el modulo webserver.py en la carpeta donde tengo todos los módulos que cree para mi programa y alli mismo coloco el html que se ejecutara, y el archivo de python cgi101.py dentro de una carpeta cgi-bin, no habrá problema.
Para correr el webserver, simplemente se ejecuta webserver.py desde una consola o haciendo doble click sobre el icono del mismo y quedara esperando request constantemente mientras no cerremos la consola. Al ejecutarlo se abrio la consola, eso quiere decir que esta corriendo, ahora como estoy en la misma maquina para acceder al server, debo hacerlo mediante http://localhost si no habra que usar el ip que se conecta al exterior, si es posible hacerlo.
Vemos que me lista todos los archivos del directorio raíz, entre ellos lógicamente esta el html que
creamos, si lo ejecutamos.
Pongamos un nombre y apretemos enviar.
Al apretar enviar se ejecuta el script cgi101.py y imprime el saludo según el nombre que hayamos tipeado.(si no llega a encontrar el script pueden mirar en la consola del webserver que les dira donde lo esta buscando)
Recordemos que si no tipeamos nada, allí en el if testeara que form.has_key('user') existe, si no existe imprimirá quien es usted o WHO ARE YOU?, veamos, probemos sin tipear nada. Es posible que si corren en linux el webserver les diga que no tiene permiso para ejecutar el archivo, bueno ahí tendrán que cambiar los permisos con CHMOD como es lo usual en el sistema LINUX, y ejecutar archivos desde un script es mas complicado el tema de permisos, así que bueno, ya veremos mas adelante como solucionar esos temas en linux.
Y si vamos a alguna pagina web como esconda que es una pagina que usa un proxy web y tipeamos nuestro IP para ver si podemos acceder desde fuera de nuestra maquina.
Allí tipeamos nuestro IP, y le damos.
Y podemos ejecutar el html a distancia.
Si se hubiera cambiado el port por ejemplo por 8080, seria el link http://localhost:8080 o desde el exterior cambiar las X por las IPs http://XX.XX.XX.XX:8080
Vemos que en la consola se muestra toda la interacción con el server
Existen muchas formas de pasarle datos a un script CGI, corro nuevamente el webserver.py y veo que
Al apretar el botón
Pero vemos que podemos obtener la misma respuesta enviadole una URL como esta directamente. http://localhost/cgi-bin/cgi101.py?user=Sue+Smith
probemos
Vemos que al enviarle en el link directamente los datos, no aparece la ventana para tipear, y directamente nos muestra la salida. El input que estamos usando ahora es conocido como query parameters o parámetros de query y se ve al final de la URL luego del signo de interrogación ?, y no esta entrando en la forma usual de
tipear texto como lo hicimos al inicio. Esta forma de entrar datos al final de las URLs también es llamada GET REQUEST, nuestro script original usa el método POST que maneja inputs en pasos separados, por suerte la implementación de CGI de python no diferencia entre ambos métodos y maneja los datos en ambas formas sin diferenciarlas. Existe una forma para enviarle URLs con inputs sin usar ningún browser, el modulo de python urllib, nos ayuda para eso, si tipeamos en la ventana interactiva de python o shell, estando el webserver funcionando.
>>> from urllib import urlopen >>> conn = urlopen('http://localhost/cgi-bin/cgi101.py?user=Sue+Smith') >>> reply = conn.read( ) >>> reply 'Reply Page\nHello Sue Smith!\n' >>> urlopen('http://localhost/cgi-bin/cgi101.py').read( ) 'Reply Page\nWho are you?\n' >>> urlopen('http://localhost/cgi-bin/cgi101.py?user=Bob').read( ) 'Reply Page\nHello Bob!\n'
Vemos al principio que define una variable conn que sera una instancia de urlopen, a la cual luego le aplica el método read() de forma de leer la respuesta, pero también vemos como se puede hacer en un solo paso, directamente aplicando el metodo read() en la misma linea. La ulrlib nos devuelve la respuesta cruda que nos daría un server para una URL que le enviamos, realmente es código HTML crudo, luego dicho texto puede ser procesado y mostrado en la salida en la misma forma que lo haría desde un browser, pero de cualquier forma, es un buen método para testear las respuestas a diferentes URLs y sus diferentes inputs, y es naturalmente muy usado para testeo interactivo Bueno el siguiente paso seria armar un html que nos permita ingresar los datos a nuestro programa, debería verse como esto.
El código seria este y lo llamaríamos peoplecgi.html.
People Input Form Key Name Age Job Pay
Vemos que es muy parecido al anterior solo que tiene mas entradas de texto, y dos botones llamados FETCH y UPDATE. Ahora vemos que este html, llamara a un script de python diferente llamado peoplecgi.py que manejara los datos de nuestro programa lo copiaremos a continuacion, es un poco largo pero explicaremos algunas cosas de su funcionamiento, someramente.
########################################################################## # implement a web-based interface for viewing/updating class instances # stored in a shelve; shelve lives on server (same machine if localhost) ########################################################################## import cgi, shelve form = cgi.FieldStorage( )
# cgi.test( ) dumps inputs # parse form data
print "Content-type: text/html" shelvename = 'class-shelve' fieldnames = ('name', 'age', 'job', 'pay')
# hdr, blank line in string
# main html template replyhtml = """ People Input Form key $ROWS$ """ # insert html for data rows at $ROWS$ rowhtml = '%s\n' rowshtml = '' for fieldname in fieldnames: rowshtml += (rowhtml % ((fieldname,) * 3)) replyhtml = replyhtml.replace('$ROWS$', rowshtml) def htmlize(adict): new = adict.copy( ) for field in fieldnames: value = new[field] new[field] = cgi.escape(repr(value)) return new def fetchRecord(db, form): try: key = form['key'].value record = db[key] fields = record.__dict__ fields['key'] = key except: fields = dict.fromkeys(fieldnames, '?') fields['key'] = 'Missing or invalid key!' return fields
# values may have &, >, etc. # display as code: quoted # html-escape special chars
# use attribute dict # to fill reply string
def updateRecord(db, form): if not form.has_key('key'): fields = dict.fromkeys(fieldnames, '?') fields['key'] = 'Missing key input!' else: key = form['key'].value if key in db.keys( ): record = db[key] # update existing record else: from person import Person # make/store new one for key record = Person(name='?', age='?') # eval: strings must be quoted for field in fieldnames: setattr(record, field, eval(form[field].value)) db[key] = record fields = record.__dict__ fields['key'] = key return fields
db = shelve.open(shelvename) action = form.has_key('action') and form['action'].value if action == 'Fetch': fields = fetchRecord(db, form) elif action == 'Update': fields = updateRecord(db, form) else: fields = dict.fromkeys(fieldnames, '?') # bad submit button value fields['key'] = 'Missing or invalid action!' db.close( ) print replyhtml % htmlize(fields) # fill reply from dict
Al archivo peoplecgi.py lo debo copiar a la carpeta cgi-bin.
Y luego al conectarme desde un browser, si es en forma local con http://localhost o mediante el IP de nuestra maquina, podremos ver los archivos y elegir ejecutar peoplecgi.html, tambien se puede hacer con un link completo. http://localhost/peoplecgi.html
Si tipeo sue en el campo key veo que llenara con los datos de sue.
Veo que el nombre que tipeo lo toma directamente, pero el campo name debe ser una string entre comillas lo mismo que el job, la edad y el pago pay son numeros enteros, tipeemos ahora un empleado que no este en la lista por ejemplo Pepe y apreto fetch.
Me dira que no existe y pone signos de pregunta para que ponga los datos de Pepe para hacer update, veamos si funciona.
Ahora borro y tipeo Pepe en key y clickeo FETCH y salen los datos que le ingrese de forma que no solo puedo leer datos existentes sino tambien updatear, cambiando los que estan o agregando nuevos.
En el caso que lo querramos debuggear con WING, debemos tener un WING acorde en este caso el 3 o mayor va bien puesto que todo trabaja con python 2.5 y 2.4, si no habra que ver que todo este instalado para python 2.4. Sabemos que para debuggear scripts que no se arrancan desde si mismos si no desde otros programas que no son escritos en python, debemos copiar el modulo wingdbstub.py, que esta en la carpeta donde tenemos instalado el WING a la carpeta donde tenemos nuestro webserver.py y el resto de los scripts. Por supuesto abrimos el peoplecgi.py en el wing y tipeamos como primera linea import wingdbstub y ponemos un breakpoint como muestra la imagen.
Debemos darle permiso al WING para que se conecte por medio del firewall configurando este para que acepte conexiones del mismo, y en el mismo WING en EDIT-PREFERENCES ponemos la tilde alli para que quede esperando conexiones y luego arrancamos el webserver, y en el browser tipeamos http://localhost o el IP de su maquina.
Primero tipeare el nombre sue, y apretare el boton fetch y eso hara que pare en el breakpoint a ver como maneja el script los datos que tipeamos.
Alli paro recuerden que muchas veces si no les funciona despues de una primera vez puede ser porque el browser guardo en su cache una copia de la pagina y no esta llamando realmente de nuevo al script si no que usa una version cacheada del mismo con lo cual no parara en el breakpoint. Voy tipeando F6 y paso las asignaciones de las variables shelvename y fieldnames.
A fieldnames, le asigna una tupla con los nombres de los campos de datos que tendra la web, y a la variable shelvename le asigna el nombre del archivo donde estan guardados los datos de los empleados, que abrira mas adelante usando shelves, a la variable form al igual que en el ejemplo anterior le asigna los datos que tipee a traves de cgi.FieldStorage() >>> form
FieldStorage(None, None, [MiniFieldStorage('key', 'sue'), MiniFieldStorage('action', 'Fetch')])
Luego llegamos aquí:
Donde tiene una string preparada para ingresar los datos del html por medio de format strings, veamos el contenido de la variable rowhtml, al ejecutar la instrucción se asigna.
Hasta alli la string es asi y asigna una variable rowshtml con una string vacia, luego en el for que hay a continuacion ira armando todo veamos como hace. Como fieldnames era fieldnames = ('name', 'age', 'job', 'pay') en el for la variable fieldname tomara en el primer ciclo el valor 'name'
Y aquí rowshtml += (rowhtml % ((fieldname,) * 3)) Lo que hara en cada ciclo del sera armar una string mas grande en rowshtml, veamos como va quedando al ejecutarlo y rowshtml quedara asi en la primera pasada si recordamos que rowhtml era asi '%s\n' y rowshtml quedo asi
name\n' Vemos que lo que hizo fue un triple format string usando 'name', y reemplazandolos tres %s que habia por dicha key, y la asigno a rowshtml, en la siguiente pasada hara lo mismo con la segunda key y la agregara y finalmente quedara asi. rowshtml 'name\nage\njob\npay\n' Alli vemos que armo una super string y ahora llego a la siguiente instrucción que si la ejecuto y veo en el navegador el codigo fuente con VIEW SOURCE veo que remplazo ROW por lo que habia armado en rowshtml.(podia haberlo ya tenido armado directamente en esa forma, la verdad un poco inutil estos pasos ya que si lo pone asi directamente en el script no necesita hacer todo esta vuelta pero bueno ya saben pueden usar el html que esta dentro de cgiscript.py directamente como se ve en la imagen de abajo y se ahorran todo el trabajo.)
Ya que el HTML en mi script ya tenia la linea correspondiente a key y le faltaban las de los campos a llenar.
De cualquier manera si no entienden mucho del HTML no se hagan mucho problema esto es un
curso de python y no de HTML solo mencionamos para ver como maneja los datos nada mas.
Luego llega a los def de las funciones que usara, alli crea una funcion llamada htmlize y otra llamada fetchRecord, para cuando se aprete el boton FETCH .
Aquí se crea la funcion updateRecord para cuando apretemos el boton UPDATE.
Bueno llegamos traceando a donde comienza a ejecutar lo primero que hace es abrir el shelve, y leerlo y asignarlo a la variable db.
Alli vemos que asigna a action el resultado de una operación AND si recuerdan la tabla de valores de AND si el primer miembro es cierto como en este caso True, el resultado sera el del segundo miembro o sea FETCH se asignara a action.
Luego tomara la desicion si apretamos FETCH ira a la funcion fetchRecord a los cuales les pasa como parametros la variable db y form y el resultado de dicha funcion, vemos que se asigna a la variable fields, entremos a la funcion a ver que pasa. Recordemos que form tenia lo que tipeamos metido alli. >>> form FieldStorage(None, None, [MiniFieldStorage('key', 'sue'), MiniFieldStorage('action', 'Fetch')]) Como form, tiene realmente los datos que trajo de la pagina web, alli vemos que esta sue, el dato que tipeamos y el boton que clickeamos FETCH.
Aqui form['key'].value, nos devuelve el valor de lo que tipeamos en el campo key, y lo asigna a la variable key.
Luego es sencillo, lee db['sue'] del diccionario, si existe, lo asigna a la variable record y si no existe dara error y ira al except.
Donde asignara los signos de pregunta a todos los campos de fields, y a key le pondra 'MISSING OR INVALID KEY' O sea que si sue existe devolvera en fields un diccionario con los valores correctos de sus datos y si no lo armara con signos de pregunta. >>> record ya que record era una variable instancia para sue, al aplicarle el metodo __dict__ lo que hace es armar un diccionario con sus atributos. fields = record.__dict__ >>> fields {'pay': 60000, 'job': 'music', 'age': 45, 'name': 'Sue Jones'} Y le agrega un campo mas con la key fields['key'] = key para tener los datos completitos de sue, en el diccionario fields >>> fields {'pay': 60000, 'job': 'music', 'age': 45, 'key': 'sue', 'name': 'Sue Jones'}
eso lo devuelve en el return
Luego cierra la base de datos actualizandola y realiza el llamado a la funcion htmlize que transformara en datos html y luego con el format string se pegaran en el HTML de la pagina. Como ya dije aquí no es tan importante entender el HTML, sino como interaccionamos con python y luego ya iremos viendo mejor estos puntos, al menos tenemos una demostracion de como se puede hacer, por supuesto en el ejemplo si clickeamos update ira a ejecutar la funcion updaterecord.
Que vera si la key que tipeamos no existe y si es asi tambien mostrara 'MISSING KEY INPUT', si
existe ira al else, y trata de leer aquí si dicha key corresponde a un empleado. if key in db.keys( ): si existe dicho empleado solo asigna sus datos a la variable record record = db[key] y si no existe ya vimos en otros modulos como realiza una instancia en Person con los signos de pregunta como valores de name y age y con setattr agregara los datos que tipeamos.
Y alli guardara en db los nuevos valores que luego al hacer close del shelve se updatearan.
Y luego de la misma forma usando htmlize se mostraran en la pagina.
Con esto concluimos nuestro primer programa hemos hecho un pantallazo de representacion de datos, CONSOLA INTERACTIVA, OOP, GUIS, y lo mas basico de WEB SITE, en los capitulos siguientes profundizaremos todos estos conceptos, pero nuestro primer programa ya se termino, albricias, jeje.
Hasta la parte 11 Ricardo Narvaja
11-2DA ETAPA DEL CURSO DE PYTHON- SYS y OS Una vez hecho un vuelo de pájaro sobre algunos temas de python, es necesario profundizar y refrescar algunas cosas para seguir adelante, algunas las hemos mencionado, otra no, pero ahora es el momento de refrescarlas y aplicarlas. Lo primero que haremos sera mirar un poco entre los módulos que trae incluido python, y tratar de profundizar en ellos, por supuesto que python tiene excelentes módulos que se le pueden agregar que pueden ser bajados de internet, en este curso en el caso que usemos módulos que no vienen incluidos en python haremos la aclaración correspondiente. La mayoría de nosotros usamos el WING, algunos el editor SPE, otros Active Python, estamos de acuerdo que cuando mas nos ayude y nos informe, mejor sera el editor de python que usemos, de cualquier manera, cuando uno aprende, tiene que también aprender los métodos de auto ayudarse, puede ocurrir que uno tenga que trabajar en una maquina donde nuestro editor preferido no este instalado y tengamos python, y ahí no podemos dejar de saber como obtener ayuda manualmente, o sea la ayuda automática de nuestro editor bienvenida sea, pero hay que saber como hacer las cosas a mano también. Por ejemplo la idea es estudiar las librerías sys y os en un principio, sin dejar de considerar que hay librerías externas muy buenas, en la parte de debugging de la lista hemos visto tutoriales de PYDBG, que permite usar scripts de python para debuggear en windows, y hay pensado sacar una versión para linux y mac, que no exista aun no significa que python sea diferente en esos sistemas, si no que la forma que trabaja cada sistema el tema debugging es diferente, por eso hay que adaptar el mismo programa python a la forma en que cada sistema debuggea. Otros módulos muy importantes que se pueden añadir glob Para expansión del nombre de los archivos socket Para conexiones de red y comunicación entre procesos thread y queue Para manejar threads time Para acceder a horas, minutos, segundos, etc y fechas del sistema sfcntl Para controlar archivos en bajo nivel Pero inicialmente hablaremos de sys y os, y trataremos de profundizar en ellos. Genéricamente sys, maneja los componentes que necesita el interprete de python para funcionar, como los paths de instalación, por ejemplo y os, maneja las variables y funciones que lo relacionan
con el sistema donde esta corriendo. Al principio la diferencia no parece mucha, pero a medida que veamos el uso de cada uno veremos como se va aclarando. tipeemos en el python shell de WING o en cualquier consola interactiva de python lo siguiente: import sys, os
Sabemos que en WING al tipear sys habiendo estado importado nos mostrara la ayuda en la pestaña SOURCE ASISTANT.
Si no tuviéramos la suerte de tener un editor que nos ayuda tanto como podríamos obtener información de estos módulos manualmente? Si hacemos un len(dir(sys)) nos dará la cantidad de atributos que posee este modulo
En este caso en Windows el resultado es 52 entre métodos y variables internas, en el caso del modulo os, tendrá 118 en mi caso.
Al tipear os y punto nos aparecen los atributos de os, podemos ver si alguno de ellos por ejemplo path tiene también atributos propios y cuantos tiene.
En este caso vemos que el mismo path tiene 43 lo cual con el wing podríamos corroborar al tipear os.path y al poner el punto nuevamente saldrán los atributos de path.
Si recorremos el menú desplegable vemos que son 43 en mi maquina lo mismo que dijo len. Por supuesto la forma manual de listar los atributos de cualquier modulo es usando dir(), una vez que importamos el modulo correspondiente en este caso sys hacemos .. >>> dir (sys) ['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', '__stdin__', '__stdout__', '_getframe', 'api_version', 'argv', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'exc_clear', 'exc_info', 'exc_type', 'excepthook', 'exec_prefix', 'executable', 'exit', 'exitfunc', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencoding', 'getrecursionlimit', 'getrefcount', 'getwindowsversion', 'hexversion', 'maxint', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'setcheckinterval', 'setprofile', 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'version', 'version_info', 'warnoptions', 'winver'] >>> Lo que nos devolverá todos los atributos del modulo sys en una lista, si no tuviéramos la ayuda con menúes desplegables, sabríamos al mirar dicha lista que existe algo llamado sys.version por ejemplo, ya que version esta en la lista de atributos del modulo sys. Si queremos información mas detallada acerca de un modulo podemos usar __doc__, probemos en el modulo sys a ver que nos devuelve. >>> sys.__doc__ "This module provides access to some objects used or maintained by the\ninterpreter and to functions that interact strongly with the interpreter.\n\nDynamic objects:\n\nargv -- command line arguments; argv[0] is the script pathname if known\npath -- module search path; path[0] is the script directory, else ''\nmodules -- dictionary of loaded modules\n\ndisplayhook -- called to show results in an interactive session\nexcepthook -- called to handle any uncaught exception other than ........................... Que es la misma ayuda que nos mostraba el wing en la pestaña SOURCE ASISTANT, aunque se ve fea si hacemos print sys.__doc__ se vera mejor ya que el print armara el texto con el formato que trae. >>> print sys.__doc__ This module provides access to some objects used or maintained by the interpreter and to functions that interact strongly with the interpreter.
Dynamic objects: argv -- command line arguments; argv[0] is the script pathname if known path -- module search path; path[0] is the script directory, else '' modules -- dictionary of loaded modules displayhook -- called to show results in an interactive session excepthook -- called to handle any uncaught exception other than SystemExit To customize printing in an interactive session or to install a custom top-level exception handler, assign other functions to replace these. exitfunc -- if sys.exitfunc exists, this routine is called when Python exits Assigning to sys.exitfunc is deprecated; use the atexit module instead. stdin -- standard input file object; used by raw_input() and input() stdout -- standard output file object; used by the print statement stderr -- standard error object; used for error messages By assigning other file objects (or objects that behave like files) to these, it is possible to redirect all of the interpreter's I/O. last_type -- type of last uncaught exception last_value -- value of last uncaught exception last_traceback -- traceback of last uncaught exception These three are only available in an interactive session after a traceback has been printed. exc_type -- type of exception currently being handled exc_value -- value of exception currently being handled exc_traceback -- traceback of exception currently being handled The function exc_info() should be used instead of these three, because it is thread-safe. Static objects: maxint -- the largest supported integer (the smallest is -maxint-1) maxunicode -- the largest supported character builtin_module_names -- tuple of module names built into this interpreter version -- the version of this interpreter as a string version_info -- version information as a tuple hexversion -- version information encoded as a single integer copyright -- copyright notice pertaining to this interpreter platform -- platform identifier executable -- pathname of this Python interpreter prefix -- prefix used to find the Python library exec_prefix -- prefix used to find the machine-specific Python library dllhandle -- [Windows only] integer handle of the Python DLL winver -- [Windows only] version number of the Python DLL __stdin__ -- the original stdin; don't touch! __stdout__ -- the original stdout; don't touch! __stderr__ -- the original stderr; don't touch! __displayhook__ -- the original displayhook; don't touch!
__excepthook__ -- the original excepthook; don't touch! Functions: displayhook() -- print an object to the screen, and save it in __builtin__._ excepthook() -- print an exception and its traceback to sys.stderr exc_info() -- return thread-safe information about the current exception exc_clear() -- clear the exception state for the current thread exit() -- exit the interpreter by raising SystemExit getdlopenflags() -- returns flags to be used for dlopen() calls getrefcount() -- return the reference count for an object (plus one :-) getrecursionlimit() -- return the max recursion depth for the interpreter setcheckinterval() -- control how often the interpreter checks for events setdlopenflags() -- set the flags to be used for dlopen() calls setprofile() -- set the global profiling function setrecursionlimit() -- set the max recursion depth for the interpreter settrace() -- set the global debug tracing function Allí vemos una descripción del modulo y sus funciones muy útil, y ahora se ve alineada y legible. La función help() que trae python incluida sera similar a esto ultimo, si la usamos en una shell interactiva pero si la usamos en una consola veremos que mientras print muestra toda la información hasta el final y no hay forma de pararla ni hacer scroll hacia arriba, perdiéndonos de leer las primeras lineas que quedan ocultas, con help vemos que nos muestra la primera parte de la info y corta para que nos siga mostrando solo cuando nosotros toquemos la barra espaciadora, pasara a mostrar la parte siguiente
Al apretar la barra salta a la segunda parte de la información como si saltáramos de pagina en pagina, permitiendonos leer en forma interactiva.
Veamos para practicar un script sencillo que realiza esta misma tarea en forma un poco mas sofisticada, lo llamaremos more.py, creamos un nuevo script vacío en el WING y le colocamos ese nombre.
Veamos como funciona, define una función llamada more(), si se usa como script independiente, se cumplirá que __name__ sera igual a __main__ y allí importara sys y abriremos un archivo de texto que querríamos leer en forma interactiva en una consola. Sabemos que sys.argv[1] sera un argumento con el cual se corrió el script, por lo tanto el formato de uso seria tipear en una consola.
more (nombre del archivo de texto a leer). Veamos como trabaja. def more(text, numlines=15): lines = text.split('\n') while lines: chunk = lines[:numlines] lines = lines[numlines:] for line in chunk: print line if lines and raw_input('More?') not in ['y', 'Y']: break if _ _name_ _ == '_ _main_ _': import sys more(open(sys.argv[1]).read( ), 10) Allí vemos que el primer parámetro que le pasamos a la función more(), es una variable con el texto que lee del archivo, que en este caso lo hace con open(sys.argv[1]).read(), que si el archivo se llamara pepe.txt seria equivalente a open('pepe.txt').read() Abre el archivo 'pepe.txt' con open y a ello le aplica el método read() con lo cual le pasara a la función more en la asignación, la string con el texto leído. El otro argumento de la función more es la cantidad de lineas que debe mostrar, en este caso trae un valor por default de 15, pero ese valor es machacado por el nuevo que le pasamos que es 10, si no le pasáramos nada, obviamente usaría 15. Probemoslo en un archivo pepe.txt cuyo contenido sea el resultado de sys.__doc__ el cual copiamos y pegamos sin formato en dicho archivo.
Copiemos dicho archivo a la misma carpeta del script more.py.
Ahora pongamos un breakpoint en el script y corramoslo con DEBUG, cuando nos sale la ventana para colocar el argumento, le ponemos el nombre del archivo pepe.txt.
Una vez que para, vamos traceando con f7 y llegamos a la llamada a la función more, aquí para no complicar lo mejor es poner un breakpoint dentro de la función y apretar DEBUG para que pare.
Bueno allí estamos dentro de la función more(), ya leyó el contenido de pepe.txt y en la variable text, tendremos dicho contenido crudo, si tipeamos text en la debug probe.
Allí vemos que tiene el texto sin formato que leyó del archivo. Aquí comprobamos que el script del libro tiene un error pues luego de pasar por split() que debería dividir el texto cuando encuentra '\n' no lo hace y el resultado es una lista de un solo elemento y no como debería ser, una lista de muchos elementos ya que debería dividirse en cada '\n' >>> lines ['"This module provides access to some objects used or maintained by the\\ninterpreter and to functions that interact strongly with the interpreter.\\n\\nDynamic objects:\\n\\nargv -- command line arguments; argv[0] is the script pathname if known\\npath -- module search path; path[0] is the script directory, else \'\'\\nmodules -- dictionary of loaded modules\\n\\ndisplayhook -- called to show results in an interactive session\\nexcepthook -- called to handle any uncaught exception other than SystemExit\\n To customize printing in an interactive session or to install a custom\\n toplevel exception handler, assign other functions to replace these.\\n\\nexitfunc -- if sys.exitfunc exists, this routine is called when Python exits\\n Assigning to sys.exitfunc is deprecated; use the atexit module instead.\\n\\nstdin -- standard input file object; used by raw_input() and input()\\nstdout -- standard output file object; used by the print statement\\nstderr -- standard error object; used for error messages\\n B', "y assigning other file objects (or objects that behave like files)\\n to these, it is possible to redirect all of the interpreter's I/O.\\n\\nlast_type -- type of last uncaught
exception\\nlast_value -- value of last uncaught exception\\nlast_traceback -- traceback of last uncaught exception\\n These three are only available in an interactive session after a\\n traceback has been printed.\\n\\nexc_type -- type of exception currently being handled\\nexc_value -- value of exception currently being handled\\nexc_traceback -- traceback of exception currently being handled\\n The function exc_info() should be used instead of these three,\\n because it is threadsafe.\\n\\nStatic objects:\\n\\nmaxint -- the largest supported integer (the smallest is -maxint1)\\nmaxunicode -- the largest supported character\\nbuiltin_module_names -- tuple of module names built into this interpreter\\nversion -- the version of this interpreter as a string\\nversion_info -- ver sion information as a tuple\\nhexversion -- versio", "n information encoded as a single integer\\ncopyright -- copyright notice pertaining to this interpreter\\nplatform -- platform identifier\\nexecutable -- pathname of this Python interpreter\\nprefix -- prefix used to find the Python library\\nexec_prefix -- prefix used to find the machine-specific Python library\\ndllhandle - [Windows only] integer handle of the Python DLL\\nwinver -- [Windows only] version number of the Python DLL\\n__stdin__ -- the original stdin; don't touch!\\n__stdout__ -- the original stdout; don't touch!\\n__stderr__ -- the original stderr; don't touch!\\n__displayhook__ -- the original displayhook; don't touch!\\n__excepthook__ -the original excepthook; don't touch!\\n\\nFunctions:\\n\\ndisplayhook() -- print an object to the screen, and save it in __builtin__._\\nexcepthook() -- print an exception and its traceback to sys.stderr\\nexc_info() -return thread-safe information about the current exception\ \nexc_clear() -- clear the exception state for the current thread\\nexit()", ' -- exit the interpreter by raising SystemExit\\ngetdlopenflags() -- returns flags to be used for dlopen() calls\\ngetrefcount() -return the reference count for an object (plus one :-)\\ngetrecursionlimit() -- return the max recursion depth for the interpreter\\nsetcheckinterval() -- control how often the interpreter checks for events\\nsetdlopenflags() -- set the flags to be used for dlopen() calls\\nsetprofile() -- set the global profiling function\\nsetrecursionlimit() -- set the max recursion depth for the interpreter\\nsettrace() -- set the global debug tracing function\\n"', ' '] Vemos que no dividió bien y eso es porque al decirle que divida en '\n' lines = text.split('\n') como '\n' es el comando especial para saltar de pagina, lo interpreta así , por lo tanto para que funcione debemos agregarle otra barra delante. lines = text.split('\\n') De esta forma la barra le quitara la funcionalidad de carácter especial y pasara a dividir el texto donde encuentre cada '\n' como corresponde probemos ahora de nuevo. Al ejecutar el split() veamos que contiene ahora la variable lines. >>> lines ['"This module provides access to some objects used or maintained by the', 'interpreter and to functions that interact strongly with the interpreter.', '', 'Dynamic objects:', '', 'argv -- command line arguments; argv[0] is the script pathname if known', "path -- module search path; path[0] is the script directory, else ''", 'modules -- dictionary of loaded modules', '', 'displayhook -- called to show results in an interactive session', 'excepthook -- called to handle any uncaught exception other than SystemExit', ' To customize printing in an interactive session or to install a custom', ' top-level exception handler, assign other functions to replace these.', '', 'exitfunc -- if sys.exitfunc exists, this routine is called when Python exits', ' Assigning to sys.exitfunc is deprecated; use the atexit module instead.', '', 'stdin -- standard input file object; used by raw_input() and input()', 'stdout -- standard
output file object; used by the print statement', 'stderr -- standard er ror object; used for error messages', ' B\ny assigning other file objects (or objects that behave like files)', " to these, it is possible to redirect all of the interpreter's I/O.", '', 'last_type -- type of last uncaught exception', 'last_value -- value of last uncaught exception', 'last_traceback -- traceback of last uncaught exception', ' These three are only available in an interactive session after a', ' traceback has been printed.', '', 'exc_type -- type of exception currently being handled', 'exc_value -value of exception currently being handled', 'exc_traceback -- traceback of exception currently being handled', ' The function exc_info() should be used instead of these three,', ' because it is thread-safe.', '', 'Static objects:', '', 'maxint -- the largest supported integer (the smallest is -maxint1)', 'maxunicode -- the largest supported character', 'builtin_module_names -- tuple of module names built into this interpreter', 'version -- the version of this interpret er as a string', 'version_info -- version information as a tuple', 'hexversion -- versio\nn information encoded as a single integer', 'copyright -- copyright notice pertaining to this interpreter', 'platform -platform identifier', 'executable -- pathname of this Python interpreter', 'prefix -- prefix used to find the Python library', 'exec_prefix -- prefix used to find the machine-specific Python library', 'dllhandle -- [Windows only] integer handle of the Python DLL', 'winver -- [Windows only] version number of the Python DLL', "__stdin__ -- the original stdin; don't touch!", "__stdout__ -- the original stdout; don't touch!", "__stderr__ -- the original stderr; don't touch!", "__displayhook__ -the original displayhook; don't touch!", "__excepthook__ -- the original excepthook; don't touch!", '', 'Functions:', '', 'displayhook() -- print an object to the screen, and save it in __builtin__._', 'excepthook() -- print an exception and its traceback to sys.stderr', 'exc_info() -- retur n thread-safe information about the current exception', 'exc_clear() -- clear the exception state for the current thread', 'exit()\n -- exit the interpreter by raising SystemExit', 'getdlopenflags() -- returns flags to be used for dlopen() calls', 'getrefcount() -- return the reference count for an object (plus one :-)', 'getrecursionlimit() -- return the max recursion depth for the interpreter', 'setcheckinterval() -- control how often the interpreter checks for events', 'setdlopenflags() -- set the flags to be used for dlopen() calls', 'setprofile() -- set the global profiling function', 'setrecursionlimit() -- set the max recursion depth for the interpreter', 'settrace() -- set the global debug tracing function', '"\n '] >>> Vemos que ahora funciono correctamente dividiendo el texto en una lista de varios campos, dividiendo donde estaban las '\n'.
Ahora entramos en un WHILE, que se repetirá mientras haya lineas o sea mientras while lines sea true que es lo mismo.
En la siguiente linea divide lines con un índice slice, desde 0 hasta el máximo de lineas que
queremos mostrar que esta en numlines y eso lo asigna a chunk, veamos pasemosla con f7. >>> chunk ['"This module provides access to some objects used or maintained by the', 'interpreter and to functions that interact strongly with the interpreter.', '', 'Dynamic objects:', '', 'argv -- command line arguments; argv[0] is the script pathname if known', "path -- module search path; path[0] is the script directory, else ''", 'modules -- dictionary of loaded modules', '', 'displayhook -- called to show results in an interactive session'] >>> Vemos que chunk solo tiene las primeras 10 lineas que queremos mostrar y en la siguiente instrucción quita estas lineas de la variable lines. lines = lines[numlines:] ya que corta con otro índice slice desde 10 hasta el final, y lo vuelve asignar a lines para que si se repite el ciclo continué a partir de donde ya mostró. >>> lines ['excepthook -- called to handle any uncaught exception other than SystemExit', ' To customize printing in an interactive session or to install a custom', ' top-level exception handler, assign other functions to replace these.', '', 'exitfunc -- if sys.exitfunc exists, this routine is called when Python exits', ' Assigning to sys.exitfunc is deprecated; use the atexit module instead.', '', 'stdin -- standard input file object; used by raw_input() and input()', 'stdout -- standard output file object; used by the print statement', 'stderr -- standard error object; used for error messages', ' B\ny assigning other file objects (or objects that behave like files)', " to these, it is possible to redirect all of the interpreter's I/O.", '', 'last_type -- type of last uncaught exception', 'last_value -- value of last uncaught exception', 'last_traceback -- traceback of last uncaught exception', ' These three are only available in an interactive session after a', ' traceback has been pri nted.', '', 'exc_type -- type of exception currently being handled', 'exc_value -- value of exception currently being handled', 'exc_traceback -- traceback of exception currently being handled', ' The function exc_info() should be used instead of these three,', ' because it is thread-safe.', '', 'Static objects:', '', 'maxint -- the largest supported integer (the smallest is -maxint-1)', 'maxunicode -- the largest supported character', 'builtin_module_names -- tuple of module names built into this interpreter', 'version -- the version of this interpreter as a string', 'version_info -- version information as a tuple', 'hexversion -- versio\nn information encoded as a single integer', 'copyright -- copyright notice pertaining to this interpreter', 'platform -- platform identifier', 'executable -- pathname of this Python interpreter', 'prefix -- prefix used to find the Python library', 'exec_prefix -- prefix used to find the machine-specific Python library', 'dllhandle -- [Windows only] integer handle of the Python DLL', 'winver -- [Windows only] version number of the Python DLL', "__stdin__ -- the original stdin; don't touch!", "__stdout__ -- the original stdout; don't touch!", "__stderr__ -- the original stderr; don't touch!", "__displayhook__ -- the original displayhook; don't touch!", "__excepthook__ -- the original excepthook; don't touch!", '', 'Functions:', '', 'displayhook() -- print an object to the screen, and save it in __builtin__._', 'excepthook() -- print an exception and its traceback to sys.stderr', 'exc_info() -- return thread-safe information about the current exception', 'exc_clear() -- clear the exception state for the current thread', 'exit()\n -- exit the interpreter by raising SystemExit', 'getdlopenflags() -- returns flags to be used for dlopen() calls', 'getrefcount() -return the reference count for an object (plus one :-)', 'getrecursionlimit() -- return the max recursion depth for the interpreter', 'setcheckinterval() -- control how often the interpreter checks for events', 'setdlopenflags() -- set the flags to be used for dlopen() calls', 'setprofile() -- set the global profiling function', 'setrecursionlimit() -- set the max recursion depth for the interpreter', 'settrace() -- set the global debug tracing function', '"\n ']
>>> Vemos que lo que queremos imprimir la primera vez estará en chunk y lo que aun no se imprimió seguirá en lines.
Luego viene el for que imprimirá todas las 10 primeras lineas y luego salta al if, alli según lo que tipeemos seguirá imprimiendo o se quedara esperando en el raw_input() mientras leemos las diez primeras lineas mostradas. El if se puede leer casi de corrido es la condición de salida según lo que tipeamos. Vemos que hay dos miembros de un AND el primero es if lines que sera verdadero mientras haya mas lineas que mostrar.
Vemos que mientras cualquiera de ambos miembros sea falso, el resultado sera falso, y no ira al break si no que continuara loopeando, ya vimos que el primer miembro mientras haya lineas es verdadero veamos que pasa con el segundo miembro, si es falso el resultado lo sera y continuara sin ir al break.
Cada vez que tipee la y en el raw_input el resultado sera falso.
Compruebo que solo sera verdadero cuando no sea y o Y lo que tipee, de esta forma mientras tipee y o Y sera falso y seguirá loopeando y continuara, y cuando se terminen las lineas o tipee otra letra saldrá por el break.
Voy el directorio donde esta el script y el archivo de texto a leer y tipeo more.py seguido del nombre del archivo de texto que leerá.(en mi caso more.py pepe.txt)
Allí imprimió las primeras 10 lineas y me pregunta si quiero continuar su apreto y o Y muestra las 10 lineas siguientes sino sale fuera por el break.
Allí esta y puedo ver todo el documento así o interrumpir el proceso.
Allí al apretar la letra r se acabo el script.
METODOS PARA STRINGS En la etapa 1 vimos ejemplos de métodos y a pesar de ser el anterior un programita simple, usamos métodos para leer de un archivo, para dividir las string con split(), etc, en la mayoría de los programas hechos en python los métodos ven acción, aquí algunos ejemplos que refrescaremos sobre métodos para strings que usaremos. Recordemos que las strings son inmutables por lo tanto los cambios se deben asignar a otra string ya que la original no cambiara.
El método para strings find, busca la primera ocurrencia de la string que le pasamos como argumento, en este caso SPAM dentro de una string mayor a la cual le aplicamos el método (en este caso str) y nos devuelve el offset donde la halla, aquí vemos que en la string 'xxxSPAMxxx' encuentra 'SPAM' la primera vez en el offset 3 y nos devuelve ese valor. Otro método para string muy útil es replace(), recordemos que el resultado hay que asignarlo a otra string, porque las strings son inmutables, no podes cambiarlas y que quede en la misma variable el resultado. >>> str = 'xxaaxxaa' >>> str.replace('aa', 'SPAM') 'xxSPAMxxSPAM' si queremos asignarlo a una variable llamada nueva, dicha variable contendría la nueva string con los cambios, mientras que la original str quedara igual como era originalmente, esto se aplica para todos los ejemplos que veremos. nueva=str.replace('aa', 'SPAM') El método replace(), si la encuentra, sustituye la string del primer argumento en este caso 'aa', por la segunda string que le pasamos como argumento o sea reemplazara donde halla las 'aa' por 'SPAM' en este caso. El método strip() quita de la string principal los caracteres que le pasamos como argumento. >>> str = 'xxxSPAMxxx' >>> str.strip('x') 'SPAM'
>>> El método lower() pasara a minúsculas las letras que encuentre en mayúscula >>> str = 'SHRUBBERY' >>> str.lower( ) 'shrubbery' El método is isalpha() testeara los caracteres de una string, si son solo letras devuelve True, si son números o símbolos devuelve False. >>> str='45' >>> str.isalpha() False >>> str='ABC' >>> str.isalpha() True >>> str='ABC?' >>> str.isalpha() False Igualmente isdigit() vera si es una string cuyos caracteres son todos números para la string anterior ABC? si lo aplicamos sera falso, en cambio para str='1234' sera verdadero. >>> str.isdigit( ) False
>>> str='1234' >>> str.isdigit() True En este caso importando el modulo string tenemos que lowercase nos devuelve todas la letras del abecedario en minúscula, es una constante de dicho modulo. >>> import string >>> string.lowercase
'abcdefghijklmnopqrstuvwxyz' Algunos ejemplos SPLIT dividirá la string principal, en este caso la divide donde encuentre comas que es el argumento que le pase.
>>> str = 'aaa,bbb,ccc' >>> str.split(',') ['aaa', 'bbb', 'ccc'] Aquí dividirá donde encuentre cualquier espacio en blanco, como '\n' es un salto de carro, por supuesto también allí habrá espacio en blanco y lo divide también allí. >>> str = 'a b\nc\nd' >>> str.split( ) ['a', 'b', 'c', 'd'] JOIN concatena las substrings en una string única, en este ejemplo una de las strings es una lista de strings, igual las unirá cada campo de la lista con la otra string como vemos. >>> delim = 'NI' >>> delim.join(['aaa', 'bbb', 'ccc']) 'aaaNIbbbNIccc' En este caso hace lo mismo pero la primera string es un espacio vacío, así que hará una string mayor con un espacio entre cada string de la lista. >>> ' '.join(['A', 'dead', 'parrot']) 'A dead parrot'
LIST hará una lista con cada carácter de la string principal. >>> chars = list('Lorreta') >>> chars ['L', 'o', 'r', 'r', 'e', 't', 'a'] Y si al resultado anterior le agregamos un campo mas con append(), un signo de exclamación y le aplicamos join() con una string vacía, armara de nuevo la string original mas el signo de admiración ya que sumara cada campo de la lista con el string vacío, haciendo una sola string como en los ejemplos anteriores. >>> chars ['L', 'o', 'r', 'r', 'e', 't', 'a']
>>> chars.append('!') >>> ''.join(chars) 'Lorreta!' INT y EVAL convertirán una string en el numero que representa >>> int("42") 42 >>> eval("42") 42 Para pasar de integer a string podemos usar STR, REPR o hacer un format string >>> str(42) '42' >>>repr(42) '42' >>>"%d" % 42 '42' Ya conocemos la forma de concatenar o sumar en el primer campo sumamos strings y en el segundo integers. >>> "42" + str(1), int("42") + 1 ('421', 43)
OPERACION DE ARCHIVOS LO BASICO Vimos como el script more.py abría un archivo de texto para leer su contenido aquí refrescamos la memoria con los métodos para abrir y leer el contenido de archivos. open('file').read( ) open('file').read(N)
# lee un archivo entero y lo devuelve como string # lee los siguientes N bytes y los devuelve como string
open('file').readlines( )
#lee un archivo entero y lo devuelve como lista de lineas
open('file').readline( )
# lee la siguiente linea
Algunos ejemplos de uso vemos como crea un archivo llamado spam.txt y le escribe 'spam'*5 y lo cierra. >>> file = open('spam.txt', 'w')
# create file spam.txt
>>> file.write(('spam' * 5) + '\n') >>> file.close( ) Luego lo abre para leerlo y vemos su contenido en la variable text. >>> file = open('spam.txt')
# or open('spam.txt').read( )
>>> text = file.read( ) >>> text 'spamspamspamspamspam\n'
USANDO PROGRAMAS EN DOS FORMAS Vimos en varios scripts que hicimos, inclusive en el more.py que existe la posibilidad de que un programa se use como script principal o como modulo importado por otro. Cada modulo de python tiene una variable propia llamada __name__ que python inicializara como __main__, solo si es ejecutado como programa, no cuando es importado por otro modulo. En el script more.py es esta parte del código if __name__ == '__main__': import sys more(open(sys.argv[1]).read( ), 10) Vemos que la llamada a la función more solo ocurre cuando es ejecutado como programa principal, ya que en ese caso la variable __name__ sera igual a __main__, si es importado por otro modulo, podrá llamar a la función more(), pero no desde aquí. Bueno en esta parte hemos repasado algunos puntos que ya habíamos visto pero que utilizaremos en las partes siguientes por lo cual es bueno refrescar y no esta de mas. Hasta la parte 12 Ricardo Narvaja
PARTE 12:PROFUNDICEMOS EN EL MODULO SYS En la parte anterior dijimos que íbamos a hablar del modulo sys y os, refrescamos algunos de los métodos que usaremos para profundizar en dichos modulos, ahora llego el momento de entrar a trabajar con ambos, en este caso empezamos por sys. PLATAFORMA Y VERSION Como otros módulos, que nos dan información sobre tópicos que abarcan su funcionamiento, sys nos entrega información sobre el sistema operativo y la versión de python sobre la cual estamos trabajando. >>> import sys >>> sys.platform 'win32' Vemos que platform es una variable que contiene una string que identifica el sistema operativo en el cual estamos trabajando, en este caso win32, por estar trabajando en Windows. >>> sys.version '2.4.3 (#69, Apr 11 2006, 15:32:42) [MSC v.1310 32 bit (Intel)]' El mismo caso se aplica a la variable version la cual contendrá la versión de python en la cual estamos trabajando, si un script debe funcionar diferente si trabaja en una plataforma especifica, lo que se hace es chequear con platform en que sistema esta corriendo y según ello se ejecuta una acción o otra, lo mismo ocurre con la versión de python, si estamos usando un nuevo feature que salio en python 2.5 por ejemplo, el script puede testear que si esta en python 2.4 o anteriores no use ese feature o advierta que no funcionara si no usamos 2.5. LOS PATHS DE MODULOS SYS tiene una función llamada PATH que nos mostrara los paths en nuestro sistema, en los cuales acepta módulos importados, ya vimos en la etapa 1 como configurar de diferentes maneras estos PATHS, incluso el llamado PYTHONPATH, veamos que me devuelve. >>> sys.path ['', 'C:\\Archivos de programa\\Wing IDE 3.0\\src\\debug\\tserver', 'C:\\WINDOWS\\system32\\python24.zip', 'C:\\', 'C:\\Python24\\DLLs', 'C:\\Python24\\lib', 'C:\\Python24\\lib\\plat-win', 'C:\\Python24\\lib\\lib-tk', 'C:\\Python24', 'C:\\Python24\\lib\\sitepackages', 'C:\\Python24\\lib\\site-packages\\win32', 'C:\\Python24\\lib\\site-packages\\win32\\lib', 'C:\\Python24\\lib\\site-packages\\Pythonwin', 'C:\\Python24\\lib\\site-packages\\wx-2.6-msw-ansi'] Allí vemos las carpetas lib y algunos paths mas que configura WING para su uso, en cualquiera de estos paths que coloquemos un modulo se podrá importar. Como vemos la lista de paths, es realmente una LISTA y podemos apendearle mas paths si queremos. >>> sys.path.append(r'C:\mydir')
De esta forma un script agrega un path propio a la lista de paths que tiene el sistema y entonces también aceptara módulos en dicha carpeta mientras dicho script este funcionando, el tema es que cada vez que corra deberá setearlo, porque los cambios hechos de esta forma, se pierden al terminar de correr el script, pero en ciertos casos puede ser útil, que el script al iniciarse setee un path que necesita como valido para importar módulos y lógicamente funcionara mientras el programa corre. WINDOWS PATHS Vemos que la variable path nos devuelve los mismos con doble barra invertida en vez de simple sea o no sea el carácter siguiente un carácter que se convertirá en especial, python prefiere cada vez que ve una barra invertida en un path, ponerla doble que tener que chequear si el carácter siguiente es especial o no. Sabemos que la barra invertida simple convierte algunos caracteres en un carácter especial, de esta forma al ponerlo doble los anula, pero si nosotros copiamos un path y lo necesitamos usar fuera de python, deberemos quitarle la segunda barra invertida, por ejemplo. “C:\\mydir” sera verdaderamente C:\mydir fuera de python, lo mismo si en python usamos la r delante para evitar todos estos problemas. r"C:\mydir" lógicamente esto corresponderá también al directorio C:\mydir También es cierto que la mayoría de los módulos de python reconocen ambas barras invertidas ( \ y /) como separación en paths, y en Windows funcionan ambas también, por lo tanto si uno usa como separación en paths, la otra barra ( / ), que es la única que acepta Linux para ello, nuestro script sera portable a cualquier versión de LINUX y tendrá menos problemas con los caracteres especiales. Allí vemos en el script more.py de la parte anterior, que si ubico el archivo pepe.txt en C: aun usando la barra (/), lo halla como valido, y tendrá menos problemas al poder correrse en LINUX y no sera nunca interpretado como carácter especial.
Aquí vemos como la barra (/) no transforma al carácter siguiente en especial. Usando la barra invertida (\) la n se transforma en carácter especial que realiza un salto de linea >>> print "pepe\npepe" pepe pepe Usando la barra (/) es un carácter mas y se imprime sin saltar linea ni nada. >>> print "pepe/npepe" pepe/npepe >>> LA TABLA DE MODULOS IMPORTADOS El modulo sys nos permite usar modules para devolvernos un diccionario con los modulos cargados hasta este momento tanto por python como por nosotros cuando importamos. >>> sys.modules {'tokenize': , 'copy_reg': , 'sre_compile': , 'threading': , 'locale': , '_sre': , '__main__': , 'site': , '__builtin__': , 'atexit': , 'encodings': , 'os.path': , 'strop': , 'imp': , 'errno': , 'encodings.codecs': None, 'gettext': , 'sre_constants': , 're': , 'collections': , 'ntpath': , 'UserDict': , 'encodings.exceptions': None, 'nt': , 'stat': , 'zipimport': , 'string': , 'warnings': , 'encodings.types': None, '_codecs': , 'inspect': , 'encodings.cp1252': , 'sys': , 'codecs': , 'copy': , 'types': , 'struct': , '_locale': , 'thread': , 'sre': , 'signal': , 'traceback': , 'linecache': , 'token': , 'opcode': , 'encodings.aliases': , 'time': , 'exceptions': , 'sre_parse': , 'os': , 'dis': } Allí vemos los módulos que han sido importados hasta este momento, tanto por el mismo python al arrancar como el sys que importe recientemente. Si ahora importo otro por ejemplo pydbg (lo debo tener entre las libs, si no puedo importar cualquier otro es solo para mostrar) >>>import pydbg y me fijo en la lista de módulos, también se agrego allí. >>> sys.modules {'_sre': , 'ctypes.ctypes': None, '__builtin__': , 'ctypes.os': None, 'ctypes._endian': , 'pydbg.system_dll': , 'pydbg': , 'ctypes.sys': None, 'struct': , 'pydbg.windows_h': , 'errno': , 'binascii': , 'sre_constants': , 'imp': , 'collections': , 'types': , 'tokenize': , 'pydbg.ctypes': None, 'zipimport': , 'string': , 'pydbg.memory_snapshot_context': , '_codecs': , 'pydbg.pdx': , 'pydbg.pydbg_client': , 'encodings.cp1252': , 'pydbg.pydasm': , 'pydbg.os': None, 'pydbg.pydbg_core': , 'copy': , 'pydbg.sys': None, 'nt': , 'signal': , 'pydbg.my_ctypes': , 'threading': , 'token': , 'encodings.aliases': , 'exceptions': , 'sre_parse': , 'dis': , 'copy_reg': , 'sre_compile': , 'locale': , '_random': , 'encodings': , 'site': , 'pydbg.struct': None, 'atexit': , '__main__': , 'pydbg.hardware_breakpoint': , 'strop': , 'pydbg.pydbg': , 'pydbg.breakpoint': , 'gettext': , 're': , 'ntpath': , 'pydbg.memory_snapshot_block': , 'encodings.exceptions': None, 'ctypes._ctypes': None, 'encodings.codecs': None, 'math': , 'pydbg.signal': None, 'stat': , 'warnings': , 'encodings.type s': None, 'UserDict': , 'inspect': , '_ctypes': , 'sys': , 'ctypes': , 'pydbg.memory_breakpoint': , 'ctypes.struct': None, 'codecs': , 'os.path': , 'pydbg.random': None, '_locale': , 'thread': , 'sre': , 'random': , 'traceback': , 'linecache': , 'opcode': , 'pydbg.defines': , 'time': , 'os': } >>> Por supuesto se agrego el modulo pydbg que yo importe y el mismo al ejecutarse también importo los módulos que el necesita para funcionar y todos están en la lista de módulos cargados hasta ahora. DETALLES DE UNA EXCEPCION El modulo sys nos provee de información de la ultima excepción que ha ocurrido a través de la función exc_info( ) . >>> try: ... raise IndexError ... except: ... print sys.exc_info() ... (, 0x00C77E40>, )
>> try: ... a=0 ... b=3/a ... except: ... print sys.exc_info() ... (, , ) En este otro ejemplo realizamos una división por cero, y lógicamente nos reporta esa excepción la cual imprimimos. Vemos que el reporte es una tupla de tres campos, el tercero es un objeto traceback, veremos como usamos eso, modifiquemos el ejemplo haciéndolo un script. Allí importamos el modulo traceback, ya que el tercer campo es un objeto de traceback, y cuando se genera la excepción imprimimos uno a uno los tres campos del reporte, vemos que el tercero como hemos dicho es un objeto de traceback, veamos que podemos hacer con el, pongamos un breakpoint allí en ese tercer print. Si cuando para en el breakpoint tipeo en el DEBUG PROBE >>> traceback.print_tb(sys.exc_info()[2]) File "c:\Documents and Settings\ricnar456\Escritorio\ejemplo.py", line 5, in ? b=3/a
Me dice exactamente donde ocurrió el error, lo cual puede ser útil para automatizar reportes de errores en un script, realmente el modulo traceback tiene muchísimos atributos útiles, pero este nos permitiría completar el script con un reporte mas detallado que antes.
Allí lo vemos, el reporte ahora es mas completo de cualquier manera, estamos estudiando el modulo sys y no el traceback solo fue un aporte para completar la idea de como manejar y reportar mejor las excepciones. OTRAS TOOLS ADICIONALES EXPORTADAS POR SYS El modulo sys exporta tools que veremos en el contexto en el cual las usemos mas adelante en esta segunda etapa, como el manejo de argumentos a través de una lista de strings llamada sys.argv, también la entrada y salida estándar se maneja por medio de sys.stdin y sys.stdout y el de error estándar con sys.stderr. También la salida de un programa se puede forzar mediante un sys.exit, como hemos dicho todos estos los cubriremos mas adelante cuando los utilicemos. Hasta la parte 13 Ricardo Narvaja
13-2DA ETAPA DEL CURSO DE PYTHON-PROFUNDICEMOS EN EL MODULO OS Así como hemos visto para que sirve el modulo sys y como su función es relacionarnos con el interprete de python de acuerdo a la maquina en la cual estamos trabajando, vimos como nos proveía de información sobre el sistema en el cual corre el interprete de python que estamos usando, asimismo como la version de python. Como a través del modulo path, sabíamos los paths donde aceptara módulos importados según como esta configurado python en la maquina en la cual corre, como podemos agregar mas paths temporalmente, como podíamos averiguar la tabla de modulos que están importados, saber información de las excepciones que produjo python, y el manejo de las entradas y salidas estándar del sistema a nuestros scripts. Realmente vemos que la funcionalidad de sys esta orientada a la relación entre el interprete de python y el sistema donde esta alojado, todas sus funcionalidades son relativas a ello, ahora veremos otro modulo muy importante que se llama os, y que a diferencia del anterior es la relación con las llamadas de sistema que podemos usar en un programa, veremos los ejemplos a continuación así se aclarara la diferencia fácilmente.
INTRODUCIENDO OS Como dijimos, os es el mas largo de los dos módulos de sistema, y contiene las mas usuales llamadas al sistema operativo que usamos en los programas y scripts. Trabaja sobre directorios, procesos, variables del shell, y sirve como una interfase a las mas importantes llamadas de sistema, los scripts que importan os y os.path normalmente no tienen que ser modificados para correr en cualquier plataforma. LAS HERRAMIENTAS MAS COMUNES DE OS Tasks
Tools
Shell variables Running programs Spawning processes Descriptor files, locks File processing Administrative tools Portability tools Pathname tools
os.environ os.system, os.popen, os.popen2/3/4, os.startfile os.fork, os.pipe, os.exec, os.waitpid, os.kill os.open, os.read, os.write os.remove, os.rename, os.mkfifo, os.mkdir, os.rmdir os.getcwd, os.chdir, os.chmod, os.getpid, os.listdir os.sep, os.pathsep, os.curdir, os.path.split, os.path.join os.path.exists('path'), os.path.isdir('path'), os.path.getsize('path')
Esta tabla generaliza lo que detallaremos a continuación: Si en una shell interactiva importamos os y usamos el comando dir (os), para listar todos los atributos del modulo vemos que son muchos realmente.
Es muy difícil aprendérselos a todos, y veremos los mas importantes que se utilizan generalmente, por supuesto, habrá muchos mas, pero no es la cuestión estudiarlos todos uno a uno, sino los realmente mas importantes, la información sobre cualquier otro atributo esta al alcance del que quiera, tanto en la ayuda de python como usando help() en el modulo que deseemos saber para que sirve.
Además de todos esos atributos que vemos allí, por ejemplo os.path también tiene sus propios atributos que no son pocos realmente.
Bueno también como en el caso de sys, el modulo os, tiene herramientas informativas y administrativas, veamos algunos ejemplos de las mas usadas. GETPID >>>os.getpid() 424
El método getpid() nos da el PID o identificador del proceso que arranco el script o la shell en la cual estamos trabajando, en este caso 424 corresponde al proceso python.exe pues es el proceso en el que estoy trabajando.
Allí escribí ese script en Wing y lo corrí como Debug y igual el PID que me da corresponde al proceso python.exe en este caso 164 que es el proceso en el cual estamos. GETCWD
Si hago este script de prueba en el escritorio y lo corro veré que el método getcwd(), me devuelve el path donde corre el script, veamos.
Allí esta si lo copiamos a otra carpeta, al correrlo mostrara siempre el path donde esta ubicado. Esto tiene una excepción, si abro una consola de comandos en el escritorio y copio el Scriptprueba.py a C:\ por ejemplo, lo puedo correr desde el escritorio pasándole el path delante del nombre del script, veamos.
Vemos que el script esta ubicado en C:\ pero el método getcwd() me devolvió el path desde donde fue lanzado, o sea el escritorio, esa es la única excepción a tener en cuenta, que puede mostrarnos un path diferente al de donde esta realmente ubicado el script Veamos que ocurre en el caso si nuestro script importara un modulo que esta en la misma carpeta, del script que corremos, probemos hacer un script mínimo llamado importado.py y coloquemoslo en C:\ al lado del otro.
Y al Scriptprueba.py agreguemosle una linea para que lo importe.
Si ejecutamos el Scriptprueba.py con doble click.
Vemos que importa bien y además nos muestra que el directorio de trabajo es C:\ por lo cual no tiene problema en encontrar allí al modulo importado, ahora ejecutemoslo desde el escritorio desde una consola a este mismo Scriptprueba.py Vemos que si lo ejecutamos desde el escritorio lo halla también, pues esta en la misma carpeta, pero el path del directorio de trabajo sera diferente, debemos tenerlo en cuenta por si acaso tengamos que usar el path para algún otro uso. CONSTANTES DE PORTABILIDAD El modulo os, nos provee las constantes de portabilidad según el sistema operativo donde estemos, en el caso de Windows.
Vemos que os.pathsep, sera el símbolo que podre usar en Windows para separar los paths, así como os.sep, sera la separación en Windows para los diferentes directorios en un path. os.pardir es la forma de acceder al directorio parent o superior, os.curdir sera para el current directory o directorio actual, y os.linesep sera el separador de lineasLo bueno es que sino sabemos donde correrá nuestro script y queremos hacerlo portable, usando estas variables, siempre tendremos el símbolo correcto, para el sistema que sea el que corra. Si lo hago en linux >>> os.pathsep ':' >>> os.sep '/' >>> os.pardir '..' >>> os.curdir '.' >>> os.linesep '\n' >>> Vemos que me devuelve diferentes caracteres, pero si utilizo el contenido de estas variables en el script siempre tendrán sea cual sea el sistema operativo en el cual corra, el símbolo correcto para el mismo y me aseguro la portabilidad. HERRAMIENTAS BASICAS DE OS.PATH Dicho modulo nos provee de herramientas para manejar y chequear directorios y archivos veamos algunos en los ejemplos siguientes. Chequearemos si en la maquina donde estamos trabajando existe el siguiente directorio C:\Temp por ejemplo, lo podemos realizar con el método os.path.isdir() al cual hay que pasarle como argumento un path que querríamos chequear si existe en la maquina. Teniendo importado os, lógicamente usamos. >>>os.path.isdir(r'C:\TEMP') True y me devuelve que es cierto que existe ya que en mi maquina existe dicho directorio, si busco uno inexistente como por ejemplo TEMP2 no lo hallara. >>>os.path.isdir(r'C:\TEMP2') False Porque no existe en mi maquina, puse la r delante para asegurarme que el path este correcto en su forma cruda y que no haya caracteres especiales molestando.
De la misma forma podemos chequear con el método os.path.isfile() si existe un archivo con ese nombre veamos. >>>os.path.isfile('importado.py') False >>>os.path.isfile(r'C:\importado.py') True >>> Al no especificar la ubicación, lo buscara en el directorio de trabajo actual y si no esta dirá false, si le especificamos una ubicación correcta, la buscara allí y como existe en dicho path me dirá true. También con os.path.getsize() podemos saber el largo del archivo pasándole el nombre del mismo. >>>os.path.getsize(r'C:\importado.py') 18L >>> También os.path tiene herramientas para dividir paths, usando el método split() que pertenece a dicho modulo. >>>os.path.split(r'C:\TEMP') ('C:\\','TEMP') Vemos que lo divide y me devuelve una tupla con las dos partes del path, divididas por la barra \. También podemos unir con el método join() >>>os.path.join(r'C:\temp','output.txt') 'C:\\temp\\output.txt' También los métodos basename () y dirname() funcionan de acuerdo al sistema operativo en el cual corre el script >>> name = r'C:\temp\data.txt' # Windows paths >>> os.path.basename(name), os.path.dirname(name) ('data.txt', 'C:\\temp') >>> name = '/home/lutz/temp/data.txt' # Unix-style paths >>> os.path.basename(name), os.path.dirname(name) ('data.txt', '/home/lutz/temp') Vemos que basename() y dirname() se adaptan al estilo del sistema en el cual corre el script devolviendo el nombre de un archivo y el directorio donde esta el mismo desde el path completo que le pasamos como argumento a ambas.
El metodo splitext() separa la extensión del nombre del archivo o sea lo que esta después del punto. >>>os.path.splitext(r'C:\importado.py')
('C:\\importado', '.py') También el método normpath() resulta útil cuando un path esta muy mezclado de separadores de diferentes sistemas operativos, dicho método lo normalizara al sistema donde estamos corriendo el script. >>>mixed='C:\\temp\\public/files/index.html' >>>os.path.normpath(mixed) 'C:\\temp\\public\\files\\index.html' Allí vemos como normalizo el path al sistema Windows donde estoy corriendo el script. Cuando localizamos un archivo en alguna carpeta y queremos saber el path completo del mismo podemos usar abspath() aunque no estemos en el directorio de trabajo, buscara el path correcto siempre que partamos de un archivo que pueda hallar. >>>os.path.abspath(r'importado.py') 'C:\\Documents and Settings\\ricnar456\\importado.py' CORRIENDO COMANDOS SHELL DESDE UN SCRIPT Existen dos comandos de os que permiten correr desde python, los comandos que podríamos correr en una consola de comandos o shell. os.system Nos permite correr un comando de shell desde un script de Python. os.popen Corre un comando de shell y conecta a sus streams de entrada y salida. DEFINICION DE SHELL Y COMANDO DE SHELL El termino SHELL define al sistema que lee y ejecuta strings del estilo linea de comandos. SHELL COMMANDS o COMANDOS DE SHELL son dichas strings que uno normalmente tipea en el prompt de dicha SHELL y producen algún efecto valido. Por ejemplo en Windows uno puede arrancar una consola de comandos de MS-DOS, lo cual seria la SHELL DE SISTEMA de Windows y los comandos de shell serian por ejemplo DIR, CD y cualquier comando que en dicha ventana ejecute una acción valida. En Linux también existen la CONSOLA que sera la shell del sistema LINUX y allí uno tipea comandos de shell diferentes a los de Windows, pero que son validos en el contexto LINUX. Por ejemplo en la SHELL DE SISTEMA WINDOWS, o sea en la consola de MS-DOS, podemos tipear por ejemplo un COMANDO DE SHELL, por ejemplo dir /b, el cual listara los archivos y carpetas en el directorio actual.
Otro ejemplo de COMANDO DE SHELL es el comando type que muestra el contenido de un archivo de formato texto.
Allí como los scripts de python tienen formato de archivos de texto, podemos ver el contenido de cualquiera de ellos con el comando type que es un COMANDO DE SHELL. EJECUTANDO COMANDOS DE SHELL DESDE PYTHON Por supuesto lo anterior no tiene que ver con python en si, pero como desde python podemos ejecutar estos COMANDOS DE SHELL, vimos su definición, para poder aprender como manejar dichos comandos correctamente. Habíamos dicho que dichos COMANDOS DE SHELL pueden ejecutarse con os.system o os.popen veamos la diferencia entre ambos. Si abrimos una ventana de comandos y allí tipeamos python para entrar en el interprete de python, allí primero importamos os y luego usamos el comando os.system y entre paréntesis colocamos el mismo comando que tipeábamos suelto en la ventana de comandos de sistema ('dir /b'), lograremos el mismo resultado, eso quiere decir que el comando os.system ejecutara una string como argumento que si es un COMANDO DE SHELL como en este caso, provocara el mismo efecto que tipearlo en la SHELL de sistema directamente.
Lógicamente esto se puede hacer en una SHELL INTERACTIVA y dentro de un script también, es una herramienta del modulo os, y por supuesto funcionara siempre que hayamos importado os antes y que la string entre paréntesis sea un COMANDO DE SHELL.
El segundo ejemplo le pasa como argumento el otro COMANDO DE SHELL que habíamos probado o sea type, vemos que funciona de la misma forma que si lo tipeamos directamente en la linea de comandos, el cero que devuelve el sistema como valor de retorno, se imprime a continuación del texto. COMUNICANDONOS A TRAVES DE COMANDOS DE SHELL Vimos que os.system() permite ejecutar un COMANDO DE SHELL, pero que ocurre si queremos obtener el resultado de la entrada y salida estándar del sistema, por medio de comandos de shell. Aquí tenemos la forma clásica de leer un archivo, lo abrimos con open y luego le aplicamos el método read() que sirve para leer de los objetos del tipo archivo.
Si ejecutamos con os.system() lo mismo.
text1=os.system('type C:\Scriptprueba.py') Por mas que asignemos el resultado a una variable text1 como en este caso, la misma solo recibirá el valor de retorno de la función que era cero como habíamos visto, y el contenido del archivo se vera algún segundo en una consola que se cerrara y no podremos ver el resultado ni manejarlo, ya que os.system() lanza el comando pero no puede detenerse de ninguna forma hasta que se termino de ejecutar y recién recibe el valor de retorno de la función, en cambio si hacemos lo mismo con os.popen(), este devolverá a la variable que espera el resultado, el valor que maneja la salida o entrada standard según como configuremos el llamado veamos este ejemplo.
Vemos que os.popen es similar a os.system salvo que pude obtener en text lo que debería verse como salida en la consola. En este otro ejemplo, vemos que usando os.popen, devuelve a la variable listing la posibilidad de leer lo que saldría por pantalla, podremos obtener dicha salida en esa variable, mientras que con os.system se ejecuta y muestra, pero no podemos pararlo ni manejar el resultado ni almacenarlo en una variable.
También si queremos imprimirlo en forma legible usando read(), veamos que obtendremos la lectura de la salida completa en listing.
Lo mismo si ejecutamos un script con os.system() se ejecutara y no podremos obtener el resultado
que se vería por consola, mientras que con os.popen, podemos tener ese resultado en una variable ya que desvía la salida estándar allí. Vemos que no solo se ejecuto el Scriptprueba.py sino que obtuvimos lo que muestra por pantalla
al ejecutarse en la variable text. Lo que si hay que tener en cuenta que la forma de ejecutar scripts de python mediante os.system o os.popen crea un nuevo proceso en el sistema donde esta corriendo el script y eso hace un poco mas lento la ejecución, que si lo hiciéramos en forma normal importando, donde se manejará dentro del mismo proceso en el cual estamos trabajando. En las ultimas versiones de python se agrego un nuevo comando a os que se llama os.startfile() que si el archivo que lanzamos no es ejecutable, lo lanzara con el programa asociado al mismo. os.startfile('C:\modulo.doc') Este comando abrirá el archivo modulo.doc con el programa asociado en dicha maquina para abrir ese tipo de archivos por ejemplo el WORD o OpenOffice Writer. os.startfile("webpage.html") # abrira el archivo en el web browser os.startfile("document.doc") # abrira el archivo en Microsoft Word os.startfile("myscript.py") # correra el archivo en Python OTROS MODULOS EXPORTADOS POR OS Estos módulos se verán bien explicados mas adelante cuando se usen en el contexto de un programa completo. os.environ Lee y setea shell environment variables
os.fork
Corre un nuevo child process en Unix
os.pipe Comunicación entre programas
os.execlp Arranca nuevos programas
os.spawnv Arranca nuevos programas con control del mas bajo nivel
os.open Abre un archivo descriptor de bajo nivel
os.mkdir Crea un nuevo directorio
os.mkfifo Crea una nueva pipe con nombre
os.stat Lee información de un archivo de bajo nivel os.remove Borra un archivo por su pathname
os.path.walk, os.walk
Aplica una función o a todas las partes de un árbol de directorios
Estos solo los mencionamos, pero no profundizaremos en ellos por ahora ya llegara su momento. Hasta aquí hemos terminado con el estudio sobre el modulo OS y algunas de sus posibilidades. Hasta la parte 14 Ricardo Narvaja
PARTE 14:DIRECTORIO DE TRABAJO Y IMPORT SEARCH PATH-ARGUMENTOSEMPEZANDO REDIRECCION Habiamos visto cuando estudiamos el modulo sys que sys.path nos mostraba los paths desde donde podiamos importar modulos en el sistema en que esta ejecutandose el script., tambien llamado IMPORT SEARCH PATH.
>>> import sys >>> sys.path ['', 'C:\\Archivos de programa\\Wing IDE 3.0\\src\\debug\\tserver', 'C:\\WINDOWS\\system32\\python24.zip', 'C:\\', 'C:\\Python24\\DLLs', 'C:\\Python24\\lib', 'C:\\Python24\\lib\\plat-win', 'C:\\Python24\\lib\\lib-tk', 'C:\\Python24', 'C:\\Python24\\lib\\sitepackages', 'C:\\Python24\\lib\\site-packages\\win32', 'C:\\Python24\\lib\\site-packages\\win32\\lib', 'C:\\Python24\\lib\\site-packages\\Pythonwin', 'C:\\Python24\\lib\\site-packages\\wx-2.6-msw-ansi'] Tambien vimos que el modulo os, a traves de os.getcwd(), nos daba el CWD, o Current Working Directory, o directorio de trabajo, desde donde fue lanzado el mismo, como vimos en la parte anterior si es lanzado desde una consola, nos dara el path que tenia la consola cuando ejecuto el script, no la real ubicación del script. >>> os.getcwd() 'C:\\' Tambien vimos que os.chdir() nos permite cambiar el directorio de trabajo al que querramos, muchas veces esto es necesario. >>> import os >>> os.getcwd() 'C:\\' >>> os.chdir(r'C:\TEMP') >>> os.getcwd() 'C:\\TEMP' Alli vemos como el directorio de trabajo era C:\ y luego con os.chdir(), lo cambie a C:\TEMP, logicamente este tiene que ser un directorio valido y existente sino dara error. >>> os.chdir(r'C:\TEMP2') Traceback (most recent call last): File "", line 1, in OSError: [Errno 2] No such file or directory: 'C:\\TEMP2' Como en mi maquina C:\TEMP2 no existe da error al tratar de cambiarlo alli. El efecto que vimos en la parte 13 se puede ilustrar aun mejor con este scriptcito que guardo en C:\
Dicho script esta guardado en el directorio raiz en mi caso C:\ , y si lo ejecuto con doble click me mostrara el CWD o directorio de trabajo y el path de los modulos desde donde puede importar, pero para no hacerlo muy largo con un indice slice hacemos que solo muestre los primeros seis.
Alli esta el resultado cuando lo corremos en el directorio raiz con doble click, lo copiare aquí para que se vea completo. my os.getcwd => C:\ my sys.path => ['C:\\', 'C:\\WINDOWS\\system32\\python25.zip', 'C:\\Python25\\D
LLs', 'C:\\Python25\\lib', 'C:\\Python25\\lib\\plat-win', 'C:\\Python25\\lib\\li b-tk'] Vemos que el CWD y el IMPORT SEARCH PATH que se añade por la ejecucion del mismo, según desde donde fue lanzado en este caso coinciden, el resto los paths de importacion de modulos es constante y depende de la PC como este comfigurada, pero no cambiara de una ejecucion a otra, los que pueden variar son los que resaltamos y en este caso coinciden. Ahora comparemos la salida cuando lo corremos desde el escritorio desde una consola, cuyo path este alli en el mismo escritorio, pero apunte al script que esta en C:\
Vemos que cambie el path al escritorio y que luego corri el script como C:\prueba.py para que pueda hallarlo. El resultado es my os.getcwd => C:\Documents and Settings\ricnar456\Escritorio my sys.path => ['C:\\', 'C:\\WINDOWS\\system32\\python25.zip', 'C:\\Python25\\D LLs', 'C:\\Python25\\lib', 'C:\\Python25\\lib\\plat-win', 'C:\\Python25\\lib\\li b-tk'] Vemos que el directorio de trabajo CWD, sera el del path desde donde se lanzo el script y no donde esta ubicado realmente el mismo, pero el sys.path, si apunta a donde esta fisicamente ubicado el script, y eso es logico, porque el script siempre debe aceptar modulos importados en la misma carpeta desde donde esta fisicamente ubicado, si no se daria el caso que si lo lanzo desde el escritorio tendria que mover los modulos importados al escritorio y asi cambiar de ubicación los modulos que se encuentran en la misma carpeta del script, al path desde dode se lanzo haciendo su funcionamiento imposible. Por lo tanto es claro comoa veces coinciden el CWD o directorio de trabajo y el path donde importara los modulos, según el lugar desde donde fue lanzado, hay casos en los cuales coincidiran como en el primer ejemplo en el cual lance con doble click, y hay otros en que seran diferentes como en el segundo ejemplo. .
ARGUMENTOS DE LINEA DE COMANDOS El modulo sys nos provee de una lista de strings llamada sys.argv, donde cuando tipeemos argumentos en la linea de comandos, estos se almacenaran en dicha lista para poder usarlos en el script.
Editemos el script prueba.py para que nos imprima el contenido de sys.argv para poder probarlo en diferentes situaciones. Si lo ejecuto con doble click alli mismo en el directorio raiz donde esta ubicado.
Vemos que nos muestra solo un campo en la lista sys.argv con el path y nombre del archivo. Ahora abramos una consola de comandos alli en C:\, y tipeemos argumentos a continuacion del nombre del archivo. C:\>prueba.py ['C:\\prueba.py'] C:\>prueba.py aaa bbb ccc ['C:\\prueba.py', 'aaa', 'bbb', 'ccc'] Vemos que en el primer caso solo nos devuelve el path y nombre del archivo, pero en el segundo que le pasamos argumentos al script, estos son almacenados dentro de la lista sys.argv y pueden ser llamados individualmente para usarse en un script como sys.argv[1], sys.argv[2], etc, mediante los indices, logicamente obtendremos el campo que querramos de la lista de strings. C:\>prueba.py -i data.txt -o results.txt ['C:\\prueba.py', '-i', 'data.txt', '-o', 'results.txt'] Alli vemos un ejemplo mas realista, igualmente cada argumento que se le agregue sera puesto como string en un campo de la lista de sys.argv, y podra ser llamado por el indice correspondiente a su posicion entre los argumentos que le pasamos.
Los argumentos de linea de comandos juegan muchas veces un rol importante en programas, ya que para no estar en el medio de la ejecucion de un script pidiendo entradas de datos necesarios al usuario, muchas veces a ciertos scripts, se le pasan los argumentos por linea de comando inicialmente, lo cual redunda en mayor velocidad y no tener que perder tiempo ingresandolos en forma interactiva mas adelante. Veamos un ejemplo de script al cual se le pasan argumentos por linea de comandos.
Veamos como funciona, lo primero que define una funcion llamada getopts a la cual se le pasan como argumento sys.argv, o en este caso argv solo ya que al importar sys de la forma from sys.... no es necesario ponerle el sys delante de argv, o sea le pasara a dicha funciona la lista de argumentos que se le tipeo al script, luego veremos internamente que hace esta funcion con ellos. Sabemos que los def solo definen funciones, no se ingresa dentro de ellas hasta que no son llamadas asi que la ejecucion continua abajo if __name__ == '__main__': from sys import argv myargs = getopts(argv) if myargs.has_key('-i'): print myargs['-i'] print myargs
# example client code
Vemos que esta parte solo se ejecutara si el script es usado como main o principal, y no al ser importado por otro, ya que __name__ sera igual a __main__ solo en ese caso. Realiza la importacion de sys.argv from sys import argv
Luego llama a la funcion myargs = getopts(argv) y el resultado que devuelva la misma se aasigna a una variable llamada myargs, si miramos un poco dentro de la funcion vemos que construye un diccionario con los argumentos que le pasamos ya veremos como, pero solo de ver dentro de la funcion... def getopts(argv): opts = {} while argv: if argv[0][0] == '-': opts[argv[0]] = argv[1] argv = argv[2:] else: argv = argv[1:] return opts
# find "-name value" pairs # dict key is "-name" arg
ya sabemos que define un diccionario llamado opts, que al inicio esta vacio y que lo armara y lo devolvera mediante return, lo que se asignara a la variable myargs que recibe ese resultado. if myargs.has_key('-i'): print myargs['-i'] print myargs Luego vemos que chequea si ese diccionario tiene alguna key ['-i'] y la imprime, y luego imprime myargs completamente, lo que nos queda ver es como arma el diccionario dentro de la funcion. def getopts(argv): opts = {} while argv: if argv[0][0] == '-': opts[argv[0]] = argv[1] argv = argv[2:] else: argv = argv[1:] return opts
# find "-name value" pairs # dict key is "-name" arg
El tema es que los argumentos deben tener el formato -nombre valor, y el script chequea si existe el guion delante del nombre en esta instrucción. if argv[0][0] == '-': En este caso argv[0], sera el primer argumento que le pasamos y es una string, y el proximo indice tomara el primer carácter de esa string y vera si es un guion, si ve que es un guion, entra y usa argv[0] como key del primer campo del diccionario y el siguiente argumento argv[1] como valor asociado. opts[argv[0]] = argv[1] como opts es un diccionario sera similar a
opts[key]= value Asi que de esa forma siempre agregara como key los argumentos que su primer carácter sea un guion y como valor asociado el argumento siguiente a ese. argv = argv[2:] Luego mediante indice slice quita esos dos argumentos de la lista para repetir el proceso sin ellos, mientras haya argumentos continuara loopeando. Ahora si el primer carácter no es un guion ira al else else: argv = argv[1:] Donde vemos que no toca al diccionario y solo quita el argumento que acaba de chequear de la lista de argumentos para que se chequee el siguiente al continuar el loop. De esta forma si al script le pasamos argumentos en la forma -xxxxx yyyyy C:\>argv2.py -pepe capo -red hawk -ricardo narvaja {'-pepe': 'capo', '-red': 'hawk', '-ricardo': 'narvaja'} Vemos que crea e imprime un diccionario cuyas keys son los argumentos que tienen guion y los valores asociados es el argumento siguiente a esos, si le agregamos un valor mezclado que no tiene guion deberia saltearlo y continuar con el siguiente veamos. C:\>argv2.py -pepe capo jejejeje -red hawk -ricardo narvaja {'-pepe': 'capo', '-red': 'hawk', '-ricardo': 'narvaja'} Vemos que el argumento jejejeje que le puse en medio al no tener guion delante y el argumento anterior tampoco, lo saltea como corresponde ya que el tema es juntar los pares de valores -name value y hacer un diccionario con ellos y ignorar los que no sigan esta forma. Lo unico que nos queda es que hay un argumento especial que es la '-i' vimos que al final lo imprimia en forma separada, veamos que ocurre si le pasamos un '-i' C:\>argv2.py -pepe capo jejejeje -red hawk -ricardo narvaja -i ESPECIAL ESPECIAL {'-pepe': 'capo', '-i': 'ESPECIAL', '-red': 'hawk', '-ricardo': 'narvaja'} Vemos que si el diccionario tiene una key con -i este es tratado en forma especial y impreso en forma separada en este script.
SCRIPTS EJECUTABLES EN LINUX Para los usuarios de linux y unix: se pueden hacer archivos de texto de Python directamente ejecutables agregando una linea especial en el inicio del script, dicha linea sera un path al interprete de Python y luego hay que darle al archivo permiso de ejecucion.
Por ejemplo creemos un archivo de texto llamado myscript: #!/usr/bin/python print 'And nice red uniforms' La primera linea es tomada como un comentario y en windows no tiene ninguna funcion y es obviada (no olvidemos que cominenza con #); Pero en linux o unix cuando se ejecuta el archivo y si este tiene permiso de ejecucion dado con chmod +x myscript, este puede ser ejecutado directamente sin tipear python delante del nombre del archivo como si fuera un ejecutable mas. % myscript a b c And nice red uniforms SHELL ENVIRONMENT VARIABLES Estudiaremos el tema de las environment variables o variables de entorno, sabemos que una de las variables de entorno es PYTHONPATH que puede estar seteada o no en el sistema, pero de existir, se agregara ese path, a la lista de paths desde donde se pueden importar modulos. Por supuesto el sistema tendra mas variables de entorno que seran utilizadas y seteadas por los diferentes programas que se instalaron en la maquina en que corre el script. LEYENDO VARIABLES DE ENTORNO En python gracias al modulo os podemos leer las variables de entorno del sistema donde corre el script, usando os.environ, que es un diccionario que contiene todas las variables de entorno del sistema.
Alli al llamar a os.environ, se muestra el diccionario que contiene todas las variables de entorno, logicamente al ser un diccionario se puede llamar mediante las keys a alguna que querramos. Si queremos ver todas las keys que hay en el sistema. >>> os.environ.keys( ) ['TMP', 'COMPUTERNAME', 'USERDOMAIN', 'COMMONPROGRAMFILES', 'PROCESSOR_IDENTIFIER', 'PROGRAMFILES', 'PROCESSOR_REVISION', 'PATH', 'SYSTEMROOT', 'WINGDB_ACTIVE', 'TEMP', 'WINGDB_SPAWNCOOKIE', 'PROCESSOR_ARCHITECTURE', 'ALLUSERSPROFILE', 'SESSIONNAME', 'HOMEPATH', 'USERNAME', 'LOGONSERVER', 'COMSPEC', 'PATHEXT', 'WINGDB_PYTHON', 'CLIENTNAME', 'FP_NO_HOST_CHECK', 'WINDIR', 'APPDATA', 'HOMEDRIVE', 'SYSTEMDRIVE', 'NUMBER_OF_PROCESSORS', 'PROCESSOR_LEVEL', 'OS', 'USERPROFILE']
O alguna en especial como ene ste caso mediante la key 'TEMP' >>> os.environ['TEMP'] 'C:\\DOCUME~1\\RICNAR~1\\CONFIG~1\\Temp' La variable de entorno cuya key es 'TEMP', tendra como valor asociado el path del directorio temporal del sistema. >>> os.environ['SYSTEMROOT'] 'C:\\WINDOWS' Logicamente la key SYSTEMROOT nos dara el path donde esta instalado el sistema. >>> os.environ['PYTHONPATH'] Traceback (most recent call last): File "C:\TEMP\", line 1, in ? File "C:\Python24\Lib\os.py", line 422, in __getitem__ return self.data[key.upper()] KeyError: 'PYTHONPATH' Vemos que como la variable de entorno PYTHONPATH no esta seteada en mi sistema, da error al tratar de leer su valor relacionado. Como es un diccionario podria temporalmente agregar o cambiar una key y su valor asociado en este caso >>> os.environ['TEMP'] = r'c:\temp' >>> os.environ['TEMP'] 'c:\\temp' Si hago restart de la shell interactiva se perderan estos cambios que son temporales mientras corre un script. Python 2.4.3 (#69, Apr 11 2006, 15:32:42) [MSC v.1310 32 bit (Intel)] Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> os.environ['TEMP'] 'C:\\DOCUME~1\\RICNAR~1\\CONFIG~1\\Temp'
STANDARD STREAMS El modulo sys es tambien el lugar donde se manejan el standard input, output y error de los programas de python. Realmente los comandos print, raw_input, etc, son solo formas mas simples y mas comodas de llamar a los streams que se conectan a la entrada y salida de los scripts de python apenas arrancan los mismos. print 'hello stdout world' hello stdout world >>> sys.stdout.write('hello stdout world' + '\n') hello stdout world Vemos que el comando print simplifica las cosas para mayor comodidad pero stdout esta conectado a la salida estandard y mediante el metodo write puede imprimir de la misma forma. >>> raw_input('hello stdin world>') hello stdin world>pepe 'pepe' >>> sys.stdin.readline( )[:-1] pepe 'pepe' De la misma forma stdin puede leer la entrada de teclado mediante readline y obtener el mismo resultado, aunque en forma mas complicada. REDIRECCIONANDO STREAMS A ARCHIVOS A veces realizar redirecciones muy util, si tenemos un script que por ejemplo imprime en pantalla una cantidad muy grande de informacion, redireccionar la salida a un archivo de texto, es interesante para poder analizarla detelladamente buscar palabras por ejemplo, y poder trabajar mas detenidamente en la salida del mismo. Haremos un script de ejemplo que llamaremos teststreams.py, lo creare en WING y lo guardare en C:\
Alli lo tenemos, es un simple script que usara print, tambien usa raw_input y tambien si lo que lee me provoca error porque no es una string que se pueda pasar a numero mediante la funcion int(), implicitamentemanejara el error. Como siempre este script testea si corre como principal y si es asi llama a la funcion interact(), que imprime 'Hello stream world', y luego entra en un ciclo, donde dentro de un try se pide que se ingrese un numero mediante raw_input(), y si no puede transformarlo a numero mediante int(), habra error y saltara el except y si no hay error va al else donde se imprime el valor del numero y el cuadrado del mismo, y luego se continua el loop, la unica forma de salir del loop es por medio del break, que se ejecuta solo cuando hay un error en int(). Probemoslo.
Vemos que mientras ingrese strings que eran posible transformar a numeros mediante int(),lo hace y calcula el cuadrado y imprime el numero y el cuadrado, cuando aprete enter salio imprimendo bye, lo mismo ocurrira cuando ingreso una letra por ejemplo ya que int() no podra transformarla a numero y habra error que saltara el break y saldra.
Ahora lo que primero haremos sera redireccionar lo que tipeabamos manualmente mediante raw_input, ahora lo hara leyendo los valores desde un archivo de texto llamado input.txt y que lo crearemos en C:\ o en donde ustedes corran su script. Creamos un archivo de texto vacio llamado input.txt en la misma carpeta donde esta el script. Al archivo le ponemos lo que tipeariamos como entrada 8, enter,6, enter y asi varios numeros mas
hasta que ponemos ENTER, ENTER para salir. Vemos que al final tipeamos dos enter seguidos para que ingrese como entrada ENTER y active el
break, tambien podia haber puesto una letra alli el efecto es el mismo. Ahora para redireccionar la entrada tipeamos en la consola de comandos
Necesitamos poner el python delante para que redireccione y a continuacion del nombre del archivo el simbolo de redireccion < significa que ingresara lo que se deberia tipear en el raw_input(), por medio del archivo cuyo nombre esta a continuacion, en este caso input.txt.
python teststreams.py < input.txt El simbolo < significa redireccion del input y el simbolo inverso > significa redireccion del output, o sea que seria el caso en el cual, lo que se deberia imprimir en pantalla se desviara a un archivo cuyo nombre esta a continuacion.
Si busco veo que en la misma carpeta se creo el archivo output.txt y que contiene lo que el programa deberia haber impreso en pantalla.
De esta forma ya se redireccionar la entrada y la salida de un script, para que lea desde archivo lo
que se deberia tipear con el simbolo (), ahora podemos usar ambos juntos en la misma linea?
Alli vemos que redireccionamos ambos tanto la entrada como la salida en la misma linea y al ejecutarlo parece no pasar nada, lo que pasa es que ingresa los datos directamente desde el archivo input.txt y la salida la guarda en output.txt. Si borro el archivo output.txt y vuelvo a ejecutar la linea aparecera de nuevo con la misma salida C:\>python teststreams.py < input.txt > output.txt
Bueno en la parte siguiente continuaremos el tema de la redireccion que es bastante largo y falta el tema de encadenar programas con pipas, que esta muy interesante, eso sera en la parte siguiente. Hasta la parte 15 FELIZ AÑO 2008 Ricardo Narvaja
PARTE 15-ENCADENANDO PROGRAMAS CON PIPAS En Windows y Unix, o Linux se puede encadenar programas mediante pipas, que significa esto, que se puede redireccionar la salida de un programa, para que sea la entrada de otro programa, y de esta forma trabajaran encandenados, uno suministrandole las entradas que necesita el otro para funcionar.
Volvamos a usar el script teststreams.py, que habiamos hecho y el archivo input.txt que estaba en la misma carpeta para redireccionar la entrada a que lea de dicho archivo.
python teststreams.py 8 squared is 64 Enter a number>6 squared is 36 Enter a number>5 squared is 25 Enter a number>4 squared is 16 Enter a number>3 squared is 9 Enter a number>4 squared is 16 Enter a number>Bye Por lo tanto el concepto de chaining o encadenamiento es simple, se dirige la salida de un programa a la entrada de otro, y este segundo esperara hasta que el primero le envie los datos para funcionar. Para practicar como encadenar dos scripts crearemos uno llamado writer.py y otro llamado reader.py en el directorio raiz.
Vemos que writer.py envia a imprimir el mensaje que vemos en la imagen, si encadenamos a writer.py con reader.py eso sera lo que le ingresara al segundo como entrada, veamos reader.py.
Vemos que tiene una primera entrada por teclado mediante raw_input(), alli entrara la string y hara format string y se imprimira 'GOT THIS ... mas lo que ingrese por medio de la entrada, en este caso como estaran encadenados, lo que deberia ingresar por raw_input(), sera la salida del otro programa o sea la primera string que envia writer.py o sea "Help! Help! I'm being repressed!".
Luego importa sys y tiene una segunda entrada esta vez no utiliza raw_input() sino sys.stdin.readline que como vimos cumple la misma funcion y ingresara la segunda string que envia writer.py o sea data sera la string 42, luego lo pasara a integer y lo multiplicara por 2 y imprimira tanto el 42 como el 84, en este caso. Probemos a ver si es cierto
Vemos que debemos tipear para que funcione, python delante del nombre de cada script, si no no funcionara. Vemos que la salida fue la esperada agrego como entrada en reader.py lo que entrega como salida writer.py. Veamos otro ejemplo un poco mas complejo de encadenamiento.
Aquí vemos el script sorter.py que cree alli en el directorio raiz, vemos que si vamos ingresando texto lo ordenara, y lo va imprimira, el otro script adder.py
Vemos que intenta leer la entrada y si es una string que representa a un numero entero, o sea que se le puede aplicar int(), lo va sumando e imprime la salida que es la sumatoria.
Usaremos un archivo data.txt para ingresar los datos lo creamos alli en la raiz.
Y escribimos dentro
Si le enviamos esos datos a sorter.py este los ordenara e imprimira veamos
Bueno me quedaron dos en la misma linea jeje, alguna pavadita habre echo, pero bueno la idea es que sorter.py toma las lineas que le enviamos y las ordena mediante la funcion sort() y luego las imprime.
Ah lo que fallaba era que faltaba el enter al final del ultimo numero, eso por copiar y pegar jeje, ahora si.
Los ordena y encolumna perfectamente.
Vemos que si le aplicamos la misma entrada a adder.py este sumara todas las strings que pueda pasar a enteros con int()
En esta forma realizamos lo mismo pero con encadenamiento, type data.txt lo encadenamos a adder.py de esta forma lo que deberia imprimir por pantalla el archivo de texto que son los numeros que contiene, se envian como entrada a adder.py que los sumara.
Si hacemos un tercer script llamado writer2.py
Vemos que imprimira en su salida
Ya que 03 significa que completara con ceros delante para imprimir tres cifras de cada numero. Ahora esto se puede enviar como entrada a ambos scripts adder y sorter
Alli lo enviamos a sorter que lo ordenara
Y si lo enviamos a adder lo sumara.
Tambien podemos encadenar los tres, ya que sorter lo ordenara y luego adder lo sumara. En la parte 16 seguiremos con ejemplos mas avanzados de encadenamiento Hasta la parte 16 Ricardo Narvaja
PARTE 16: Habiamos visto en la parte 15 los scripts adder.py, sorter.py y writer2.py, ahora veremos versiones de los mismos usando mejoras que se agregaron en python a partir de la versión 2.4 en adelante. Lo llamaremos sorter24.py a la versión para python 2.4 o superior.
Vemos que en una sola linea realizara el mismo trabajo gracias a la función sorted() que fue agregada en python 2.4 y a la posibilidad de directamente leer las lineas de sys.stdin en un for como vemos alli, y la imprimimos, realiza el mismo trabajo que la versión anterior, ordenando e imprimiendo lo que le ingresa por la entrada, por redirección o encadenamiento con otro programa que se la suministra. La versión para 2.4 de adder.py es también mas sencilla.
Vemos que realiza el mismo trabajo por cada linea en la entrada, le halla el int(), lo suma y lo imprime.
Writer2 .py no cambia suministrara en su salida la entrada para los otros dos scripts, probemos.
Vemos que funcionan igual escritos en forma abreviada. STREAMS REDIRIGIDAS Y INTERACCION CON EL USUARIO Recordamos que en las partes anteriores redirigíamos la entrada de nuestro script teststreams y la salida la encadenábamos al programita more que viene incluido en windows.
Recordemos que more solo toma lo que vas tipeando y lo imprime, alli tipee la f y al apretar enter imprimió la f, lo mismo con el 5,4,3,2 y lo que fui tipeando cada vez que apreto enter lo imprime.
Podríamos hacer un script de python que se comportara en la misma forma que el more que trae windows pero mas interactivo lo llamaremos moreplus.py. La diferencia sera que el moreplus.py a pesar de estar redireccionada su entrada o encadenada, igual deberia recibir ordenes por el teclado, lo cual no ocurre con el more de windows, una vez redireccionada su entrada, ya no acepta mas ingresos por teclado. Aquí pongo el código igual lo adjuntare al tutorial, parece complejo pero lo analizaremos paso a paso. ---------------------------------------------------------------------------------------------------------------------import sys def getreply( ): """ read a reply key from an interactive user even if stdin redirected to a file or pipe """ if sys.stdin.isatty( ): # if stdin is console return raw_input('?') # read reply line from stdin else: if sys.platform[:3] == 'win': # if stdin was redirected import msvcrt # can't use to ask a user
msvcrt.putch('?') key = msvcrt.getche( ) # use windows console tools msvcrt.putch('\n') # getch( ) does not echo key return key elif sys.platform[:5] == 'linux': # use linux console device print '?', # strip eoln at line end console = open('/dev/tty') line = console.readline( )[:-1] return line else: print '[pause]' # else just pause--improve me import time # see also modules curses, tty time.sleep(5) # or copy to temp file, rerun return 'y' # or GUI pop up, tk key bind def more(text, numlines=10): """ split multiline string to stdout """ lines = text.split('\n') while lines: chunk = lines[:numlines] lines = lines[numlines:] for line in chunk: print line if lines and getreply( ) not in ['y', 'Y']: break if __name__ == '__main__': # when run, not when imported if len(sys.argv) == 1: # if no command-line arguments more(sys.stdin.read( )) # page stdin, no raw_inputs else: more(open(sys.argv[1]).read( )) # else page filename argument Su funcionamiento parece similar al more de windows
Pero si armamos un archivo de texto, con una gran cantidad de texto como en mi caso el prueba.txt, y se lo paso como argumento al nombre de dicho archivo o redirigiendo ese archivo a la entrada de moreplus.py....
Vemos que lee el texto y que luego de una cierta cantidad de lineas impresas, queda esperando a ver si queremos leer mas para lo cual debemos apretar 'Y' o 'y' cualquier otra tecla terminara el script. Vemos que hay definidas dos funciones getreply() y more(), luego de que las define va a ejecutar el código aquí if __name__ == '__main__': # when run, not when imported if len(sys.argv) == 1: # if no command-line arguments more(sys.stdin.read( )) # page stdin, no raw_inputs else: more(open(sys.argv[1]).read( )) # else page filename argument Vemos que el funcionamiento básico es antes que nada verificar si esta corriendo como modulo principal, y si es así ejecutará lo que esta dentro del if, luego testea si solo tiene un argumento o sea el nombre del script moreplus.py en este caso, que le pasamos y si es asi llama a la función more() leyendo de la entrada sys.stdin.read() y en el caso que tenga mas de un argumento, este segundo deberá ser el path a un archivo que se abrirá y llamara a la función more() luego de leer su contenido. Veamos un poco mas en detalle como funciona, para ello debuggearemos el script ya que le podemos pasar como argumento el archivo de texto prueba.txt, para ver como se comporta.
Allí le colocamos un BREAKPOINT al inicio y le damos a DEBUG, pasándole como argumento el archivo de texto prueba.txt que hicimos que contiene mucha cantidad de texto.(esta adjunto al tutorial)
Allí paro y testeara si sys.argv tiene un solo campo, lo cual en este caso no es cierto, pues además del nombre del script le pasamos como argumento el nombre del archivo de texto prueba.txt. Allí en el DEBUG PROBE podemos chequear el contenido de alguna variable en este caso de sys.argv.
Por lo tanto llamara a la función more() aquí
Vemos que aquí open() abre el archivo y read() leerá el contenido completo del mismo, que se le pasara como argumento a la función more(), si apreto f7 debería primero entrar dentro de las funciones de lectura del archivo y yo no quiero eso, así que mejor pongo un breakpoint dentro de more(), y le doy a DEBUG.
Allí paro, por supuesto la variable text tiene el texto completo del archivo que se le asigno por
medio del llamado a la función, pues le pasamos como argumento a more, ese texto. >>> lines ['13-2DA ETAPA DEL CURSO DE PYTHON-PROFUNDICEMOS EN EL MODULO OS', '', 'Asi como hemos visto para que sirve el modulo sys y como su funcion es relacionarnos con el interprete de python de acuerdo a la maquina en la cual estamos trabajando, vimos como nos proveia de informacion sobre el sistema en el cual corre el interprete de python que estamos usando, asimismo como la versionde python, como a traves de path, sabiamos los paths donde aceptara modulos importados seg\xfan como esta configurado python en la maquina en la cual corre, como podemos agregar mas paths temporalmente donde aceptara mas modulos,como podiamos averiguar la tabla d emodulos que estan importados, informacion de las excepciones que produjo python, y el manejo de las entradas y salidas estandar del sistema a nuestros scripts.', 'Realmente vemos que la funcionalidad de sys esta orientada a la relacion entre el interprete de python y el sistema donde esta alojado, todas sus funcionalidades son relativas a ello, ahora veremos otro modulo muy importante que se llama os, y que a diferencia del anterior es la relacion con las llamadas de sistema que podemos usar en un programa, veremos los ejemplos a continuacion asi se aclarara la diferencia facilmente.', '', ' ', 'Introduciendo el Modulo os ', 'Como dijimos os es el mas largo de los dos modulos de sistema, y contiene las mas usuales las mas usuales llamadas al sistema operativo que usamos en los programas y scripts.Trabaja sobre directorios, procesos,variables del shell, y sirve como una interfase a las mas importantes llamadas de sistema, los scripts que importan os y os.path normalmente no tienen que ser modificados para correr en cualquier plataforma. ', '', 'LAS HERRAMIENTAS MAS COMUNES DE OS', '', '', '', 'Tasks', 'Tools', 'Variables de Shell ', 'os.environ', 'Corriendo programas', 'os.system, os.popen, os.popen2/3/4, os.startfile', 'Manejo de Procesos', 'os.fork, os.pipe, os.exec, os.waitpid, os.kill', 'Descriptor de archivos, locks', 'os.open , os.read, os.write', 'Proceso de archivos', 'os.remove, os.rename, os.mkfifo, os.mkdir, os.rmdir', 'Herramientas admninistrativas', 'os.getcwd, os.chdir, os.chmod, os.getpid, os.listdirP', 'Portabilidad', 'os.sep, os.pathsep, os.curdir, os.path.split, os.path.join', 'Herramientas de path', "os.path.exists('path'), os.path.isdir('path'), os.path.getsize('path')", '', 'Esta tabla generaliza lo que detallaremos a continuacion:', '', 'Si en una shell interactiva importamos os y usamos el comando dir (os), para listar todos los atributos del modulo vemos que son muchos realmente.', ''] >>> Vemos que realiza split usando \n como divisor de forma de obtener una lista cuyos campos seran las lineas del texto del archivo prueba.txt y se asignara a la variable lines.
Vemos que en la variable chunk usando índice slice, tendremos las 10 primeras lineas ya que se usa el parámetro por default 10, al no haber especificado otra cantidad de lineas a mostrar. >>> chunk ['13-2DA ETAPA DEL CURSO DE PYTHON-PROFUNDICEMOS EN EL MODULO OS', '', 'Asi como hemos visto para que sirve el modulo sys y como su funcion es relacionarnos con el interprete de python de acuerdo a la maquina en la cual estamos trabajando, vimos como nos proveia de informacion sobre el sistema en el cual corre el interprete de python que estamos usando, asimismo como la versionde python, como a traves de path, sabiamos los paths donde aceptara modulos importados seg\xfan como esta configurado python en la maquina en la cual corre, como podemos agregar mas paths temporalmente donde aceptara mas modulos,como podiamos averiguar la tabla d emodulos que estan importados, informacion de las excepciones que produjo python, y el manejo de las entradas y salidas estandar del sistema a nuestros scripts.', 'Realmente vemos que la funcionalidad de sys esta orientada a la relacion entre el interprete de python y el sistema donde esta alojado, todas sus funcionalidades son relativas a ello, ahora veremos otro modulo muy importante que se llama os, y que a diferencia del anterior es la relacion con las llamadas de sistema que podemos usar en un programa, veremos los ejemplos a continuacion asi se aclarara la diferencia facilmente.', '', ' ', 'Introduciendo el Modulo os ', 'Como dijimos os es el mas largo de los dos modulos de sistema, y contiene las mas usuales las mas usuales llamadas al sistema operativo que usamos en los programas y scripts.Trabaja sobre directorios, procesos,variables del shell, y sirve como una interfase a las mas importantes llamadas de sistema, los scripts que importan os y os.path normalmente no tienen que ser modificados para correr en cualquier plataforma. ', '', 'LAS HERRAMIENTAS MAS COMUNES DE OS'] >>>
Sera la ultima linea que tenga ya que spliteará de 0 a 9. Ahora también por medio de slice quitara las mismas lineas de la variable lines, para que en el siguiente ciclo del for, ya no estén y se puedan imprimir las 10 siguientes. lines = lines[numlines:] Quedara lines con las lineas que originalmente estaban en la posición 11 en adelante. >>> lines ['', '', '', 'Tasks', 'Tools', 'Variables de Shell ', 'os.environ', 'Corriendo programas', 'os.system, os.popen, os.popen2/3/4, os.startfile', 'Manejo de Procesos', 'os.fork, os.pipe, os.exec, os.waitpid, os.kill', 'Descriptor de archivos, locks', 'os.open, os.read, os.write', 'Proceso de archivos', 'os.remove, os.rename, os.mkfifo, os.mkdir, os.rmdir', 'Herramientas admninistrativas', 'os.getcwd, os.chdir, os.chmod, os.getpid, os.listdirP', 'Portabilidad', 'os.sep, os.pathsep, os.curdir, os.path.split, os.path.join', 'Herramientas de path', "os.path.exists('path'), os.path.isdir('path'), os.path.getsize('path')", '', 'Esta tabla generaliza lo que detallaremos a continuacion:', '', 'Si en una shell interactiva importamos os y usamos el comando dir (os), para listar todos los atributos del modulo vemos que son muchos realmente.', ''] >>>
Aquí vemos el nudo del problema, el for imprimirá las primeras 10 lineas, pero luego si tuviéramos la entrada redireccionada y quisiéramos ingresar mediante raw_input o sys.stdin, la entrada de lo que tipea el usuario para ver si continua mostrando o no las siguientes 10 lineas (y o Y), nos encontraríamos con el problema de que la entrada esta redireccionada o sea que solo acepta entrada del archivo de texto prueba.txt o el archivo que le esta suministrando datos. Vemos que el script moreplus.py funciona perfecto aun teniendo la entrada redireccionada, igual acepta que tipeemos la Y para continuar o cualquier otra tecla para salir.
Esta posibilidad la da la función getreply(), que se usa para eso,para poder ingresar por el teclado, a pesar de tener la entrada redireccionada o encadenada.
Allí imprimió las primeras 10 lineas y veremos como usa getreply() para poder tener un ingreso por teclado en cualquier caso.
Bueno habrá que estudiar lo que devuelve la función getreply(), pero ya podemos ver que mientras lines sea verdadero o sea mientras haya lineas y el otro miembro igual mientras sea verdadero o sea que la respuesta de la función getreply() no sea y o Y, solo en ese caso ira el break para salir mientras que si es y o Y continuara repitiendo el while mientras haya lineas, ahora pongamos un brakpoint dentro de getreply() para ver como funciona y apretemos DEBUG.
Allí vemos que mediante la funcion sys.stdin.isatty() testea de donde esta recibiendo datos stdin, si recibe de la consola dará verdadero y me dejara entrar datos por raw_input en la forma normal y devolverá ese valor mediante el return, ahora el tema es que si esta stdin redireccionado, hay diferentes comportamientos según la plataforma, por lo que hay que chequear en que plataforma esta corriendo el script.
Vemos que testea si las primeras tres letras de sys.platform son win, mediante un índice slice que toma solo las tres primeras letras y si es así, entra, si no va a chequear si estamos en linux y trabajara de otra forma, especial para linux.
Allí vemos que importa el modulo msvcrt que tiene unas funciones de bajo nivel para consola como putch() que imprime y getche() que recibe una sola tecla que tipeamos por teclado, evidentemente sin usar stdin y stdout de cualquier forma no se puede continuar debuggeando aquí en WING, porque estas funciones no toman las entradas que hacemos en el DEBUG I/O de WING solo funcionaran en una en consola pues son funciones especiales para ella.
Pero vemos aun sin debuggear que si tipeamos y o Y la función getche() me devolverá en la variable key ese valor y de esa forma no ira al break y continuara mostrando mas lineas y este moreplus.py pudo tener entradas por teclado a pesar de tener el stdin que no esta aceptándolas de la consola en este momento. En linux solo abriendo /dev/tty como un simple archivo y leyendo con readline() como vemos, leerá lo que tipeamos en la consola. console = open('/dev/tty') line = console.readline( )[:-1] También la función more() que trae este script moreplus.py puede ser importada en forma separada, en una consola funcionara, tipeamos python primero, y luego importamos more from moreplus import more >>> more (open('prueba.txt').read())
Esto funciona en una consola de DOS y no en una consola interactiva de WING porque las funciones de msvcrt son especiales para consolas de DOS, pero el script funciona perfecto aquí.
También funciona si le pasamos el nombre del archivo de texto como argumento
En el caso de encadenamiento
Vemos que primero imprime lo que le envía el archivo writer2 a la entrada, como es menos de 10 lineas no dará la opción de seguir tipeando al apretar Y.
Aquí desviamos el texto del primer moreplus.py al otro al cual le ingresara como entrada y lo mostrara de a 10 lineas y siempre pidiendo si queremos mas para lo cual si tipeamos Y continuara mostrándolo.
En este caso type mostrara en pantalla el contenido del archivo de texto moreplus, esa salida se encadena a la entrada del script moreplus.py por lo cual el texto del script se mostrara de a 10 lineas y apretando la Y si queremos mas. Igualmente la redirección por medio de símbolos > < y el encadenamiento | son posibilidades de la ventana de comandos y no python en si, en la parte 17 veremos la forma pythoniana de redireccionar y encadenar en python sin usar estos símbolos. Hasta la parte 17 Ricardo Narvaja
PARTE 17-REDIRECCIONANDO STREAMS A OBJETOS DE PYTHON-EL MODULO StringIO-OS.POPEN En las partes anteriores vimos como redireccionar y encadenar usando los símbolos > < y | que se aplican en la linea de comandos, mas que de python en si, aunque se pueden utilizar perfectamente. Veremos ahora una forma mas pythoniana de hacer esto que es redireccionar sys.stdin y sys.stdout a objetos de python. Haremos un script en WING que demostrara este concepto se llamara redirect.py lo analizaremos paso a paso como siempre. ############################################################################# # file-like objects that save standard output text in a string and provide # standard input text from a string; redirect runs a passed-in function # with its output and input streams reset to these file-like class objects; ############################################################################# import sys
# get built-in modules
class Output: def _ _init_ _(self): self.text = '' def write(self, string): self.text = self.text + string def writelines(self, lines): for line in lines: self.write(line)
# simulated output file # empty string when created # add a string of bytes # add each line in a list
class Input: # simulated input file def _ _init_ _(self, input=''): # default argument self.text = input # save string when created def read(self, *size): # optional argument if not size: # read N bytes, or all res, self.text = self.text, '' else: res, self.text = self.text[:size[0]], self.text[size[0]:] return res def readline(self): eoln = self.text.find('\n') # find offset of next eoln if eoln == -1: # slice off through eoln res, self.text = self.text, '' else: res, self.text = self.text[:eoln+1], self.text[eoln+1:] return res def redirect(function, args, input): # redirect stdin/out savestreams = sys.stdin, sys.stdout # run a function object sys.stdin = Input(input) # return stdout text sys.stdout = Output( ) try: function(*args) # was apply(function, args) except: sys.stderr.write('error in function! ') sys.stderr.write("%s, %s\n" % tuple(sys.exc_info( )[:2])) result = sys.stdout.text sys.stdin, sys.stdout = savestreams return result
La función allí definida redirect(), nos permitirá redireccionar la entrada de un programa que acepta entradas por teclado, y enviarle una string como entrada, podemos antes de explicar como funciona hacer una simples pruebas para ver cual es la idea de su funcionamiento.
Allí usamos el script teststream.py que habíamos hecho en partes anteriores, al cual al hacerlo correr sin redirigir nos muestra una salida HELLO STREAM WORLD y luego nos pregunta números, cada uno que tipeamos le halla el cuadrado y lo imprime, ahora ya vimos que para ingresarle datos de un archivo usando el símbolo < podríamos redirigir su entrada a que lea de allí, pero lo que hará redirect.py sera que redirigirá su entrada para que le pasemos una string directamente sin leer de un archivo veamos el ejemplo a continuación. Recordamos que dentro de teststreams.py la función que realizaba todo el trabajo se llamaba interact() y no necesitaba pasarse le parámetros.
O sea que podrá importarse dicha función y llamarla directamente primero tipeo python en la consola y luego cuando se abre python tipeo. from teststreams import interact interact() Que importara la función interact() y luego la llamamos
Funcionará en forma similar ahora veamos si la llamamos por medio de redirect().
Allí importamos redirect() y luego tipeamos. output = redirect(interact, ( ), '4\n5\n6\n') Lo que hará aquí es que se le pasara la función interact() como primer argumento de redirect(), el segundo serán los argumentos que necesita la función, como no necesita en este caso se pasa vacío, y el tercero es la string de entrada, vemos que redireccionamos la entrada ya que le ingresamos 4, 5 y 6 con saltos de linea entre ellos para separarlos, y redirect() la devuelve mediante su return al archivo output que la espera, allí vemos que si imprimimos output tendremos lo que debería haber impreso en pantalla el programa. De esta forma es mucho mas cómodo trabajar pues podemos pasarle a un programa que esta esperando entrada por teclado directamente una string que armemos como en este caso, y que lo que debería imprimir lo devuelva en una variable, si queremos podemos imprimirlo luego o no,o seguir trabajando , ya que stdin y stdout son restaurados al salir de redirect() y tendremos la salida en la variable output. Para poder debuggear redirect() como son funciones sueltas y no tiene un punto de llamada, le agregaremos al final un llamado a la función que luego lo quitaremos es solo para poder debuggear.
Allí esta son las dos lineas que nos permiten llamar a la función redirect(), la primera importara de teststreams.py la función interact(), como en el ejemplo que probamos antes, y luego llamaremos a la función redirect(), pongamos un breakpoint allí.
Recordemos luego de darle a DEBUG, que no necesitamos argumentos en este caso.
Allí paro en nuestro breakpoint, avanzamos con f6 para no entrar en el import.
Allí esta el llamado a la función redirect() y como se asignan los argumentos que le pasamos, en la variable function, tendremos la función interact(), en args, estará vacío pues la función interact() no necesita argumentos, si no habría que pasárselos allí, y en input tendrá la string que armamos como entrada, para redireccionar lo que se debería teclear al usar interact().
Ingresamos con f7 dentro de la función y vemos que todo se asigno como esperamos.
Allí la variable savestreams guardará el estado original de sys.stdin y sys.stdout para que a la salida luego de redireccionarlos se puedan restaurar al terminar el trabajo. Luego vemos como antes de llamar a la función a ejecutar, se instancia sys.stdin y sys.stdout en dos clases que creo para ello Input y Output.
Vemos como al instanciar le debe pasar como parámetro obligatorio a la clase Input la string que suplanta lo que deberíamos tipear que esta en la variable input.
Vemos que el método constructor de dicha clase, lo que hace es guardar dicho texto en una variable self.text. Luego llegamos a la instancia en Output, allí sys.stdout se instanciara en dicha clase.
Vemos que en su método constructor inicia una variable self.text que por ahora estará vacía.
Vemos que estas clases tienen algunos métodos que usara mas adelante por ahora realizo solo la instanciación, pero el truco es que cuando se usa raw_input() y print estos llaman a los métodos write() y readline() hechos para manejar la impresión y la entrada por teclado, al crear nuestros propios write() y readline() usara estos últimos en vez de los que usan normalmente para stdin y stdout.
Vemos que allí va a tratar de ejecutar la función interact() dentro de un try-except por si hay algún error.
Vemos que el método read() y readlines() lo que harán es tomar el texto que estaba guardado en self.text y devolverlo en la variable res , también tiene un parámetro size por si en los argumentos se especifica un tamaño, vemos que si size no existe, lo que hace es asignar a res el texto que hay en self.text y vaciar self.text aquí. res, self.text = self.text, '' Es una asignación tipo tupla,a res se le asignara self.text y a self.text se le asignara vacío (“”) De esta forma al usar el método read() devolverá todo lo que se tipeo, mientras que no le pasemos un tamaño, en el caso de pasarse le un tamaño ira al else.
Vemos que la variable size puede ser una lista con varios tamaños, y mediante índice slice lo que hace es sacar los caracteres desde cero hasta size[0], y luego actualizar para quitarle a self.text esos caracteres ya devueltos, así puede ser llamado de nuevo y leerá a partir de allí como hace la función read() cuando leemos de un archivo, si le decimos que lea 5 caracteres leerá cinco y quedara apuntando para la próxima vez que sea lea, a continuar desde donde quedo del 6to en adelante.
En el caso del método readline() vemos que leerá hasta que encuentre un (\n) o sea un salto de linea, si no lo encuentra asignara a res todo self.text y si lo encuentra ira al else donde hará el mismo trabajo que read(), leerá hasta el primer (\n) y por medio de índice slice quitara de self.text la linea que ya leyó y retornara en res.
Nos queda ver los métodos write() y writelines() que usara en reemplazo del print, vemos que a dicho método se le pasa la string que el programa enviara a imprimir, en la variable string y la salida la ira guardando en la variable self.text de esta clase, cada vez que mande a imprimir ira agregándolo a self.text, en el caso de writelines(), será similar solo que se le pasara las lineas y hará un for para llamar a su propia función write() donde guardara en self.text todas las lineas agregándolas.
Allí estamos justo para ejecutar la función, hagamoslo directamente con f6. Una vez que ejecuta la función vemos que no hay salida de la misma en DEBUG I/O y eso es lógico porque le ingresa el texto que le pasamos como string, y la salida estará guardada en la variable self.text de la clase Output, podemos verla en el debug probe una vez que se asigna a result.
Si la imprimimos veremos la salida con su formato
Vemos que funciono perfectamente interact() tomo la entrada de la string que le pasamos, vemos que ingreso el 4,5 y 6 y le calculo los cuadrados y que la salida no se imprimió si no que se guardo en una variable llamada result que sera lo que devolverá mediante el return.
Allí restaura stdin y stdout a sus valores antes de redireccionarlos y luego saldrá de la función redirect(), devolviendo el resultado en la variable result que se asignara a output que esta esperando el valor de retorno de la función.
Vemos que todo funciono como lo esperamos logramos redireccionar la entrada en forma mas cómoda que la que proporciona el sistema, ya que ahora podemos enviar strings como entrada en vez de tener que crear archivos, y podemos tener lo que el programa imprimiría en una variable, lo cual es fácilmente manejable. Le quitaremos lo que le agregamos al final para ejecutarlo, para guardarlo en la forma correcta y así sin los agregados lo adjunto al tutorial. EL MODULO StringIO La técnica anterior es tan cómoda que existe un modulo incluido en python que la realiza fácilmente en forma automática.
Vemos que la clase StringIO es muy parecida a las funciones Input y Output de nuestro script anterior ya que en el primer caso si instanciamos una variable buff, la clase tiene un método write() que va guardando todo y que al llamarlo con getvalue() me devuelve todas las strings que se le ingresaron, y en el segundo ejemplo si le pasamos una string como argumento, tiene un método readline() que va devolviendo las lineas lo cual podría suplantar al readline() de sys.stdin.
Esto lo hago en una ventana de comandos donde tipeo python y escribo allí, extrañamente no funciona bien en la shell interactiva de WING se ve que la redirección mediante StringIO no es el fuerte allí, pero aquí funciona, vemos que instanciamos buff en la clase StringIO y guardamos en una variable temp el estado de sys.stdout para restaurarlo al final. Y luego asignamos a sys.stdout esa instancia buff de forma tal que reemplazamos los métodos propios de sys.stdout por los de la instancia buff, al enviar a imprimir no se ve ninguna salida, porque esta redireccionada, luego restauro sys.stdout, y al final obtengo lo que guardó mediante la función buff.getvalue() que se podría asignar a otra variable llamada resultado.
De esta forma tenemos desviada la salida como vimos.
Vemos que de la misma forma podemos desviar la entrada, allí instanciamos buff en StringIO usando como argumento una string que sera la que leerá en el momento en que se use raw_input(), guardamos en una variable temp el estado de sys.stdin para restaurarlo y luego llamamos a raw_input(), y no tenemos que tipear nada, se guardara en la variable jose, la string que le pasamos, de esta forma utilizando el modulo StringIO, podemos desviar las entradas y salidas de
un programa fácilmente de la misma forma que lo hacia nuestro script redirect.py REDIRECCION DENTRO DE UN COMANDO PRINT El comando print tiene una posibilidad de redireccionar lo que se va a imprimir, directamente a un archivo y eso se realiza mediante >>.
Allí vemos que abrí un archivo con permiso de escritura y redireccioné al mismo la string 'HOLA' >> pepe.close() Cuando cierro con el método close(), se guardara en el mismo.
OTRAS OPCIONES DE REDIRECCION-OS.POPEN (2/3/4) Hemos visto en las partes anteriores la función incluida en python os.popen, que provee una forma cómoda de redireccionar, mas cercana al funcionamiento de la barra vertical, | pues su mismo nombre lo indica popen significa pipe open y nos permitirá conectarnos a las streams de un programa directa y fácilmente. El argumento que se le pasa a la función popen es el mismo que se ejecutaría en una linea de comandos, por lo tanto si queremos usarlo para un script de python deberemos agregarle la palabra python delante.
Allí veo que abro con os.popen el script llamado teststreams.py que hicimos en partes anteriores,
vemos que se construye una pipe o tuberia de forma que mediante el método read(), podemos leer lo que imprimiría en pantalla o sea que de esta forma redireccionamos stdout. Para la redirección de stdin deberemos abrirlo como escritura y se abrirá como un objeto donde cada vez que escribamos con write() le estaremos enviando lo que se tipearía en una entrada.
Probemos con dos script que haremos para ello, los llamaremos hello-in.py y hello-out.py.
Vemos que hello-in.py tomara lo que se tipea en raw_input(), le agregara la palabra HELLO, y lo guardara en un archivo hello-in.txt que creara.
El script hello-out.py solo imprimirá la string 'Hello shell world' Si abro hello-out.py con os.popen podre redireccionar la salida que imprimirá.
Allí obtuve la salida la cual podría asignar a una variable.
Para el caso de la entrada
Vemos que abrimos con os.popen con la 'w', y escribimos con el método write lo que deseamos ingresar, y eso el script debería guardarlo en el archivo.
Si funciono perfectamente. Vemos que os.popen debe manejar las entradas y salidas por separado pero existe os.popen2 que maneja simultáneamente ambas.
Aquí vemos el script hello-in-out.py que combina los anteriores en uno solo, con os.popen2, podemos redireccionar tanto su entrada como su salida a la vez.
Vemos que manejamos ambas a la vez, en este caso childStdIn, estará redireccionado a la entrada y le pasare mediante el método write() la entrada que tipearia en el teclado, y luego childStdOut, estará conectado a la salida y con el método read(), podre leer o asignar a una variable lo que imprimiría.
Vemos que el script funciono y guardo la entrada que estaba en input en el archivo de texto. También existe os.popen3 que es similar solo que retorna un tercero que estará conectado a sys.stderr, con la stream de los errores. childStdin, childStdout, childStderr = os.popen3('python hello-in-out.py') Y os.popen4 también existe solo con dos, pero de forma tal que que output y error estarán unidas en la segunda childStdout_and_err, tendrá la posibilidad de darnos la salida y también el error juntos.
childStdin, childStdout_and_err = os.popen4('python hello-in-out.py') Bueno y con esto terminamos el tema de la redirección y encadenamiento, espero que haya quedado claro así podemos seguir avanzando en tópicos mas difíciles.
Hasta la parte 18 Ricardo Narvaja
PARTE 17-REDIRECCIONANDO STREAMS A OBJETOS DE PYTHON-EL MODULO StringIO-OS.POPEN (2/3/4) En las partes anteriores vimos como redireccionar y encadenar usando los simbolos >< y | que son siblos que se aplican en la linea de comandos mas que de python en si, aunque s epueden utilizar perfectamente. Veremos ahora una forma mas pythoniana de hacer esto que es resetear sys.stdin y sys.stdout a objetos de python del tipo archivo. Haremos un scrit en WING que demostrara este concepto se llamara redirect.py lo analizaremos paso a paso como siempre. ############################################################################# # file-like objects that save standard output text in a string and provide # standard input text from a string; redirect runs a passed-in function # with its output and input streams reset to these file-like class objects; ############################################################################# import sys
# get built-in modules
class Output: def _ _init_ _(self): self.text = '' def write(self, string): self.text = self.text + string def writelines(self, lines): for line in lines: self.write(line)
# simulated output file # empty string when created # add a string of bytes # add each line in a list
class Input: # simulated input file def _ _init_ _(self, input=''): # default argument self.text = input # save string when created def read(self, *size): # optional argument if not size: # read N bytes, or all res, self.text = self.text, '' else: res, self.text = self.text[:size[0]], self.text[size[0]:] return res def readline(self): eoln = self.text.find('\n') # find offset of next eoln if eoln == -1: # slice off through eoln res, self.text = self.text, '' else: res, self.text = self.text[:eoln+1], self.text[eoln+1:] return res def redirect(function, args, input): # redirect stdin/out savestreams = sys.stdin, sys.stdout # run a function object sys.stdin = Input(input) # return stdout text sys.stdout = Output( ) try: function(*args) # was apply(function, args) except: sys.stderr.write('error in function! ') sys.stderr.write("%s, %s\n" % tuple(sys.exc_info( )[:2])) result = sys.stdout.text sys.stdin, sys.stdout = savestreams return result
La idea del funcionamiento de la funcion alli definida redirect es que podamos redireccionar la entrada de un programa que acepta entradas por teclado, y enviarle una string como entrada, podemos antes de explicar como funciona hacer una simples pruebas para ver cual es la idea de su funciamiento.
Alli vemos el script teststream.py que habiamos hecho, al cual al hacerlo correr sin redirigir nos muestra una salida HELLO STRAM WORLD y luego nos pregunta numeros, cada uno que tipeamos le halla el cuadrado y lo imprime, ahora ya vimos que para ingresarle datos de un archivo usando el simbolo < podriamos redirigir su entrada a que lea de alli, pero lo que hara este programa redirect.py sera que redirigira su entrada para que le pasemos una string directamente sin leer de un archivo veamos el ejemplo a continuacion. Recordamos que dentro de teststreams la funcion que realizaba todo el trabajo se llamaba interact() y no necesitaba pasarsele parametros.
O sea que podra importarse dicha funcion y llamarla directamente primero tipeo python en la consola y luego cuando se abre python tipeo. from teststreams import interact interact() Que importara la funcion interact y luego la llamamos
Funcionara en forma similar ahora veamos si la llamamos por medio de redirect.
Alli importamos redirect y luego tipeamos. output = redirect(interact, ( ), '4\n5\n6\n') Lo que hara aquí es que se le pasara la funcion interact() como primer argumento de redirect()., el segundo son los argumentos que necesita esta funcion, como no necesita se pasa vacio, y el tercero es la string de entrada, vemos que redireccionamos la entrada ya que le ingresamos 4, 5 y 6 con saltos de linea entre ellos para separarlos, y la salida la funcion redirect la devuelve mediante su return al archhivo output que la espera, alli vemos que si imprimimos la salida tendremos lo que deberia haber impreso en pantalla el programa. De esta forma es mucho mas comodo trabajar pues podemos pasarle a un programa que esta esperando entrada por teclado directamente una sring que armemos como en este caso, y que lo que deberia imprimir lo devuelva en un archivo, si queremos podemos imprimirlo luego o no,o seguir trabajando con dicha salida , ya que stdin y stdout son restaurados al salir de redirect y tendremos la salida en output. Para poder debuggear redirect como son funciones sueltas y no tiene un punto de llamada, le agregaremos al final un llamado a la funcion que luego lo quitaremos es solo para poder debuggear.
Alli esta son las dos lineas que nos perimiten llamar a la funcion redirect, la primera importara de teststreams la funcion interact como en el ejemplo que probamos antes, y luego llamaremos a la funcion redirect, pongamos un breakpoint alli.
Recordemos quitar luego de darle a DEBUG, que no necesitamos argumentos en este caso.
Alli paro en nuestro breakpoint, avanzamos con f6 para no entrar en el import.
Alli esta el llamado a la funcion redirect() y como se asignan los argumentos que le pasamos, en funcion, tendremos la funcion interact, en args, estara vacio pues la funcion interact no necesita argumentos, si no habria que pasarselos alli, y en input tendra la string que armamos como entrada, para redireccionar lo que se deberia teclear al usar interact().
Ingresamos con f7 dentro de la funcion y vemos que todo se asigno como esperamos.
Alli la variable savestreams guardara el estado original de sys.stdin y sys.stdout para que a la salida luego de redireccionarlos se puedan restaurar al terminar el trabajo. Luego vemos como antes de llamar a la funcion a ejecutar instancia sys.stdin y sys.stdout en dos clases que creo para ellos Input y Output.
Vemos como al instanciar le debe pasar como parametro obligatorio a la clase Input la sring que suplanta lo que deberiamos tipear que esta en la variable input.
Vemos que el metodo init o contructor de dicha clase, lo que hace es guardar dicho texto en una variable self.text. Luego llegamos a la instancia en Output, alli sys.stdout se instaciara en dicha clase.
Vemos que en su metodo constructor inicia una variable self.text que por ahora estara vacia.
Vemos que estas clases tienen algunos metodos que usara mas adelante por ahora realizo solo la instanciacion, pero el truco es que cuando se usa raw_input y print estos llaman a los metodos write y readline hechos para manejar la impresion y la entrada por teclado, al crear nustros propios write y readline usara estos ultimos en vez de los que usan normalmente. Podemos hacer una prueba mas contundente para entender esto
Le agregamos dos lineas dentro de los metodos constructores de Input y Output que no afectan el funcionamiento. .
Paro el script y lo vuelvo a debuggear y llego hasta el punto donde se va a instanciar dentrod e la clase Input, antes de instanciar, veo en el DEBUG PROBE al tipear sys.stdin y el punto en el menu desplegable sus atributos Ahora hagamos la instanciacion
Alli pasamos con F6 y de esta forma se instancia sys.stdin en la clase Input() veamos ahora sus atributos.
Vemos que tiene los de la instancia que le hemos hecho, de cualquier forma el sistema llamara a sys.stdin.readline para leer cuando realice un raw_input y en el caso de output sera similar,
estaremos instanciando en una clase propia que tendra su propio metodo write, con lo cual cuando el sistema trate de usar sys.stdout.write usara el metodo de nuestra clase Output en vez del de sistema.
Vemos que alli va a tratar de ejecutar la funcion interact() dentro de un try-execpt por si hay algun error.
Vemos que el metodo read() y readlines() lo que haran es tomar el texto que estaba guardado en self.text y devolverlo en la variable res , tambien tiene un parametro size por si en lso argumentos se especifica un tamaño, vemos que si size no existe lo que hace es asignar a res el texto que hay en self.text y vaciar self.text aquí. res, self.text = self.text, '' Es una asignacion tipo tupla, res se le asignara self.text y a self.text se le asignara vacio (“”) De esta forma al usar el metodo read devolvera todo lo que se tipeo, mientras que no le pasemos un tamaño, en el caso de pasarsele un tamaño ira al else.
Vemos que la variabel size puede ser una lista con varios tamaños, y mediante split lo que hace es sacar los caracteres desde cero hasta size[0], y luego actualizar para quitarle a self.text esos caracteres ya devueltos asi puede ser llamado de nuevo y leera a partir de alli como hace la funcion read cuando leemos de un archivo, si le decimos que lea 5 caracteres leera cinco y quedara apuntando para la proxima vez que sea lea, continuar desde donde quedo del 6to en adelante.
En el caso del metodo readline vemos que leera hasta que encuentre un (\n) o sea un salto de linea, si no lo encuentra asignara a res todo self.text y si lo encuentra ira al else donde hara el mismo trabajo que read leera hasta el primer (\n) y por medio de indice slice quitara de self.text la linea que ya leyo y retornara en res.
Nos queda ver los metodos write y writelines que usara en reemplazo del print, vemos que a dicho metodo se le pasa la string que el programa enviara a imprimir en la variable string y la salida la ira guardando en la variabel self.text de esta clase, cada vez que mande a imprimir ira agregandolo a self.text en el caso se writelines sera similar solo que se le pasara las lineas y hara un for para llamar a su propia funcion write donde guardara en self.text todas las lineas agregandolas.
Alli estamos justo para ejecutar la funcion, hagamoslo directamente con f6. Una vez ejecuta la funcion vemos que no hay salida de la misma en DEBUG I/O y eso es logico porque le ingresa el texto que le pasamos como string, y la salida estara en la variable self.text de la clase Output, podemos verla en el debug probe una vez que se asigna a result.
Si la imprimimos veremos la salida con su formato
Vemos que funciono perfectamente interact tomo la entrada de la string que le pasamos, vemos que ingreso el 4.5 y 6 y le calculo los cuadrados y que la salida no se imprimio si no que se guardo en una variable llamada result que sera lo que devolvera.
Alli restaura stdin y stdout a sus valores antes de redireccionarlos y luego saldra de la funcion redirect devolviendo el resultado en la variable result que se asignara a output que esta esperando el valor de retorno de la funcion.
Vemos que todo funciono como lo esperamos logramos redireccion de la entrada mas comoda que la que proporciona el sistema ya que ahora podemos enviar strings como entrada en vez de tener que crear archivos, y podemos tener lo que el programa imprimiria en una variable, lo cual es facilmente manejable.
Le quitaremos lo que le agregamos para guardarlo en la forma correcta y asi sin los agregados lo adjunto al tutorial. EL MODULO StringIO La tecnica anterior es tan comoda que existe un modulo incluido en python que la realiza facilmente.
Vemos que la clase StringIO es muy parecida a las funciones Input y Output de nuestro script anterior ya que en el primer caso si instanciamos una variable buff, la clas etiene un metodo write que va guardando todo y que al llamarlo con getvalue() me devuelve todas las strings que se le ingresaron, y en el segundo ejemplo si le pasamos una string tiene un metodo readline() que va devolviendo las lineas lo cual podria suplantar al readline de sys.stdin.
Esto lo hago en una ventana de comandos donde tipeo python y escribo alli, extrañamente no funciona bien en la shell interactiva de python se ve que la redireccion mediante StringIO no es el fuerte alli, pero aquí funciona, vemos que instanciamos buff en la clase StringIO() y guardamos en una variable temp el estado de sys.stdout para restaurarlo al final. Y luego asignamos a sys.stdout esa variable buff de dorma que reemplazamos los metodos propios de sys.stdout por los de la instancia buff, al enviar a imprimir no se ve ninguna salida, porque esta redireccionada, luego restauro sys.stdout, y al final obtengo lo que guardo mediante la funcion
buff.getvalue() que se podria asignar a otra variable llamada resultado.
De esta forma tenemos desviada la salida como vimos.
Vemos que de la misma forma podemos desviar ella entrada, alli instanciamos en StringIO usando como argumento una string que sera la que leera en el momento en que se haga raw_input(), guardamos en una variable temp el estado de sys.stdin para restaurarlo y luego llamamos a raw_input(), y no tenemos que tipear nada se guardara en la variable jose la string que le pasamos, de esta forma utilizando el modulo StringIO, podemos deviar las entradas y salidas de un programa facilmente de la misma forma que lo hacia nuestro script redirect.py REDIRECCION DENTRO DE UN COMANDO PRINT El comando print tiene una posibilidad de redireccionar lo que se va a imprimir directamente a un archivo y eso se realiza mediante >>.
Alli vemos que abri un archivo con permiso de escritura y redireccione al mismo la string HOLA >> pepe.close() Cuando cierro con el metodo close(), se guardara en el mismo.
OTRAS OPCIONES DE REDIRECCION-OS.POPEN (2/3/4) Hemos visto en las parets anteriores la funcion incluida en python os.popen, que provee una forma comoda de redireccionar, mas cercana al funcionamiento de la barra | pues su mismo nombre lo indica popen significa pipe open y nos permitira conectarnos a las streams de un programa directa y facilmente. El argumento que se le pasa a la funcion popen es el mismo que se ejecutaria en una linea de comandos, por lo tanto si queremosusarlo para un script de python deberemos agregarle la palabra python delante.
Alli veo que abro con os.popen el script llamado teststreams.py que hicimos en partes anteriores, alli vemos que se construye una pipe o tuberia de forma que mediante el metodo read(), podemos leer lo que imprimiria en pantalla o sea que de esta forma redireccionamos stdout. Para redireccion stdin deberemos abrirlo como escritura y se abrira como un objeto donde cada vez que escribamos con write() le estaremos enviando lo que se tipea en una entrada.
Probemos con dos script que haremos para ello, los llamaremos hello-in.py y hello-out.py.
Vemos que hello-in.py tomara lo que se tipea en raw_input si no esta redireccionado le agregara la palabra HELLO, y lo guardara en un archivo hello-in.txt que creara.
El script hello-out.py solo imprimira si no esta redireccionado la string 'Hello shell world' Si abro hello-out.py con popen podre redireccionar la salida que imprimira.
Alli obtuve la salida la cual podria asignar a una variable.
Para el caso de la entrada
Vemos que abrimos con popen con la 'w', y escribimos con el metodo write lo que deseamos ingresar, y eso deberia escribirse en el archivo.
Si funciono perfectamente. Vemos que popen debe manejar las entradas y salidas por separado pero existe os.popen2 que maneja simultaneamente ambas.
Aquí vemos el script hello-in-out.py que combina los anteriores en uno solo, con popen2, podemos redireccionar tanto su entrada como su salida a la vez.
Vemos que manejamos ambas a la vez, en este caso childStdIn, estara redireccionado a la entrada y le pasare mediante el metodo write() la entrada que tipearia en el teclado, y luego childStdOut, estara conectado a la salida y con el metodo read(), podre leer o asignar a una variable lo que imprimiria.
Vemos que el script funciono y guardo la entrada que estaba en input en el archivo de texto. Tambien existe os.popen3 que es similar solo que retorna un tercero que estara conectado a stderr, con la stream de los errores. childStdin, childStdout, childStderr = os.popen3('python hello-in-out.py') y os.popen4 tambien solo restirna 2 solo que output y error estaran unidas en la segunda childStdout_and_err tendra la posibilidad de darnos la salida y tambien el error juntos. childStdin, childStdout_and_err = os.popen4('python hello-in-out.py') Bueno y con esto terminamos el tema de la redireccion y encadenamiento, espero que haya quedado claro asi podemos seguir avanzando en topicos mas dificiles.
Hasta la parte 18 Ricardo Narvaja
PARTE 18:HERRAMIENTAS Y DE ARCHIVOS Y DIRECTORIOS En esta parte veremos herramientas para manejar archivos y directorios, sabemos que los archivos externos que podemos abrir en nuestra máquina, son el corazón de lo que podamos hacer con la misma. Un programa puede leer sus entradas de un archivo, guardar el trabajo realizado en el mismo o otro archivo y chequear contra otro archivo, realmente el manejo de archivos es primordial en nuestro uso cotidiano. En python la función open(), es la base para manejar estas acciones, como sabemos esta función es parte del lenguaje python y viene incluida en el mismo, la hemos usado en partes anteriores, pero en sentido técnico lo que hace open, es darnos acceso directo a stdio, que nos devuelve un objeto archivo, que estará conectado al archivo externo, y tiene incluidos métodos que permiten manejarlo. Sabemos que el principal aliado en el manejo de archivos como dijimos es la función open(), el objeto archivo que es devuelto por open, tiene métodos para leer datos (read, readline, readlines), métodos para escribir datos (write, writelines), para cerrar y liberar recursos (close), movernos por el archivo (seek), forzar datos a ser transferidos fuera de buffers (flush) y algunos mas como fileno(), que devuelve un entero representando el descriptor de fichero, ya veremos su uso en los ejemplos. ARCHIVOS DE SALIDA Para generar un archivo de salida nuevo, solo debemos llamar a la función open(), con dos argumentos, el nombre del archivo que va a ser creado, y el segundo argumento la letra 'w', que es la abreviatura de write, pasada como string. Una vez abierto el archivo, el método write() escribirá los datos en el mismo lo que se realizará efectivamente cuando cerremos el archivo con close(). En la shell interactiva de WING o en la que querramos tipeemos.
Y el archivo se creará, si existe algún archivo con el mismo nombre al usar el parámetro 'w' , se borrará y será reemplazado por el nuevo que creamos, así que hay que tener cuidado con eso para no perder datos. Vemos alli el método name, si necesitamos el nombre del archivo al cual la variable file, esta relacionada, pues con name, lo obtenemos facilmente. Tambien solo tipeando file nos da la informacion, pero como por ahi necesitamos guardar el nombre en una variable el metodo name es util ya que retornara solo el nombre y lo podremos utilizar en el
programa. >> file >>> >>> pepe=file.name >>> pepe 'data.txt' >>> Escribimos algun texto en el archivo.
Luego escribo una linea mas y lo cierro. file.write('Bye file world.\n') file.close() En este momento puedo ir a mirar el archivo, a ver si lo que escribí esta alli.
No hay nada raro aquí, si abrimos una consola de DOS y vamos al directorio conde se creo el archivo con cd (path)
Veo que puedo listar con dir y aparecera el archivo en este caso use dir d* para que muestre solo los que empiezan con d para que no sea tan largo el listado, y con type, podemos ver el contenido del archivo perfectamente. En el ejemplo anterior precediendo al nombre del archivo puede ir el path completo al mismo, para elegir donde se guardara, si no existe path, python lo guardara en el actual directorio de trabajo del script, o CWD como hemos visto en partes anteriores.
Alli obtenemos el directorio de trabajo de forma que si no especificamos path, lo guardara en mi caso en C:\ , normalmente sera el lugar donde esta ubicado el script, como vimos.
Vemos alli que abrimos otro archivo, y esta vez escribimos las lineas directamente con el metodo writelines(), normalmente este metodo toma una lista donde cada campo es una linea, y las escribe todas juntas al archivo, cuando cerramos con file.close() se guardara efectivamente.
El metodo close(), que se utiliza para finalizar el trabajo con el archivo y guardar efectivamente su contenido, ademas libera los recursos del sistema que ocupaba al mantenerlo abierto. Normalmente el archivo puede no ser cerrado por close(), ya que al terminar el script, el sistema lo cierra automaticamente y todos los cambios en el archivo son guardados y liberados los recursos, el hecho que nosotros usemos close() deliberadamente, es porque vamos a mirar el contenido del archivo por fuera del script y este no se mostrara hasta que no se cierre con close o se termine el script, pero realmente dentro del trabajo de un script, que tiene acceso al archivo, no es necesario usar close(), ya que siempre el contenido se mantendra actualizado cada vez que hagamos write, y el script podra leer del mismo sin problemas. Una forma de guardar en un archivo y que se cierre automaticamente es no referenciarlo. open('somefile.txt', 'w').write("G'day Bruce\n")
Vemos la forma de realizarlo en una sola linea, como no se asigna a una variable que mantendra el objeto abierto, sino que se crea temporariamente el objeto, se guarda en el y al terminar de guardar se cerrará, y ya no podremos guardar mas en el mismo ya que no existe un objeto asociado al archivo en el cual se pueda seguir trabajando, a no ser que abramos y creemos uno nuevo, pero si lo hacemos con 'w' borrara el contenido anterior.(podria ser reabierto con el parametro 'a' en vez de 'w', que mantiene lo que hay escrito y agrega a continuacion, 'a' es la abreviatura de append) ARCHIVOS DE ENTRADA Hemos visto en el punto anterior como crear archivos para guardar informacion, o sea como manejar archivos de salida, ahora toca el turno al manejo de archivos de entrada. Sabemos que leer datos de un archivo es tan sencillo como guardar datos, se utiliza tambien la funcion open(), usando como primer parametro el nombre del archivo a abrir, que como en el caso anterior si no es especificado path, buscara el archvo en el directorio de trabajo o current working directory (CWD), el segundo parametro es la 'r' que significa read, y puede ser omitido, ya que si uno no lo especifica, es tomado por default como 'r'.
Alli abro el archivo que cree antes, y con el metodo read(), puedo leer el contenido completo
Tambien puedo asignarlo a una variable.
Aquí lo asignamos a una variable, a la cual logicamente al mandar imprimir se vera con el formato correspondiente de acuerdo a los saltos de linea que posee.
Como el metodo readlines(), me devuelve una lista con todas la lineas del archivo, al usar un for, puedo recorerlas una a una e imprimirlas todas o las que quiera.
Alli vemos la lista de lineas que devuelve el metodo readlines(), siempre antes de volver a leer el
archivo debo cerrar con close(), ya que sino el puntero de lectura del archivo quedara apuntando al final del archivo ya que ya lo lei entero, y no podre releer desde el inicio.
Para poder rebobinar un archivo a su inicio o mover el puntero de lectura al punto que querramos existe el metodo seek().
Alli vemos que leemos completamente las lineas y luego usamos seek(0) que reseteara para poder leer a partir de 0 el archivo nuevamente, si hubieramos reseteado con seek(1)
Vemos que el puntero queda para leer a partir del carácter 1, y de esta forma no se leerá el primer carácter, asi que el metodo seek() es una forma de manejar el puntero de lectura, para apuntarlo a donde uno quiera. Usemoslo con read(), leeremos los primeros 5 bytes del archivo. Logicamente si vuelvo a aplicar read leera a partir de alli.
Si quiero rebobinar para leer desde el incio usando seek (0) lo podre hacer.
Alli vemos como leo desde el inicio nuevamente. Ya vimos que el metodo readlines() devuelve una lista con las lineas del archivo pero si quiero leer una linea sola puedo user readline() sin la ese al final, y leera una sola linea a la vez.
Alli puse el puntero al inicio y lei una sola linea desde el comienzo con readline() si lo aplico nuevamente leere la segunda linea.
Y por supuesto con seek() podre apuntar al inicio nuevamente o a donde quiera para que continue leyendo lineas desde alli.
El metodo seek() que usualmente se utiliza con un solo argumento como hemos visto en los ejemplos anteriores tiene un segundo argumento opcional que es 0, 1 o 2. 0 (significa posicion absoluta en el archivo)
1 (significa que la posición se tomará relativa a la ubicación actual del puntero) 2 (significa que la posicion se lee desde el final del archivo)
Igualmente son poco usados, asi que el valor por default cero, como segundo argumento sin necesidad de pasarselo, es el que casi siempre se utiliza. LEYENDO ARCHIVOS CON ITERADORES La forma tradicional de leer archivos linea a linea dentro de un for >>> file = open('data2.txt') >>> for line in file.readlines( ):
...
# open input file object # read into line string list
print line,
En los python recientes se ha simplificado dado que el objeto archivo, posee un iterador que es lo suficientemente inteligente como para darse cuenta de que al hacer un for line in .... estamos iterando a traves de las lineas del archivo asi que no necesita usar readlines().
Alli vemos que al usar line, el iterador, reconoce que estamos leyendo lineas del archivo, y no necesita usar readlines() como en la forma tradicional. Aun mas corto se puede hacer con un objeto temporario cerramos con file.close() y luego vemos que el for line in.. directamente se aplica a un objeto temporal que se crea con open('data2.txt')
Ademas la forma que utiliza iteradores no lee todas las lineas y las coloca en una lista lo cual supone un mayor consumo de recursos, por lo tanto la forma de leer lineas usando iteradores, es la forma utilizada hoy dia por economia de recursos y brevedad. Aquí vemos algunos ejemplos donde el uso de iteradores abrevia el trabajo y optimiza recursos cerramos con file.close() y luego.. >>> lines = [line.upper( ) for line in open('data2.txt')]
>>> lines ['HELLO FILE WORLD!\n', 'BYE FILE WORLD.\ Vemos que el iterador tomara linea a linea y se la pasara a line.upper() que las ira pasando a mayusculas y guardando en la variable lines en forma de lista, ya que esta siendo utilizado como patron de creacion de la lista, por eso esta entre corchetes. Otro ejemplo similar siempre recordamos cerrar con file.close() entre ejemplo y ejemplo >>> lines = [line.rstrip( ) for line in open('data2.txt')] >>> lines ['Hello file world!', 'Bye file world.'] En similar al anterior la funcion rstrip() le quitara el (\n) final a cada linea y armara la lista.
OTRAS FORMAS DE MANEJAR ARCHIVOS Como hemos comentado ademas del modo 'w' y 'r' para manejar archivos existe el modo 'a' que significa append este modo tiene metodos write para escribir sin borrar lo que existia, apendeando a continuacion.
Vemos que agrego a continuacion sin perderse lo que existia antes en el archivo como pasa con 'w'. ARCHIVOS BINARIOS En el caso de que manejemos archvos binarios, es conveniente que se especifique con una b de
binario a continuacion de la r o la w ya que de esta forma podra leer todo tipo de valor en cada byte sin formato de texto, y del archivo se leeran los bytes crudos, lo cual es util si abrimos un archivo de imagen por ejemplo o un mp3. >>> file = open('data.txt', 'wb')
# open binary output file
>>> file = open('data.txt', 'rb')
# open binary input file
Por ejemplo si quiero leer bytes del crackme de cruehead o cualquier otro ejecutable.
Vemos que lee perfectamente todos los bytes si lo abrieramos sin la b de binario.
Vemos diferencias de lectura que se explicarán en el punto siguiente. >>> data = 'a\0b\0c' >>> data 'a\x00b\x00c' >>> len(data)
5 Por supuesto las strings de python no terminan en el caracter cero como en C, aceptan el carácter cero perfectamente y el final de string se maneja por el tamaño que python mantiene guardado.
FINAL DE LINEA EN WINDOWS Realmente colocar la b para archivos binarios solo hace falta en sistemas Windows, ya que en otros sistemas trabajando con 'w', 'r' y 'a' sera suficiente para comprender el problema y porque ocurre hay que remontarse a la historia del sistema Windows y como maneja el fin de linea. Por razones históricas el final de linea se representa como un solo carácter en UNIX y LINUX solo con \n, pero en Windows se representa con dos, \r \n, esto muchas veces hace que archivos creados en una plataforma se vean raro cuando se abren en la otra. La mayoria de los editores de texto aceptan ambas posibilidades, una excepcion es el Notepad de windows, si abrimos un archivo creado en sistema Linux se vera como una sola linea larga, ya que no interpreta los saltos de linea de un solo carácter. Los scripts de python no tienen el problema de los caracteres de fin de linea porque actualmente mapean en archivos de texto los \r\n como un solo carácter \n, eso es lo que vemos como diferencia en el ejemplo anterior, cuando lo abririmos en modo texto python translada los \r\n a solo \n y eso es valido siempre y cuando el archivo sea de texto, al suprimir un byte en un archivo binario, le faltara y no funcionara bien, vemos que cuando lo abrimos como binario los dos bytes \r \n son representados y no hay problema ya que no se pierden bytes del archivo. Por lo tanto abreviando en windows ocurre lo siguiente
Archivos abiertos en modo text , \r\n es traducido a \n cuando se realiza el input..
Archivos abiertos en modo text , \n es traducido a \r\n cuando se realiza el output.
Archivos abiertos en modo binario, no se realiza traduccion en input o output.
En Unix Linux, no se realiza traduccion porque no es necesaria, sea cual sea el modo de abrirlo..
Asi que solo debemos tener cuidado en Windows cuando trabajemos con archivos binarios, agregar la b para no corromperlos, para los otros casos no hay problemas, tanto en Linux como Mac, manejan perfectamente los archivos binarios, sin necesidad de agregarle ninguna letra mas. STRUCT EN DATOS BINARIOS Vimos que manejar datos binarios no es problema, estos pueden ser leidos, y guardados perfectamente, pero como procesamos los datos si cuando imprimimos muchos de esos caracteres son no imprimibles, y se ven como secuancias de \xNN bytes dificiles de interpretar, mezclados con caracteres imprimibles, vimos en el ejemplo de los datos binarios que al imprimir se veia confuso.
Si uno solo tiene que pasar datos de un archivo a otro o leer cierta cantidad de bytes no habra problema, slice en este ultimo caso hara el trabajo de la misma forma que siempre, ahora para tener un control mas completo del contenido la librería struct es una herramienta poderosa que vendrá en nuestra ayuda.
El modulo struct contiene formas de packear y desempacar datos binarios. Veamos un par de ejemplos. Si quiero empacar en una string de python varios objetos de python diferentes como strings, numeros, floats, etc, puedo hacerlo luego de importar struct. .import struct .data = struct.pack('>i4shf', 2, 'spam', 3, 1.234) El tema esta aquí en el formato que esta resaltado que permite packear los 4 valores que se pasan a continuacion en una string.
En esta primera tablita vemos el primer carácter del formato > significa big-endian, luego se puede ver en esta otra tablita.
La i significa que el primer argumento será un entero dword, lo cual es logico pues el primer valor es un 2. .data = struct.pack('>i4shf', 2, 'spam', 3, 1.234)
Luego viene 4s que es una string de cuatro caracteres que se refiere a la string 'spam' .data = struct.pack('>i4shf', 2, 'spam', 3, 1.234) Luego viene la h que es un word, y al final la f que se refiere al número de punto flotante o float que esta al final 1.234. Al ejecutar esto vemos que arma en data una string con todo ello.
Alli vemos el 2 al inicio, luego la string spam, luego el 3 y al final el valor flotante todo armado en una string, ahora como struct tiene el metodo pack, para generarlo tambien tiene un metodo unpack con lo cual se puede recuperar los objetos iniciales.
Vemos que el primer argumento sera el mismo formato con el cual lo construimos y el segundo argumento sera la string a desempacar, el resultado se guarda en values que sera una tupla con los objetos originales. Otro ejemplo: >>> from struct import * >>> pack('hhl', 1, 2, 3) '\x00\x01\x00\x02\x00\x00\x00\x03' >>> unpack('hhl', '\x00\x01\x00\x02\x00\x00\x00\x03') (1, 2, 3)
Alli vemos como packea los numeros 1 , 2 y 3 los dos primeros como words y el tercero como dword. Y vemos como lo unpackea a sus valores originales. >>> struct.pack('l', 0x0121000)
'\x00\x12\x10\x00' si le quiero extraer un word. >>> struct.pack('>h', 0x0121000) '\x10\x00' Tambien el indice slice nos puede ayudar trabajando con unpack veamos el siguiente ejemplo. bytes='\x00\x00\x00\x02spam\x00\x03?\x9d\xf3\xb6'
>>> number = bytes[8:10] >>> number '\x00\x03' >>> struct.unpack('>h', number) (3,) Aquí vemos que primero extrajo los bytes y luego le aplico unpack solo a ellos con los cual solo reconstruyo el objeto que era originalmente era el 3, me lo devuelve en una tupla de un solo elemento.
Bueno en la siguiente parte continuaremos con mas Hasta la parte 19 Ricardo Narvaja
PARTE 19-HERRAMIENTAS Y DE ARCHIVOS Y DIRECTORIO HERRAMIENTAS DE ARCHIVO EN EL MODULO OS Seguimos profundizando en la teoría, porque luego vendrá el salto a la creación de programas y si no estamos duchos en toda la teoría, fallaremos miserablemente así que continuamos adelante se que es un poco denso, pero nada se consigue sin esfuerzo así que adelante. El modulo os tiene un set adicional de herramientas para manejar archivos, diferentes a los que vienen incluidos por defecto, que hemos visto hasta ahora. os.open( path, flags, mode) Abre un archivo y devuelve su descriptor os.read( descriptor, N) Lee N bytes y devuelve una string os.write( descriptor, string) Escribe bytes de una string al archivo os.lseek( descriptor, position) Mueve el puntero de posición en el archivo. Vemos que son bastante parecidas a las funciones que trae python por defecto, salvo que tiene un descriptor o handle, que identifica al archivo. Vemos que el funcionamiento es bastante parecido al que conocemos con las apis de Windows, con CreateFileA se obtiene el handle, y luego ReadFile y WriteFile, leen y escriben del archivo usando ese handle o descriptor. Para entender el funcionamiento haremos algunos experimentos con este set de herramientas para familiarizarnos con ellos y ver las similitudes y diferencias con el standard incluido en python. Como hemos mencionado, la función fileno() devuelve el descriptor de un objeto tipo archivo, por lo tanto si hacemos.
Vemos que el descriptor de sys.stdin el stream de entrada es siempre 0, lo mismo que de sys.stdout y sys.stderr 1 y 2 respectivamente.
Allí vemos en una consola de DOS como los valores son 0, 1 y 2 respectivamente, si lo intento hacer en una shell interactiva de python, sys.stdout me da error, seguramente algún problema de adaptación de la shell, pero vemos que los valores por defecto son 0,1 y 2 respectivamente. Recordamos que si usamos sys.stdout con el método write es similar a imprimir.
Ahora puedo también usar las herramientas de os y el descriptor de sys.stdout para imprimir.
Usando las herramientas de archivo de os, que vimos al inicio, habíamos visto que: os.write( descriptor, string) Escribe una string al archivo, o sea que usando el descriptor 1 sera similar a print string o sys.stdout.write(string) Estos son objetos streams que trae el sistema, y que se comportan como objetos archivos, pero también lo podemos hacer con objetos tipo archivo que abrimos nosotros explicitamente. Vemos en la consola el numero 23 que retorna la función que es el numero de caracteres escritos.
Si creo un archivo nuevo para guardar datos allí con el open() normal.
Luego puedo hallar su descriptor con la función fileno() en esta caso se asigna el descriptor a fd, y vemos que vale 3. Finalmente usando ese descriptor utilizamos la herramienta os.write a la cual le pasamos el descriptor y la string que queremos que guarde.
Por supuesto luego cerramos con file.close() como siempre. Si se pueden hacer las mismas cosas con ambos métodos, el incluido en python y el del modulo os, porque existen ambos? La respuesta es un poco difícil de entender, pero la realidad es que el usar las tools de os, nos da mayor control a bajo nivel de los archivos, veamos un ejemplo para ver si nos clarifica. fdfile = os.open(r'C:\temp\spam.txt', (os.O_RDWR | os.O_BINARY))
Aquí se abre el archivo spam.txt, los argumentos (os.O_RDWR | os.O_BINARY) son equivalentes a READ y WRITE y BINARY es que se abre en modo binario, la función abre el archivo y devuelve el descriptor que se asigna a fdfile. Allí vemos el descriptor que vale 3.
Usando el descriptor leemos los primeros 20 bytes del archivo. Luego con os.lseek(), reseteo el puntero a cero nuevamente de forma que empiezo a leer desde el comienzo del archivo nuevamente y leo 100 bytes desde el inicio.
Si reseteo de nuevo el archivo puedo usar write desde el inicio ya que el archivo fue abierto como READ-WRITE
y sobrescribir los primeros cinco bytes con la palabra HELLO en mayúscula.
Vemos que si vuelvo a resetear el archivo y leer del inicio aparece la palabra HELLO en mayúscula que fue guardada.
Vemos que la forma de manejar un archivo es mucho mas practica, ya que se puede leer y escribir en el, si fue abierto como read-write, mientras que con el sistema tradicional, puedo abrir un archivo como lectura o escritura, al menos con lo visto hasta ahora, aquí se pueden realizar ambos trabajos con una sola apertura fácilmente. OTRAS HERRAMIENTAS DE OS PARA ARCHIVOS Aquí vemos otras herramientas incluidas que sirven para diferentes tareas como renombrar, borrar, y cambiar el dueño o permisos de los archivos. RENOMBRAR (os.rename) >>> os.rename(r'C:\temp\spam.txt', r'C:\temp\eggs.txt') BORRAR (os.remove) >>> os.remove(r'C:\temp\eggs.txt') CAMBIANDO EL DUEÑO DEL ARCHIVO (os.chown) os.chown('filename', 'usernameID', 'groupnameID') pwd.getpwnam ('user')
#me da el usernameID
grp.getgrnam ('grupo')
#me da el groupnameID
CAMBIANDO EL PERMISO ( os.chmod) >>> os.chmod('spam.txt', 0777) Yo se que es difícil recordar absolutamente todo, y hay muchísimos comandas, pero al menos si uno se acuerda que lo leyó, puede volver a consultar el curso y buscarlo al momento de usarlo, al final del mismo haremos un sistema de búsqueda de temas y palabras, una especie de search detallado de lo que se ve en cada parte, así uno puede hallar fácilmente lo que busca y consultar como era que se utilizaba. SCANNERS DE ARCHIVOS A veces necesitamos un script que abra un archivo y vaya escaneando linea a linea y a cada una le aplique una función, este script que vemos a continuación esta preparado para eso, se puede importar y pasarle cualquier función que pueda aplicarsele a las lineas que se leen de un archivo.
Vemos que es muy simple, puedo importar y pasarle el nombre del archivo, y la función a aplicarle a cada linea, y a cada una le aplicara dicha función, lo guardaremos en la carpeta de módulos de python para poder importarlo y usarlo desde otros scripts. Ahora crearemos un script que lo utilizara se llamara commands.py que no va en la carpeta de módulos, sino en el directorio en el cual estamos trabajando, en mi caso C:\.
Vemos que al inicio importa argv del modulo sys, para poder trabajar con argumentos, luego importa el scanner que hemos creado anteriormente. Creamos un archivo hillbillies.txt en la misma carpeta donde esta este script
El contenido del mismo sera
Hay que tener cuidado de no colocar caracteres extra en las lineas subsiguientes o sea borrar los caracteres vacíos o de salto de linea que pueda haber en las lineas abajo de las 4 que usara.
Vemos que aquí después de las definiciones comienza a ejecutar filename = 'data.txt' if len(argv) == 2: filename = argv[1] scanner(filename, processLine)
# allow filename cmd arg # start the scanner
Vemos que el script tiene un nombre de archivo por default que buscará en caso de que no le pasemos un nombre de archivo como argumento, por lo tanto como queremos aplicárselo al archivo que creamos recién y no a data.txt deberemos pasarle el nombre del archivo de texto como argumento al ejecutar el script. Vemos que llama a la función scanner que aplicara a cada linea que lea del archivo la función processLine que esta definida arriba, veamos que hace dicha función. def processLine(line): if line[0] == '*': print "Ms.", line[1:-1] elif line[0] == '+': print "Mr.", line[1:-1]
# define a function # applied to each line
# strip first and last char: \n
else: raise UnknownCommand, line
# raise an exception
Aquí vemos el trabajo de la función, si el primer carácter es un asterisco imprime la palabra Ms. y si es un signo +, imprime Mr., y ademas luego imprime usando indice slice quitando ese primer carácter y el último de cada linea, que es el \n del final de linea. Si no halla al inicio de la linea un asterisco o un signo suma genera una excepción. Probemoslo desde una consola de DOS yendo al path donde esta el script y el archivo de texto que va a leer.
Vemos que el script leyó del archivo de texto, y que usando el modulo scanner que importo, se ayudo para hacer mas breve el trabajo y poder dar formato e imprimir la salida en la forma que quería, sin asteriscos ni signos de suma y poniendo el Ms o Mr delante según el caso En la parte 20 continuaremos adelante con la parte de herramientas de directorios Hasta la parte 20 Ricardo Narvaja
PARTE 20-HERRAMIENTAS DE DIRECTORIOS Existen en los diferentes sistemas, herramientas incluidas para realizar trabajos sobre directorios o carpetas, pongamos por ejemplo que tengamos que buscar un script de cierto nombre en nuestra maquina, según el sistema operativo en el cual estemos podremos hacerlo fácilmente, pero si hacemos un script de python que realice la misma tarea podremos correrlo en cualquiera de los sistemas en los cuales python funciona, Windows, Mac, Linux etc y funcionará, independientemente del sistema, ademas que tenemos la posibilidad de importarlo y usarlo en otros scripts para realizar dicha tarea. Vayamos paso a paso, lo primero que debemos saber para construir un script como el que hablamos es antes que nada saber como listar un solo directorio, una vez hecho eso, teniendo los nombres de los archivos podríamos buscarlo con un loop entre todos los nombres, si existe el deseado, por ejemplo. Ahora como hacíamos para listar el contenido de una carpeta, cuando no sabíamos nada de python, en Windows con hacer doble click en el icono de una carpeta en el Explorador de Windows, entramos en ella y nos mostrara su contenido, pero vayamos al método en una consola, en Windows en una consola de DOS, tipeando el comando dir, listamos su contenido, en Linux y Mac tipeando el comando ls, hará el mismo trabajo. Una de las formas de hacerlo en python es usar el comando os.popen(), hemos visto en partes anteriores que os.popen() corria un comando de shell, que podía abrir un archivo y devolvernos un objeto archivo, del cual podíamos leer sus streams, aquí funcionara en forma parecida, veamos los ejemplos.
Aquí tengo un directorio TEMP que he creado con algunos archivos para poder trabajar, si lo listo en una consola de DOS deberé primero ir al path con el comando cd.
Y luego listare con el comando dir si es en Windows o con el comando ls, en Linux o Mac.
Si quiero ver solo los nombres de los archivos usaría dir /b.
Allí en Linux también creo una carpeta TEMP, esto es solo para mostrar, los que no tengan Linux, por supuesto, solo lean las partes de Linux.
Ahora en el terminal de Linux si voy al directorio con el comando cd y luego tipeo ls vere el contenido del mismo.
Tanto en la carpeta de Windows como en la de Linux vemos que cree una subcarpeta llamada newdir a la cual le pondré también un par de archivos dentro para poder practicar listarlos desde python.
Y en Linux
Bueno en ambos puedo listar el contenido de newdir, en Windows usando dir newdir
Y aquí en LINUX usando ls newdir
Vemos que no hay problema en ambos sistemas usando comandos diferentes podremos listar. Ahora volvamos a Windows y tipeemos en la misma consola de DOS.
Vemos que así mismo como tipeabamos el nombre de un archivo y lo ejecutábamos con os.popen(), se creaba un objeto archivo al cual conectábamos con su stream de salida, y podíamos mediante readlines(), leer lo que imprimía, aquí hacemos lo mismo pero con un comando de shell, directamente os.popen() ejecuta ese comando y se crea un objeto archivo y se conecta a la salida, de forma que con readlines() podemos leer lo que imprimiría en pantalla al ejecutar dicho comando, jeje. Si alguien no recuerda se hablo de ello en la parte 17 de esta segunda etapa aquí esta el recorte de dicho tute, ahí se explicaba como con os.popen() podíamos ejecutar un archivo y con read() en ese caso leer lo que imprimirá en su salida. -----------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------Ahora aquí estamos en un caso similar solo que en una forma mas genérica os.popen() no solo ejecuta archivos, si no que realmente puede ejecutar cualquier comando de shell, y por supuesto siempre devolverá un objeto archivo al cual mediante read(), readlines() podremos leer lo que imprime en pantalla.
Vemos que en LINUX aplicándole el comando ls también funciona. Ahora en ambos sistemas, vemos que se muestra el carácter final de retorno de linea lo cual no queda muy lindo, por lo tanto se podría hacer lo siguiente:
Vemos que hicimos un for donde a cada linea antes de imprimirla le quita el \n se puede hacer en Linux en forma similar solo cambiando el dir /b por ls.
Vemos que aquí también funcionan los iteradores, y no es necesario usar readlines() solo con hacer un for line in ... es detectado y leerá linea a linea. Como os.popen() puede correr cualquier comando de shell, podemos también agregar filtros en la búsqueda con asteriscos como si lo hacemos en la linea de comandos.
Allí con *p filtre todos los resultados a que me muestre solo los que comienzan con p.
Esta es una primera opción para leer directorios, pero tiene el problema que hay que detectar el sistema antes de poder ejecutarla dentro de un script, ya que los comandos de shell, son diferentes y ademas tiene una pobre perfomance, veremos a continuación otras mejores posibilidades para listar directorios. EL MODULO GLOB El modulo glob sirve para colectar nombres de un directorio, usando wildcards o asteriscos.
Vemos que funciona en forma similar tanto en Windows como en Linux y Mac.
Vemos que aquí tenemos que usar el asterisco como comodín en la búsqueda
Si busco palabras que comiencen con p, deberé usar p* y si quiero hallar los archivos de texto txt, deberé usar *.txt.
Si quiero leer dentro del directorio newdir
Como vemos glob es una solución mucho mas potente para listar directorios y buscar en ellos, ademas puede trabajar en múltiples directorios, al hacer uso de los asteriscos, como en este ejemplo.
Allí salí de python y volví al directorio raiz, ahora entrare en python y usare glob.
Allí le dije que busque en las carpetas cuyo nombre empieza con P, luego dentro de las carpetas internas en cualquiera y alli dentro los archivo que comiencen con L y tengan extensión .py. 'Python25\\Lib\\locale.py Vemos que las soluciones se ajustan a lo que quería buscar y que me devuelve una lista con los resultados, así que puedo trabajarlo en un for sin problemas.
Lo cual se ve mucho mas presentable, jeje. OS.LISTDIR El comando os.listdir() es una forma sencilla de listar directorios, aunque no tiene la potencia filtradora de glob por los asteriscos, permite listar el contenido fácilmente.
Volvamos a TEMP y allí abramos python. Ahora usemos os.listdir() cuyo argumento deberá ser una string con el nombre del directorio a listar, por supuesto al poner punto solamente listaremos el directorio actual.
Aquí listamos el CWD o Current Working Directory que es el mismo donde estamos en este caso.
Vemos que los subdirectorios los detecta y no hay que poner el path completo
Existe listar el nivel superior con dos puntitos
aquí vemos una comparación de los tres métodos de listar.
>>> os.popen('dir C:\TEMP').readlines() [' El volumen de la unidad C no tiene etiqueta.\n', ' El n\xa3mero de serie del volumen es: DCFD-C3CA\n', '\n', ' Directorio de C:\\TEMP\n', '\n', '23/01/2008 10:37 .\n', '23/01/2008 10:37 ..\n', '23/01 /2008 10:47 newdir\n', '23/01/2008 10:32 2.440 O ffice.odt\n', '23/01/2008 10:32 0 pepe.bmp\n', '22/01/2008 09: 49 23 spam.txt\n', '01/12/2007 21:23 0 texto.txt \n', ' 4 archivos 2.463 bytes\n', ' 3 dirs 6.440.374.272 bytes libres\n'] >>> import glob >>> glob.glob ('C:\TEMP\*') ['C:\\TEMP\\newdir', 'C:\\TEMP\\Office.odt', 'C:\\TEMP\\pepe.bmp', 'C:\\TEMP\\sp am.txt', 'C:\\TEMP\\texto.txt'] >>> os.listdir('C:\TEMP') ['newdir', 'Office.odt', 'pepe.bmp', 'spam.txt', 'texto.txt'] >>> Vemos que de los tres usar os.listdir() es mas rápido y tiene una salida mas prolija y fácil de manejar, aunque glob no se queda atrás y hay que reconocerle sus posibilidades de filtrado mediante asteriscos que os.listdir() no posee, ademas estos últimos dos funcionan en cualquier sistema, mientras que os.popen() sera diferente según el sistema donde se aplique, por lo cual no es muy recomendable. Vemos que en los ejemplos anteriores, glob devuelve el path completo de los archivos, mientras que os.listdir() devuelve el nombre de los archivos solamente. En al caso que usáramos glob y quisiéramos separar el nombre del archivo del resto podemos usar os.path que con su método split(), hace dicho trabajo automáticamente.
En el caso inverso si usamos os.listdir() y queremos hallar los paths completos ya que solo devuelve los nombres de archivos.
Vemos que con os.path.join() podemos unir el path y el nombre del archivo fácilmente. NAVEGANDO DIRECTORIOS Y SUBDIRECTORIOS Vemos que igual en todos los casos anteriores se complica navegar por subdirectorios recursivamente, hay que especificarlos y si hay muchos subdirectorios anidados esto puede ser muy tedioso, por suerte en python existen diversas posibilidades de navegar dentro de subdirectorios sin tener que especificarlos uno a uno, o poner /*/*/* como en glob, un asterisco y barra para cada subdirectorio que quiera entrar a buscar. OS.PATH.WALK La función os.path.walk(), es mucho mas difícil explicarla que aplicarla jeje, tratemos de simplificar su explicación. Lo que hará os.path.walk(), es permitirnos aplicarle una función que definiremos nosotros, a cada archivo dentro de los directorios y subdirectorios anidados que vaya encontrando, hagamos un ejemplo sencillo para entender como va. Definamos primero una función que llamaremos lister() que sera la encargada de aplicarsele a los archivos que va encontrando dentro de cada directorio y subdirectorio. >>> import os >>> def lister(dummy, dirname, filesindir): ... print '[' + dirname + ']' ... for fname in filesindir: ... print os.path.join(dirname, fname)
Allí vemos la definición de nuestra función lister(), siempre os.path.walk() , le pasara tres argumentos, el primero no lo usamos por eso le pusimos dummy, sirve para pasarle variables que necesite nuestra función para trabajar, el segundo sera el nombre del directorio donde esta trabajando en ese momento, y el tercer argumento sera una lista de los archivos que hallo en dicho directorio. Como vemos nuestra función lister() imprimirá el nombre del directorio, y luego mediante os.path.join() lo unirá con el nombre de los archivos que encontró en el. La forma de llamar a nuestra función lister() con os.path.walk() sera: os.path.walk('.', lister, None)
A os.path.walk(), le deberemos pasar tres argumentos, el primero el directorio desde donde comienza a buscar, el segundo el nombre de la función que llamara cada vez que halle un directorio, y tenga la lista de archivos, y el tercero los argumentos opcionales para nuestra función. Probemos este ejemplo en nuestra linea de comandos debemos tipear todo, ya que tenemos que definir lister().
Allí escribimos toda la definición de la función lister(), recordemos respetar el indentado, tanto debajo del def, como dentro del for interno que imprimirá los nombres de los archivos. Ahora llamo a os.path.walk()
Vemos que a partir del directorio inicial en que estamos o sea ('.'), navegara por todos los subdirectorios que haya y le pasara los datos del nombre del directorio donde se encuentra, y el listado de archivos que tenga ese directorio a la función lister(), la cual ejecutara la acción que hallamos programado.. os.path.walk('C:\Python24', lister, None) Nos damos cuenta que realmente va buscando recursivamente dentro de todos los subdirectorios que halla, si tipeamos eso el resultado no entra en una ventana de comandos sola.
Pero vemos que es una buena forma de poder aplicarle funciones propias a cada directorio y los archivos que contiene sin limites de nivel de profundidad de subdirectorios. Haremos un script basado en la función lister() que acabamos de usar, pero mejorado este se llamara lister-walk.py y lo escribimos en WING.
Vemos que su funcionamiento es similar puede funcionar tanto importando la función lister(), o como script principal, usando argumentos, en este caso el argumento debe ser el directorio inicial donde comenzara a buscar. La linea resaltada chequea si una vez que halla un nombre y le suma el path, con la función os.path.isdir(), verifica que no sea el nombre de un directorio, en ese caso no lo imprime, solo imprime el nombre de los archivos , porque vemos en el anterior ejemplo cuando lo usamos desde linea de comandos, que imprime todo lo que halla dentro de un directorio sin fijarse si es un
subdirectorio o un archivo, con esta modificación, solo imprimirá los nombres de llos archivos y no de los subdirectorios que halle dentro, pues como seguirá recursivamente, ya le tocara el turno a ese subdirectorio de ser listado a su tiempo. Vean la imagen como listó newdir como si fuera un archivo, siendo que mas abajo imprimirá su nombre y su contenido que es lo que queremos que se vea solamente.
Bueno tenemos nuestro script hecho podemos llamarlo desde la ventana de comandos ubicado en el path donde guarde a lister_walk.py en mi caso lo guarde en C:\ así que voy allí y tipeo.
Ahora si vemos que me muestra el contenido de TEMP sin poner allí el nombre del subdirectorio newdir, y luego si imprime su nombre y su contenido como corresponde. EL GENERADOR OS.WALK En las ultimas versiones de Python se ha agregado una nueva posibilidad para trastear entre los directorios sin necesitar una función callback como en el caso anterior, que realmente puede ser muy útil, en muchos casos, pero a veces se necesita algo mas simple y para eso se agrego os.walk (no confundir con el anterior os.path.walk) os.walk() normalmente se usa dentro de loops, porque cada vez que es accedido dentro del loop, al pasarle un directorio inicial para que busque, directamente me devolverá una tupla donde el primer campo es el directorio actual donde esta buscando, el segundo una lista de los subdirectorios que halla, y el tercero una lista de los archivos que halla allí. Si yo lo uso fuera de un loop para aclarar veo que:
Uso la función next() que me simula que esta dentro de un loop en el primer ciclo y muestra los valores que devolvería en ese caso, veo que me devuelve una tupla con el primer campo el directorio desde donde comienza a buscar, luego una lista con los subdirectorios, y luego una lista con los archivos dentro del directorio inicial. Si salto al segundo ciclo del loop con gen.next()
Vemos que en este segundo ciclo entro al subdirectorio, y por eso me devuelve la tupla con el primer campo que es el nombre del subdirectorio actual, el segundo estarán los subdirectorios, y en el tercer campo una lista con los archivos y así. Trabajando dentro de un loop veremos lo mismo, buscara recursivamente dentro de los subdirectorios e ira imprimiendo las tuplas que devuelve.
aquí vemos como se organiza un poco mejor la salida
SCRIPTS PROPIOS PARA HACER EL TRABAJO Vimos que os.pathwalk() y os.walk() hacen el trabajo a la perfección pero muchas veces queremos hacerlo con un script propio, este se llamara lister_recur.py hagamoslo en WING.
Vemos que puede llamarse a la función mylister() importándola desde otro script o si no correr el script como principal pasandole el argumento del directorio inicial donde comenzara a listar. Allí imprimirá el directorio actual y mediante os.listdir() hallara los archivos del mismo, y si es un directorio lo salteara pero volverá a llamar a la misma función pasandole este nuevo subdirectorio para que continué dentro del mismo y así recursivamente.
Vemos que el único problema es que esta desordenado, ya que si encuentra un subdirectorio entra en el e imprime su contenido, y luego sale y continua con los archivos del directorio inicial.
Esta versión hace dos for idénticos, el primero imprimirá todos los archivos y salteara los subdirectorios del directorio principal, y en el segundo for, mirara si hay algún subdirectorio y llamara a la función mydir() pasandole ese argumento nuevamente pero ya habiendo impreso todo lo del directorio principal.
Agregarle un \n al inicio de cada directorio aclara mas la lectura
Ahora si quedo mas presentable jeje Aun agregándole mas directorios dentro funciona perfecto
CREANDO NUESTRO PROPIO MODULO FIND Muchas veces necesitamos hallar una lista de resultados entre muchos directorios recursivos, si queremos hallar los scripts de python de extensión py por ejemplo, glob, lo hará en uno o dos directorios que le especifiquemos, pero si queremos hacerlo recursivamente en todos los directorios dentro del que empezamos a buscar necesitamos una herramienta para ello. En la viejas versiones de python existía un modulo find, que realizaba ese trabajo y era muy útil, no sabemos porque fue quitado en la nuevas versiones, pero igual podemos crear un modulo que realice el mismo trabajo y agregarlo a la carpeta de módulos, para tenerlo para usar que es tan útil.
Allí lo guardo en la carpeta Lib de python para poder usarlo e importarlo.
Vemos que crea una función find() que va guardando en una lista llamada matches, las ocurrencias que va encontrando, para ello usa os.path.walk() con la función findvisitor() que crea para que trabaje con los directorios y los archivos que encuentra.
Vemos que la función findvisitor() recibe los argumentos que le pasa os.path.walk(), en este caso le pasa el directorio inicial y los argumentos opcionales (matches,pattern) que son variables que se le pueden pasar necesarias para el funcionamiento de la función.
Este es el núcleo de todo, ya que por cada directorio que encuentre os.path.walk(), llamara aquí, pasandole el directorio donde se encuentra en thisdir, la lista de archivos del mismo en nameshere, y manejara las variables matches que va guardando las ocurrencias y pattern que dice lo que debe comparar para filtrar. if fnmatch.fnmatch(name, pattern): La función fnmatch() busca el pattern (o lo que queremos filtrar) dentro del nombre name de cada archivo. Y solo si coincide lo agrega a matches con append() fullpath = os.path.join(thisdir, name) matches.append(fullpath) Aquí lo ejecutare
Vemos que hallo todos los archivos de texto en todos los subdirectorios ademas del principal.
Vemos que funciona perfectamente y encuentra lo que le pasamos en todos directorios y subdirectorios. Bueno terminamos con las herramientas de directorios, en la parte siguiente empezamos un nuevo tópico Hasta la parte 21 Ricardo Narvaja
PARTE 21-HERRAMIENTAS DE TRABAJO PARALELO-FORKING-OS.EXEC En las siguientes partes enfocaremos la forma de hacer que nuestros programas puedan tener diferentes partes trabajando en paralelo, sabemos que esto es muy importante en la programación actual, ya que no solo se aprovecha mejor el rendimiento del procesador, si no que los programas funcionan realmente mejor, pongamos por ejemplo un programa que tiene que bajar mails de un servidor, si no tuviera varios threads, hasta que no termine de bajar, el usuario no podría hacer mas nada, mientras que si tiene varios threads, mientras uno trabaja en la bajada de los mails, otros pueden seguir cumpliendo otras tareas. Este es un tema absolutamente nuevo que no se ha tocado hasta ahora por lo cual lo llevaremos con cuidado, para ir aprendiendo tranquilamente ya que no es un repaso y merece mas detalle. FORKING PROCESOS Realizar un fork de un proceso para el que no conoce la acepción de la palabra, es la forma tradicional de realizar trabajos en paralelo, y una de las partes mas importantes del funcionamiento de la plataforma UNIX. Forking esta basado en la ida de COPIAR programas, cuando un programa que se esta ejecutando, llama a la rutina fork(), creara una copia exacta del proceso actual en memoria y comenzara a correr dicha copia como un proceso hijo o child, del proceso parent que lo creo. El resultado es que quedan corriendo dos procesos similares el parent y el child, y no hay limitación, el proceso parent puede crear tantos child como quiera, a su vez cualquier child puede crear sus propios childs, de forma que los trabajos en paralelo se multiplicaran. Es de notar que si el proceso padre guardo datos, o se auto modificó de alguna forma, el proceso child sera una copia idéntica con todo lo que el parent tenia en memoria, no arranca desde cero, sino en el mismo estado en el momento que el parent lo creo. Crearemos en el WING un script que intentara forkear procesos hasta que se tipee la letra 'q', el problema es que como el sistema WINDOWS no permite forkear, solo funcionara en LINUX o MAC, pero podemos probarlo en ambos para ver la diferencia se llamara fork1.py.
Vemos la idea de funcionamiento hay dos funciones dentro del programa, parent() y child(), cuando el programa corra como parent mostrara el mensaje 'HELLO FROM PARENT' o sea saluda desde el proceso parent, y cuando sea un proceso child, mostrara 'HELLO FROM CHILD' y saldrá con os.exit(), la diferencia esta en que cuando se forkea un proceso, el parent obtiene el PID del nuevo proceso child, como valor de retorno de dicha función, en este caso se guardara en la variable newpid, y luego al ser diferente de cero saltara aquí: PARENT
Mientras que cuando corra como child, os.fork(), devolverá cero la primera vez que se ejecute, y ira a ejecutar la función child() que mostrara el saludo desde el proceso hijo y se cerrara. CHILD
Vemos que al correrlo en Windows dará error pues no existe la función fork, dentro de os.
Si vemos en el python de Linux, si existe la función fork(), dentro de os, aquí en una terminal de Linux, vemos que no da error al importar os y usar fork().
Cambien veo que si miro la lista de procesos en ese Linux se creo un nuevo proceso en python con el PID 9206, solo con tipear os.fork().
Allí veo el proceso padre y el hijo que se creo con el PID 9206 en mi caso.
Aquí tengo mi WING de LINUX
Veamos si corre dicho script, desde un terminal lo corro.
Veo que primero el parent de PID 9372 crea el primer child que tendrá PID 9373 y es el primero que imprime en pantalla el saludo HELLO FROM CHILD 9373, ya que se imprime el PID a continuación del saludo, luego el parent continua y imprime su saludo HELLO FROM PARENT 9372 y a continuación el nuevo PID de su hijo o sea 9373, también vemos que cada vez que apreto la barra se repite el proceso y se crea un nuevo CHILD en este caso 9374, 9376 etc y siempre saluda desde el nuevo proceso y el padre continua y imprime su PID y el del nuevo hijo que creo.
Allí están todos los procesos creados mediante fork(). Vemos que el script funciona en Linux perfectamente, en Windows podría hacerse una simulación con un comando fork(), agregado por nosotros, pero creo que no vale la pena, existen otras formas que veremos a continuación de trabajar en Windows en paralelo, aunque existe el paquete gratuito CYGWIN que agrega fork() al python de Windows tratando de imitar en lo mas posible el fork() original que traen los sistemas Linux y Mac.
Aquí a continuación agrego la información que trae el libro en ingles sobre este paquete y donde se consigue por si alguien lo necesita, aunque no agregaremos mas sobre el aquí., si alguien lo necesita allí esta la dirección y seguro el paquete traerá instrucciones o en la pagina mostrara como se utiliza
OS.EXEC y sus derivados Veremos aquí la definición de esta nueva función que no habíamos visto hasta hoy las cuales si existen en python de Windows, allí las vemos en el WING de Windows.
Este conjunto de funciones nos permite arrancar un programa, pasandole los argumentos y environment variables, según los casos hay 8 variedades de la función os.exec y son las siguientes:
os.execv( program, commandlinesequence) La variedad 'v' es la mas básica y permite arrancar un programa, pasandole como segundo parámetro allí donde dice commandlinesequence, una lista o tupla de argumentos de linea de comandos que el programa usa en su funcionamiento, o sea esta seria la forma mas parecida a arrancar un programa desde una shell.
os.execl( program, cmdarg1, cmdarg2,... cmdargN) La variedad"l" se le pasa el nombre del ejecutable y los argumentos que necesita el programa separados con comas, en vez de como en el caso anterior que se pasaban en un solo argumento que era una lista o tupla. Es similar a os.execv(program, (cmdarg1, cmdarg2,...)).
os.execlp
os.execvp Agregando la letra p a las funciones execv o execl significa que Python localizara el ejecutable en el standard search path de sistema para buscar
os.execle
os.execve Agregando una letra a execv y execl significa que tendrá un extra ultimo argumento que sera un diccionario conteniendo las environment variables para enviarle al programa.
os.execvpe
os.execlpe Agregando p y e a execv y execl significa usar el search path de sistema y agregar el ultimo argumento que sera un diccionario con las environment variables.
Allí tenemos las variedades son todas muy parecidas con muy pequeñas diferencias nos permitirán arrancar un programa, pasarle argumentos, pasarle enviroment variables, y elegir donde lo buscara, ya se aclarara mejor cuando lo usemos mas adelante, por ahora vamos definiendo funciones que son muy útiles y luego se usaran seguramente. En la parte 22 comenzaremos con el tema threads quizás este 21 sea mas orientado a Linux y por eso la mayoría lo pasen rápidamente pero es bueno tener información, algún día podemos necesitarla, por lo demás las funciones os.exec y sus variantes son generales para todos los sistemas, así que las vamos definiendo para poder tener una idea cuando las usemos mas adelante. Hasta la parte 22 que empezaremos el tema Threads Ricardo Narvaja
22-HERRAMIENTAS DE TRABAJO PARALELO-THREADS-LOCKS Ademas del forking que vimos en la parte anterior, hay otra forma de ejecutar varias tareas al mismo tiempo. Brevemente lo que hacemos es ejecutar una función en paralelo con el resto del programa. La diferencia con los procesos que se arrancan con fork(), es que estos últimos son procesos diferentes, mientas que los threads son tareas independientes que funcionan en paralelo, pero dentro del mismo proceso que los creo y lanzo. EL MODULO THREAD La primera y mas sencilla forma de manejar threads es usando el modulo del mismo nombre, empezaremos con el, luego veremos otras formas mas avanzadas, es bueno mencionar que este modulo funciona en todos los sistemas tanto en Windows, como en Linux o Mac, de la misma forma, por lo cual al utilizarlo no se pierde portabilidad. Crearemos un primer script en WING o en el editor que usen lo llamaremos thread1.py para ejemplificar el funcionamiento de este modulo.
Allí vemos el script es bastante parecido al fork1.py, solo que este importa el modulo thread y dentro de un while, crea nuevos threads mediante la función thread.start_new(), a dicha función hay que pasarle como argumento el nombre de la función que se ejecutara en ese thread, y una tupla con los argumentos que necesita la misma, en este caso hacemos un contador que se lo pasamos para enumerar los threads que se van creando. Cada vez que nos pregunta con raw_input(), si tipeamos cualquier tecla diferente de q, volverá a loopear y crear otro thread, cuando apretemos la tecla q, saldrá mediante break y terminará. Probemoslo desde una ventana de comandos.
Aquí el tema es que cada thread solo imprime el mensaje 'Hello from thread x' y luego se cierra porque ya no tiene mas nada que ejecutar dentro de la función, crearemos otro script donde los threads continúen vivos por mas tiempo para poder verlo mejor, lo llamamos thread-count.py.
Aquí vemos el script, al inicio importa thread y time, que usara para hacer un temporizador, luego define la función counter() que es la que sera llamada cada vez que se cree en nuevo thread, vemos que creara 10 threads dentro de un for que va de 0 a 10. for i in range(10): thread.start_new(counter, (i, 3)) y los parámetros que le pasaran a dicha función, están en la tupla thread.start_new(counter, (i, 3)) O sea que cada vez que se cree un thread se ejecutara la función counter(), llamándola con los argumentos de la tupla (i y 3).
Allí se asignan los argumentos de la función y arranca el nuevo thread al correr la misma. def counter(myId, count): for i in range(count): #time.sleep(1) print '[%s] => %s' % (myId, i)
# this function runs in threads
Dentro de un for la función counter() imprimirá tres veces la salida por cada thread El programa luego de crear los 10 threads, tiene el temporizador time.sleep(4) para que tarde en cerrar el programa, hasta que los threads terminen su tarea. Al correrlo veo la salida que es caótica pues los threads están trabajando en simultaneo, solo con una leve demora entre uno y otro pues fueron creados secuencialmente. C:\>python thread-count.py [0] => 0 [0] => 1 [0] => 2 [1] => 0 [1] => 1 [1] => 2 [2] => 0 [2] => 1 [2] => 2 [3] => 0 [3] => 1 [3] => 2 [4] => 0 [4] => 1 [4] => 2 [5] => 0 [5] => 1 [5] => 2 [6] => 0 [6] => 1 [6] => 2
[7] => 0 [7] => 1 [7] => 2 [8] => 0 [8] => 1 [8] => 2 [9] => 0 [9] => 1 [9] => 2 Main thread exiting. Aquí vemos que todo es tan rápido que no podemos apreciar el trabajo simultaneo, realmente el thread numero 0 , que esta resaltado lo que imprimió, hace sus tres loops antes de que le de tiempo al thread1 a empezar a imprimir, solo se genera una superposición allí por el thread 4, pero si descomentamos la linea que agrega un temporizador dentro de cada thread y guardamos los cambios.
Al correrlo nuevamente.
Vemos que el thread numero cero imprimió su primer loop y luego entra en el timer, mientras tanto el thread numero 1 puede imprimir su primer pasada mientras el thread cero esta esperando. [0] => 0
(primera pasada del thread cero)
[1] => 0
(primera pasada del thread uno)
Así los thread imprimen y esperan y la salida es menos caótica y se puede apreciar como trabajan todos en simultaneo los 10 threads imprimen todos su primera pasada, y luego recién continúan con la segunda pasada y así hasta que los 10 threads imprimen las tres pasadas y el programa termina luego del sleep. Vemos también que el thread principal en este script necesita mantenerse vivo, porque si se termina, todos los threads se cerraran al mismo momento pues se acaba el programa, por eso el temporizador al final del script es necesario, si comentamos este temporizador, el programa principal se cerraría y terminarían todos los threads antes de poder imprimir nada. C:\>python thread-count.py Main thread exiting. Ya veremos alguna formas mas avanzadas en las cuales sin tener que usar sleep se puede mantener threads funcionando y el thread principal no terminará mientras haya algún thread trabajando, pero en estos ejemplos sencillos, si termina el thread principal, muere el programa.
SINCRONIZANDO ACCESOS A OBJETOS GLOBALES Una de los puntos salientes del tema threads es que se genera una intercomunicación entre todos los threads del programa de forma tal que si algún thread cambia alguna variable global, el cambio se reflejara en todos los threads y si alguno lee esa variable, tendrá el valor que modifico otro thread. Por otro lado esto que es un buen punto, merece que el trabajo de los threads sea cuidadoso a la hora de cambiar variables globales, porque puede darse el caso que dos threads cambien simultáneamente una misma variable, y que uno de los cambios se pierda o pueda corromperse el programa al esperar un resultado y obtener otro diferente. También hay otros puntos a cuidar, archivos y streams son también compartidos por todos los threads, si diferentes threads escriben en el mismo stream a la vez o si diferentes threads escriben en un archivo a la vez la cosa puede ponerse fea, jeje. Eso lo podemos ver y nos ha ocurrido ya pero podemos exagerar el ejemplo para que ocurra mas seguido, si al script thread-count.py lo modificamos lo llamaremos thread-count1.py
Vemos que al usarlo muchas veces nos ocurrirá que dos threads quieren acceder casi a la vez a imprimir, si el stream esta redireccionado puede esto provocar error de acceso simultaneo al mismo stream.
Por suerte el modulo thread trae incluidas herramientas de sincronización de acceso a objetos compartidos por diferentes threads. Estas herramientas usan el concepto de lock, o sea que cuando un thread va a cambiar un objeto compartido se realizara un lock y ningún otro thread podrá tener acceso, y luego de hacer los cambios hará release, para liberarlo y que otros threads puedan seguir trabajando sin restricción esa parte del código. Haremos una versión que utilice estas posibilidades se llamara thread-count-mutex.py
Python garantiza que solo un thread por vez puede ejecutar acquire() de esta forma cuando varios threads llegan al mismo punto, el primero ejecutara acquire() y los demás quedaran esperando que ese haga release(), y ahí otro podrá ejecutar acquire() y así uno a uno, lo que esta entre el acquire() y el release(), es el código que necesito proteger porque podría tener problemas de acceso simultaneo, de esta forma nunca dos threads a la vez podrán estar ejecutando esa misma parte del código, que en este caso es un print. En esta versión si uno descomenta el time.sleep(1) lo que ocurre es que los threads quedaran
esperando el tiempo que pase ese temporizador para que ese thread termine y haga release(), de esta forma la impresión se enlentecerá y cada vez que se imprima deberá pasar el tiempo que esta especificado en dicho temporizador para que otro thread pueda volver a imprimir.
Vemos que ahora no hay sobre impresiones y todos los threads trabajan simultáneamente y van imprimiendo a medida que pueden entrar a ejecutar el acquire() en orden. Vemos que todo se maneja a través de esta función que crea un objeto lock. mutex = thread.allocate_lock()
Los métodos que se le pueden aplicar al objeto lock son: acquire() -- lockea el lock release() -- unlockea el lock locked() -- testea si el lock esta en estado locked ESPERANDO QUE LOS THREADS TERMINEN Usaremos los locks para otro script que se llamara thread-count-wait1.py
Aquí vemos una variante un poco mas sofisticada, que vale la pena depurar, aunque por ahora vemos que se crea un objeto lock que aquí es stdoutmutex que al igual que en el script anterior, se le aplica acquire() para que solo un thread pueda imprimir a la vez.
Allí coloco un breakpoint y ar darle a DEBUG parara, al pasarlo con f6 veamos.
Allí vemos que stdoutmutex es un objeto lock, si lo tipeamos allí en el Debug Probe y a continuación el punto podemos ver los métodos para ese tipo de objeto.
Al pasar con f6 vemos que exitmutexes es una lista vacía
Si entramos en el for que se repetirá 10 veces , vemos que antes de crear los 10 threads con thread.start.new() en forma similar a como lo hacíamos en el script anterior, antes dentro de ese diccionario apendeará por cada thread creado un objeto lock, en cada campo, veamos en la primera pasada pongo otro breakpoint donde creara el primer thread para saltear el append() ya que con f6 se me queda colgado.
Ahora si el primer campo de la lista es un objeto lock ahora pongo un BP (cada vez que vean BP significa Breakpoint para abreviar), dentro de la función que ejecuta el thread, cuando para vemos que estamos en el primer thread ya que myId tomara los valores de 0 a 9, en este caso estamos en el thread 0.
Vemos que al igual que la vez anterior, imprimirá luego de ejecutar el acquire() y liberara para el uso por otros threads mediante release(), pero a su vez vemos que hay otro acquire() que se aplica al objeto lock que esta en la lista exitmutexes, como esta en la posición 0, pues es el primer objeto, haciendo exitmutexes[0], accederemos a el, y aplicaremos acquire() a dicho objeto con lo cual quedara locked. Quiere decir que se irán agregando objetos lock como campos de exitmutexes y pasaran al estado locked cuando terminen su trabajo y solo cuando todos estén lockeados, al programa principal se le permite terminar eso ocurre aquí.
Dentro del for hay un while que continuara hasta que el primer campo de exitmutexes este lockeado, cuando se de esa condición seguirá, o sea terminara el while y pasara al segundo ciclo del for con el segundo campo de exitmutexes, y así solo llegara al print y a salir del programa cuando verifique uno a uno que todos los campos de exitmutexes estén locked.
La comparación que hay en el while hará que mientras no este locked, continuara en pass, solo saldrá cuando este locked, y pasara al siguiente.
Allí apenas entro sera falso que esta lockeado, y como le aplica not a ese resultado, sera verdadero y ira al pass a continuar hasta que este lockeado y salga al siguiente campo en el for..
Pongo un BP en el print y quito todos los otros y cuando llega allí, puedo verificar el estado de los campos de exitmutexes.
Ahora si todos están true, o sea locked(), y el programa puede imprimir y terminar, lo cual cerrara todos los threads. En este caso los campos de exitmutexes se utilizan como flag para chequear si están locked, pero realmente eso querrá decir que terminaron de hacer su trabajo y ya puede salirse del programa. El ultimo script que miraremos es el que llamaremos thread-count-wait2.py
En este caso el funcionamiento es muy parecido al anterior solo que la variable exitmutexes es una lista de 10 campos que son todos ceros que se crea aquí. exitmutexes = [0] * 10
Allí vemos que tendrá todos ceros y a medida que cada thread va haciendo su trabajo, después de imprimir, asignara 1, al campo correspondiente a ese numero de thread, quiere decir que cuando todos los threads terminen su trabajo quedara. >>> exitmutexes [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] Luego el while final testara que si hay algún cero, ira al pass y solo saldrá cuando no haya ningún campo que sea cero, el resto es igual al anterior la impresión se manejara con acquire() y release() para evitar superposiciones, solo cambia la forma de manejar la salida del programa. Bueno esto es la primera parte del tema threads en la parte 23 continuaremos con la segunda parte. Hasta la parte 23 Ricardo Narvaja
23-HERRAMIENTAS DE TRABAJO PARALELO-THREADS-EL MODULO THREADING -MODULO QUEUE Python viene con dos módulos para manejar threads incluidos, uno es el mas sencillo llamado thread que hemos visto en la parte anterior, el segundo es el llamado threading, aunque muchas veces threading termina llamando a thread, para ciertos trabajos simples, veamoslo en acción en un ejemplo para poder ver como trabaja. Haremos un script en WING que se llamara thread-classes.py.
Si lo corremos vemos que la salida es similar a los que usaban el modulo thread anterior
N
No hay superposiciones al imprimir ya que de la misma forma utilizamos lock, creamos un objeto lock y usando acquire() y release(), mantenemos la impresión lockeada, de forma que solo un thread a la vez puede imprimir y hasta que ese no haga release(), ningún otro thread puede hacerlo.
Ahora como se crean y manejan los threads en este modulo?
Vemos que el modulo threading posee una clase llamada Thread donde si uno instancia allí, creara un thread que ejecutara el método run() pero este thread estará detenido, esperado que le demos arranque, lo cual se realiza desde el programa principal aplicando el método start() a la instancia. El tema es que como nosotros queremos manejar lo que hará el thread, no podemos usar el run() que trae dicha clase por defecto, así que creamos una subclase llamada en este caso mythread que heredara todos los atributos de la clase Thread, y por supuesto al crearle un nuevo método run(), usara este ultimo en vez del método run() de la clase original. Por lo tanto cuando se llame a start() para arrancar el thread, Python buscara si existe un método run() y como el nivel de mi clase es el mas bajo, ejecutara mi run() en vez del run() original que sera reemplazado. Veamos como funciona por partes. Después de definir la clase mythread que heredara de Thread los atributos viene a ejecutar aquí.
Allí vemos como crea el objeto lock
stdoutmutex = threading.Lock() En este modulo en vez de usar thread.allocate_lock(), se usa threading.Lock() ambas son similares, solo que esta ultima pertenece al modulo threading y la otra a thread, cumplen la misma función.
threads = [] for i in range(10): thread = mythread(i, 100) thread.start( ) threads.append(thread)
# make/start 10 threads # start run method in a thread
Luego vemos que creara una lista vacía en threads, y dentro del for que lo repetirá 10 veces, hará en cada loop una instancia en la clase mythread, a la cual como valores iniciales para el método constructor, le pasa el numero de thread que coincide con el numero de loop, y la constante 100.
Allí vemos como se pasan los valores iniciales para el método constructor, los cuales son guardados en las variables self.myId y self.count, y ademas luego se llama al método constructor de la clase Thread sin parámetros pues no lo necesita. Una vez creada la instancia se le aplica el método start() para arrancar el thread.
Y luego dentro de la lista threads se appendeará dicha instancia, para hacer una lista con las 10 instancias de threads que se crearan en este loop.
Al llamar al método start() que se le aplica a la instancia de mythread, Python buscara el método run() y lo ejecutara, por lo tanto ira allí, al run() que cree, y allí imprimirá, usando lock, para que varios threads a la vez no puedan hacerlo simultáneamente. La salida del programa utiliza el método join(), lo que hace es que solo continuará si el thread termino de ejecutarse, o sea que como threads es una lista con las 10 instancias, le aplicara a la primera join() y como no termino el thread, quedara allí, solo cuando este primer thread termine, seguirá y continuará el for, buscando la segunda instancia, a la cual le aplicara join() y así, hasta que no le aplique join() una a una y cada thread termine de ejecutar su código, no terminara el programa. Aquí el método join() provisto por la clase threading para sus instancias, realiza el trabajo que antes teníamos que hacer manualmente de chequear que todos los threads hayan terminado. El modulo threading posee herramientas mas avanzadas para controlar threads que el mas sencillo modulo thread (Semaphore, Condition, Event), no las veremos en este momento, pero se puede consultar el manual de Python, para su uso. De cualquier forma el uso de uno u otro modulo depende de la sencillez del caso, para la creación de threads fácilmente que no necesiten muchas vueltas, lo mejor es usar el modulo thread, ahora si queremos tener un control mas avanzado el modulo threading posee mas métodos, y es mas avanzado y complejo.
EL MODULO QUEUE Una de las formas de sincronizar datos entre thread es usar locks como hemos visto anteriormente, pero existe otra forma que el modulo queue nos proporciona, que es una zona compartida para guardar de forma que en ella lo que se guarda tiene un orden de salida, como una lista FIFO (first in-first out), o sea que el primero que entro a la lista, sera el primero que tendrá salida, es como una lista que se guarda agregando cartas o naipes la primera que agrego la pongo boca abajo, luego agrego otra arriba, y otra arriba, ahora cuando voy a sacar, siempre saco de abajo, o sea la primera que entro y esta abajo de todo, como solo saco de abajo, sera la primera que salga. El modulo queue implementa esta forma de guardar datos, pero ademas es controlado automáticamente mediante locks, o sea que internamente los accesos al queue, están protegidos con acquire() y release() de forma que dos threads a la vez no puedan acceder a la misma información, por eso los programas que usan queue, están seguros que que no habrá problemas de acceso de datos entre los threads y que no necesitaran hacer locks propios para el acceso de datos, pues queue manejara los accesos sin problemas haciendo los locks necesarios. El modulo queue puede usarse en ambos módulos thread y threading sin problemas, en el siguiente ejemplo veremos su uso llamaremos al script xd5 ueuetest.py.
Aquí vemos el funcionamiento, este script usa el modulo thread clásico que es importado luego de asignar valores constantes a numconsumers=2, numproducers=4 y nummessages=4.
Vemos que luego importa thread, Queue y time para un temporizador. Vemos que luego crea un objeto lock para cuidar la impresión simultanea. safeprint = thread.allocate_lock( ) Luego crea una instancia en Queue. dataQueue = Queue.Queue( ) Luego define las funciones producer() y consumer() que serán las que ejecutaran los threads que se creen aquí.
Vemos que el primer for creara dos threads que usaran la función consumer(), pasandole el parámetro del numero de thread creado en ese for, serán dos threads solamente pues numconsumers=2, en el otro for se crearan 4 threads que llamaran a la función producer() ya que numproducers=4 y también le pasaran el numero de thread de este for. Veamos ahora lo que hacen las funciones producer() y consumer() que ejecutan los threads.
Vemos que como nummessages=4, dentro de este for se crearan 4 mensajes por cada thread que use esta función y serán puestos en el Queue y guardados con el método put() que se le aplica a la instancia dataQueue. Quiere decir que producer() va guardando los mensajes 'producer 0:0', 'producer 0,1' y así pero recordemos que hay varios threads trabajando y creando y guardando mensajes a la vez, en esta función por lo tanto el queue no permitirá que se pisen mientras guardan datos, a la vez los otros threads que están trabajando simultáneamente en la función consumer()
Trataran de leer del Queue lo cual se realiza dentro de un try-except para evitar que se produzca error al leer del queue vacilo, una vez que leyó un mensaje, que sera el primero que ingreso, lo recuperara con el método get() y igual imprime dentro de un lock ya que el queue protege que los threads no tengan problemas al guardar y leer datos del mismo, ya que ese trabajo se realiza simultáneamente, pero al llegar al momento de imprimir podría ocurrir una superposición, lo cual hay que proteger mediante un lock.
Al correrlo vemos que no se producen problemas de acceso a los datos al guardar y leer del queue y no se produce problema de impresión gracias al lock. De cualquier manera vemos que el uso del queue hizo que no necesitáramos usar mas locks, ya que si no existiera el mismo, si los threads guardaran en variables globales tipo lista por ejemplo los mensajes a imprimir, habría a la vez threads accediendo a agregar mensajes y threads accediendo leer y quitar mensajes de la lista lo cual necesitaría locks de acceso cuando se lea o agregue mensajes en ella y con queue no es necesario. Bueno con esto terminamos el tema threads que sera aplicado cuando veamos en forma profunda el tema GUIs, ya que una GUI normalmente usa threads para hacer tareas que no impidan que el programa continúe funcionando, mientras dicha tarea se realiza. Bueno hasta la parte 24 Ricardo Narvaja
PARTE 24-SALIDA O FINALIZACION DE UN PROGRAMA-EXIT THREADS Vimos que en Python no es necesario como en C definir una función “main”, el código que el script encuentre desde el principio al final sera ejecutado, y cuando no exista mas código para ejecutar y llegue al final del archivo, el script terminara, sin necesidad de determinar una función para que haga ello.(a pesar de que puede hacerse usando sys.exit o os.exit) SYS.EXIT Existe una función que termina el script y que en ciertos casos es útil llamada sys.exit(). >>> sys.exit(N)
#
termina el script con status N
La función dispara la excepción SystemExit que se usa para terminar un programa, y como podemos capturar excepciones podríamos interceptar el llamado a sys.exit() de esta forma. C:\...\PP3E\System>python >>> import sys >>> try: ... sys.exit( ) ... except SystemExit: ... print 'ignoring exit' ... ignoring exit
# llamado a sys.exit() para terminar
Vemos que dentro de un try-except capturamos la excepción producida por sys.exit(), y imprimimos que se ignorara y continuamos el programa sin problema. De la misma forma si uno provoca una excepción SystemExit provocada con raise, sera equivalente a llamar a sys.exit(). Este pequeño script muestra el funcionamiento de sys.exit().
Es muy sencillo vemos que luego de definir la función later(), testea si esta corriendo como principal y si es así, llama a dicha función para que se ejecute. Al ejecutar la función, importa sys, y luego imprime 'Bye sys world' y llama a sys.exit() que terminara el script, luego de sys.exit() vemos que hay otra linea para imprimir que nunca se ejecutara, pues el script se cerro antes, el print 'Never reached'.
Allí lo corrí con DEBUG y veo que solo imprimió 'Bye sys world' y salio, como corresponde, lo mismo si lo ejecuto desde una consola de DOS.
Como vimos la llamada a sys.exit() genera una excepción quiere decir que si uno importa la función later() desde otro script, puede capturar la excepción y evitar la finalización y continuar como vimos antes.
Si hago otro script llamado importer.py en la misma carpeta y importa later(), al llamarla imprimirá su 'Bye sys world' y al ejecutar sys.exit() cerrara todo, sin posibilidad de que el script principal pueda imprimir 'importer calling' pues el script se cerro antes, mientras se ejecutaba la función later().
Si quiero evitar el cierre la única forma es desde el script principal capturar la excepción de salida y manejarla.
Aquí lo modifique la llamada a later(), ahora la hace dentro de un try-except que si captura la excepción SystemExit, impide que termine, manejando la excepción y no solo imprimiendo 'importer calling', si no continuando el programa que se burla de la función que quería cerrar todo jeje.
Si lo corro
OS.EXIT Existen varias formas mas de cerrar un script de python, la función os.exit() es casi similar a su compañera del modulo sys, salvo que a diferencia de la anterior es inmune a ser capturada por tryexcept ya que sale inmediatamente sin producir una excepción.
Aquí vemos este script llamado testexit_os.py que es similar al anterior testexit_sys.py solo que aquí importamos os, y llamamos a os.exit() en vez de a sys.exit(). Si lo ejecuto veo que funciona aparentemente igual que el otro imprime y se cierra sin llegar al segundo print.
Hagamos un importer2.py para que llame a la función outahere() dentro de un try-except a ver que pasa.
Ahora ejecutemos importer2.py
Vemos que esta vez el se rió de mi, jejeje, como os.exit() sale sin producir excepción, el try-except no capturo nada y salio directamente luego de imprimir 'Bye os world'. CODIGOS DE STATUS Vemos que ambos sys.exit() como os.exit(), tienen un argumento numérico que se denomina status, en el caso de sys.exit() es opcional, mientras que es obligatorio para os.exit(). El uso de este numero se el siguiente: si el script fue ejecutado como CHILD al recibir el numero de status, el programa principal sabe que realmente termino ese CHILD, y como el numero puede ser cualquiera, si tiene varios CHILDS, si cada uno sale con numero de status diferente, podrá determinar cual salio y ejecutar una acción al respecto, testeando ese numero. En Linux desde un terminal se puede chequear la variable status fácilmente.
Allí vemos el numero 42 que era el status de salida de testexit_sys.py y el 99 que era el status de salida de testexit_os.py, esto solo funciona en Linux y es una posibilidad del sistema y no de Python. Para hallar el código de status de un programa en python se puede abrir con os.system como ya vimos y asignar la salida a una variable que capturara el status..
Allí vemos que stat, capturara el status y sera el valor de retorno luego de ejecutar el programa.
Allí vemos diferentes formas de obtener el status, algunas utilizando os.system() y otra os.popen() que al cerrar con close() obtiene en la variable stat también el numero de status de salida del programa que corrió. EXIT THREADS Aquí vemos un ejemplo de la función para cerrar threads, aunque también como en el caso de los programas cuando un thread no tiene mas código que ejecutar se cerrara, igual existe la función para cerrarlos llamada thread.exit(). El script se llamara testexit_thread.py
Aquí lo vemos es un script que tiene dos funciones una parent() y una child(), la función parent() va creando threads cada vez que uno apreta la tecla diferente de 'q', gracias al modulo thread que ha sido importado usando start_new_thread().
Cada uno de esos threads creados ejecuta la función child(), que convierte en global la variable exitstat que inicialmente esta a cero, y se va incrementando en uno cada vez que un thread entra a ejecutar child(). Luego mediante la función get_ident() obtenemos el ID de cada thread, y imprimimos el mensaje 'HELLO FROM CHILD' con el ID y el numero exitstat a continuación, luego se llama a la función thread.exit() que cerrara el thread, así que nunca se llegara al print 'never reached'
Vemos que funciona, va creando de a un thread y cada uno realiza su impresión con su ID y numero de orden, pero se cierran antes de ejecutar el print 'never reached' gracias al thread.exit(), que esta justo antes. Bueno terminamos con la parte de salida de programas y threads. Hasta la parte 25 Ricardo Narvaja
PARTE 25-COMUNICACION ENTRE PROCESOS-PIPES ANONIMOS Y CON NOMBRE Hemos visto en partes anteriores algunas formas de intercomunicación entre procesos, por ejemplo los puntos de la siguiente lista pueden ser interpretados como formas de comunicarse entre procesos: .ARGUMENTOS EN LINEA DE COMANDOS .REDIRECCIONES DE LOS STANDARD STREAMS .PIPES GENERADAS POR OS POPEN CALLS Y SUS DERIVADOS (POPEN2,3,4) .CODIGO DE STATUS DE SALIDA .SHELL ENVIRONMENT VARIABLES .SIMPLES ARCHIVOS y nos faltaría ver para terminar la lista .SOCKETS, SHARED MEMORY, SIGNALS, PIPES ANONIMOS Y CON NOMBRE Por ejemplo pasarle argumentos a un programa, o permitir escribir es su stream de entrada, nos permite comunicarnos con el programa enviándole datos, leer los stream de salida o el status code cuando se cierra, nos da un resultado del funcionamiento del mismo. Las environment variables, ya vimos que hay formas de ejecutar programas pasandoles esa info (os.exec, etc), ademas si es un programa hijo recibirá como herencia sea cual sea la forma en que se creo, las mismas environment variables que el programa padre, así que es otra forma de ingresarle datos, asimismo las pipes creadas por os.popen permiten comunicación mas dinámica lo mismo que el uso de archivos, para traspasar información ya que los datos pueden ser enviados de un programa a otro en cualquier momento y no solo al inicio y recibir la respuesta al cerrarse. Nos quedaría por lo pronto el ultimo punto de la lista que es ver sockets que es la forma de transferir datos entre programas que estén en una misma maquina o a través de maquinas conectadas en diferentes locaciones a través de una red que se vera mas adelante, en esta parte veremos pipes con nombre y en las siguientes veremos shared memory o memoria compartida entre procesos, y signals o señales, para comunicarse entre procesos. PIPES ANONIMOS Y CON NOMBRE PIPES es una forma de comunicación entre programas, en Python están disponibles en el modulo os, gracias a la función os.pipes. Realmente pipes son canales unidireccionales que funcionan como un buffer de memoria compartido, pero con una interface que asemeja a que se trabaja con simples archivos en cada uno de los extremos de la pipe. En el uso típico un programa escribe datos en un extremo de la pipe y otro lee esos datos en el otro extremos de la misma, cada programa maneja su extremo como si leyera o escribiera en un archivo usando comandos de lectura y escritura que se usarían en archivos. También si a un extremo se lo pone a leer datos y el otro extremos de la pipe, no los envía, el que debe recibir se quedara esperando los mismos, hasta que se los envíen, así que también es una forma de sincronizar procesos, ya que cuando se le envíen los datos de descongelara y continuara funcionando perfectamente.
Las pipes vienen en dos variantes, anonymous pipes o anónimas y named pipes o pipes con nombre. Normalmente las pipes anónimas se usan entre procesos forkeados o entre procesos padres e hijos, como una forma de sincronizar datos entre ellos, mientras que las pipas con nombre, son como archivos que están en una maquina, pueden ser usados entre procesos absolutamente diferentes y que no tengan relación entre si. Empecemos con un script de pipes anónimas, aunque como el script osa os.fork() para crear un proceso forked solo funcionara en Linux, pero es bueno aprenderlo, porque si alguien lo necesita implementar en Windows, podrá hacerlo mediante el paquete Cygwin que hablamos en partes anteriores, por eso lo importante aquí es entender la idea del funcionamiento, este script se llamara pipe1.py.
Aquí vemos el script, luego lo probare en Linux para que vean como funciona, pero vemos que se definen dos funciones child() y parent(), y que luego de la definiciones, el script comienza llamando a la función parent(). Dentro de dicha función lo primero sera crear una pipe anónima, con os.pipe(), con dos extremos de donde se puede escribir pipeout y el otro desde donde se podrá leer pipein. Luego crea el proceso CHILD con os.fork(), pero ya testea el valor de retorno que sera cero cuando eso se ejecute por primera vez en el proceso child, pero sera diferente de cero cuando ejecute por primera vez en el proceso padre. Por lo tanto se lanza el proceso child y el valor de retorno sera diferente de cero cuando se crea, eso hace que salte al else donde vemos que quedara leyendo como si pipein fuera un objeto archivo line = os.read(pipein, 32) Alli vemos que lee 32 bytes de pipein, pero si en el otro extremos de la pipe, no se suministran estos
datos, el proceso padre quedara esperando y congelado allí, hasta que el hijo escriba en el otro extremo de la pipe. El comportamiento del hijo cuando arranca es similar al del padre solo, allí se bifurcara ya que el retorno de os.fork() en el child sera cero, así que ira a ejecutar la función child() pasandole como parámetro pipeout.
Vemos que entrara en la función child() y luego de un tiempo determinado por el temporizador time.sleep(zzz), ya que zzz inicialmente vale cero, el temporizador no funcionara por ahora, luego escribe como si fuera un objeto archivo en pipeout el mensaje 'Spam 000'. Vemos que zzz se incrementara. Como % halla el resto cuando zzz valga cero zzz = (zzz+1) % 5 0= (0+1) % 5 =1 % 5 = 1 Ya que el resto de la división entera de 1 dividido 5 es uno y se incrementara de a 1 hasta que llegue a 4 ya que cuando zzz valga 4. zzz= (4+1) % 5= 0 ya que el resto es cero y volverá a contar desde cero por lo tanto zzz valdrá zzz= 0 y de ahi 1,2,3,4,0,1,2,3,4,0,1,2,3,4,0.... y asi Luego se repetirá el ciclo con el temporizador aumentado y el hijo escribiendo nuevamente 'SPAM ....' Del otro lado de la pipe, el padre esta esperando, recibirá lo enviado y imprimirá un mensaje en el cual mediante format string incluirá el PID del padre, line que leyó de la pipe, y el tiempo actual que lee de la variable time.time()
print 'Parent %d got "%s" at %s' % (os.getpid(), line, time.time( ))
Veamos si lo corro en Linux que ocurre. Parent 19481 got "Spam 000" at 1201603463.64 Parent 19481 got "Spam 001" at 1201603464.64 Parent 19481 got "Spam 002" at 1201603466.64 Parent 19481 got "Spam 003" at 1201603469.64 Parent 19481 got "Spam 004Spam 000" at 1201603473.64 Parent 19481 got "Spam 001" at 1201603474.64 Parent 19481 got "Spam 002" at 1201603476.65 Parent 19481 got "Spam 003" at 1201603479.64 Parent 19481 got "Spam 004Spam 000" at 1201603483.65 Parent 19481 got "Spam 001" at 1201603484.65 Parent 19481 got "Spam 002" at 1201603486.65 Parent 19481 got "Spam 003" at 1201603489.65 Vemos que una vez que el padre leyó lo que esperaba que le envíe el hijo, lo va imprimiendo en el formato esperado, vemos que el programa queda funcionando siempre ya que no tiene una condición que lo haga terminar, salvo que lo paremos o apretemos CTRL +C en el terminal donde corrió, quedara siempre trabajando. Si miramos el resultado con cuidado veremos que el temporizador que hace al hijo esperar entre enviar un mensaje y el siguiente y va incrementándose con el valor de zzz o sea valdrá 0,1,2,3,4,0,1,2,3,4 y así, ahora vemos que cuando zzz vuelve a valer cero, como el temporizador no funciona ocurre el solapamiento de dos mensajes que entran muy seguido, y el padre los imprime prácticamente uno encima del otro, allí se ve en la parte resaltada del resultado, como el padre lee 32 bytes, cada vez que lee no puede diferenciar lo que va entrando y lee de 32 como venga, una forma mas elegante de separar es mandar caracteres de final de linea \n y leer mediante readline como vemos en esta variante del anterior que llamamos pipe2.py.
Vemos que es similar al anterior, los únicos cambios que tiene, son que el hijo agrega \n para terminar cada linea que escribe. os.write(pipeout, 'Spam %03d\n' % zzz) Allí vemos que de esta forma cada mensaje que escriba en la pipe sera una linea, y del otro lado el padre leerá con readline, linea a linea como si lo hiciera de un archivo. line = pipein.readline( )[:-1] y a cada linea le quitara el \n final con el indice slice [:-1]
Y otra diferencia que es que cada lado cierra las partes que no usa de la pipe lo cual es un uso correcto ya que el padre no usara pipein y el hijo no usara pipeout.
Veamos si funciona en Linux Parent 6902 got "Spam 000" at 1201682115.22 Parent 6902 got "Spam 001" at 1201682116.22 Parent 6902 got "Spam 002" at 1201682118.22 Parent 6902 got "Spam 003" at 1201682121.22 Parent 6902 got "Spam 004" at 1201682125.22 Parent 6902 got "Spam 000" at 1201682125.28 Parent 6902 got "Spam 001" at 1201682126.22 Parent 6902 got "Spam 002" at 1201682128.22 Parent 6902 got "Spam 003" at 1201682131.22 Parent 6902 got "Spam 004" at 1201682135.22 Parent 6902 got "Spam 000" at 1201682135.29 Parent 6902 got "Spam 001" at 1201682136.22 Parent 6902 got "Spam 002" at 1201682138.22 Parent 6902 got "Spam 003" at 1201682141.22 Parent 6902 got "Spam 004" at 1201682145.27 Parent 6902 got "Spam 000" at 1201682145.28 Parent 6902 got "Spam 001" at 1201682146.27 Parent 6902 got "Spam 002" at 1201682148.27 Parent 6902 got "Spam 003" at 1201682151.27 Parent 6902 got "Spam 004" at 1201682155.27 Parent 6902 got "Spam 000" at 1201682155.34 Parent 6902 got "Spam 001" at 1201682156.27 Parent 6902 got "Spam 002" at 1201682158.27 Parent 6902 got "Spam 003" at 1201682161.27 Parent 6902 got "Spam 004" at 1201682165.27 Parent 6902 got "Spam 000" at 1201682165.34 Parent 6902 got "Spam 001" at 1201682166.88 Parent 6902 got "Spam 002" at 1201682168.67 Parent 6902 got "Spam 003" at 1201682171.67 Parent 6902 got "Spam 004" at 1201682175.67 Parent 6902 got "Spam 000" at 1201682175.74 Parent 6902 got "Spam 001" at 1201682176.68 Parent 6902 got "Spam 002" at 1201682178.67 Parent 6902 got "Spam 003" at 1201682181.67 Parent 6902 got "Spam 004" at 1201682185.67 Parent 6902 got "Spam 000" at 1201682185.76 Parent 6902 got "Spam 001" at 1201682186.67
Parent 6902 got "Spam 002" at 1201682188.67 Parent 6902 got "Spam 003" at 1201682191.67 Parent 6902 got "Spam 004" at 1201682195.67 Parent 6902 got "Spam 000" at 1201682195.71 Parent 6902 got "Spam 001" at 1201682196.67 Parent 6902 got "Spam 002" at 1201682198.68 Parent 6902 got "Spam 003" at 1201682201.67 Realmente el script funciono y no tiene superposiciones, ya que se escribe y se lee linea a linea en vez de cantidades de bytes. COMUNICACION EN DOS DIRECCIONES CON PIPES Una pipe es una tubería unidireccional donde un proceso lee y escucha en un extremo y el otro escribe, en el otro extremo, ahora como se hace cuando necesitamos comunicación bidireccional entre dos procesos, por ejemplo que uno envíe información y el otro lea, pero a su vez este ultimo envíe una respuesta que el primero necesita para continuar. Ya que una sola pipe es una tubería unidireccional, la única forma que podemos realizar comunicación bidireccional con pipes es usando dos, una para cada sentido de comunicación, lo veremos mas adelante a medida que avance el curso. NAMED PIPES o PIPES CON NOMBRE En este caso el siguiente script funciona en Linux y para funcionar en Windows hay que agregarle el paquete Cygwin pues os.mkfifo solo esta portada para Windows en dicho paquete, pero nos da una idea del funcionamiento de las pipes con nombre entre dos procesos diferentes. Este script es muy similar al pipe2, solo que aquí los dos procesos no se crean con fork(), así que no tienen relación entre si, son procesos diferentes que al ejecutar el script sin argumentos se crea el que ejecuta la función parent(), y al ejecutarlo con algún argumento esto es detectado y este otro proceso ejecutara la función child(), aunque podrían ser dos scripts diferentes con diferentes nombres donde uno tuviera la función parent() solo, y el otro la función child() solamente, y seria el mismo caso
Aquí vemos que al ejecutar el script con algún argumento llamará a la función child(), mientras que si se ejecuta sin argumento llamará a la función parent()
O sea que hay que ejecutarlo dos veces para crear dos procesos diferentes una con argumentos y otra sin argumentos, seria lo mismo que crear dos scripts llamados por ejemplo test1.py y test2.py donde al correr uno ejecute parent() y no tenga mas nada que dicha función, y el otro tenga solo child(). Bueno, ya que la pipe tiene un nombre, verifica si no existe ese nombre y si es así lo crea, cuando lo ejecute por segunda vez, este segundo proceso vera que la pipe con ese nombre ya esta creada y no la creara, salteara esa parte. fifoname = '/tmp/pipefifo'
Una vez creada la pipa con su nombre cualquier proceso que la llame podrá utilizarla.
El primer proceso que usa la función parent() la abre con open tal como si fuera un archivo real, con el parámetro r ya que solo leerá de ella y el segundo proceso que usa la función child() la abrirá para escribir, en este caso uso os.open() para abrirla y escribir en ella.
Probemos a ver si funciona en Linux allí en el terminal tipeo el nombre sin argumentos para que arranque el primer proceso, y lógicamente no muestra ninguna salida ya que queda esperando que otro proceso le escriba en la pipe.
Ahora creemos un segundo proceso en otra terminal, a este le pasamos un argumento para que use child(), al arrancar, inmediatamente el primero se puso a imprimir en su terminal lo que le iba mandando el segundo a través de la pipe.
Allí esta una vista de cada terminal vemos como solo al ejecutar con un argumento en el terminal de arriba el script, comienza a mandar información por la pipe, que el proceso de abajo estaba esperando y la imprime. Yo se que la mayoría de los que siguen el curso usan Windows y por ahí no les parece muy importante esta información, pero dado que existe en Windows la posibilidad de forkear y crear pipas con nombre, aunque sea mediante un paquete adicional como es el Cygwin, es importante conocer que esto se puede realizar, no entraremos en detalles de como instalar y usar ese paquete, pero el que lo necesite, lo ira a buscar e instalara según las instrucciones, al menos ya sabe como se utilizan fork, mkfifo etc, porque las hemos visto en estos ejemplos anteriores, y ya lo tendrá mas claro, por lo demás en cada versión de Python se van agregando y portando mas utilidades que existen en un sistema, para los otros para hacer cada vez mas universal el funcionamiento, por lo tanto no seria raro que en versiones posteriores de python, aparecieran os.fork y os.mkfifo incluidas realizando los mismos trabajos que en Linux. Hasta la parte 26 Ricardo Narvaja
PARTE 26-COMUNICACION ENTRE PROCESOS- SEÑALES o SIGNALS-OTRAS FORMAS DE CORRER PROGRAMAS MAS GENERALES Las señales para hacer una analogía sencilla de entender, son como si a un proceso se lo prepara para ejecutar una función determinada, que se disparará cuando se active una señal desde algún otro proceso, las señales están identificadas por número y son eventos controlados por el sistema operativo. Para poder utilizar las señales en un script, Python nos provee del modulo signal que nos permite registrar funciones de Python como manejadores para eventos de señal. El modulo signal funciona en Linux y Windows (en este ultimo solo hay que tener cuidado de que define una cantidad menor de tipos de señales y es mucho mas limitado) Como mejor que hablar es mostrar un ejemplo y explicarlo, aquí veremos un script que usa el modulo signal llamado signal1.py,.que instala una función manejadora para el numero de señal pasado como argumento en linea de comandos.
Aquí hay solo dos llamadas del modulo signal esas son: signal.signal() Toma un numero de señal y instala la función manejadora para cuando dicho numero sea invocado. signal.pause() Hace dormir al proceso hasta que un llamado a la señal es captado En Windows este script no correrá pues el tipo de numero que le podes pasar es limitado y tampoco existe signal.pause() , pero al menos veamos como es la idea, les muestro como funciona en Linux para que lo vean, es muy sencillo.
Allí llamo al script signal1.py con el argumento 12, que sera el numero de señal y el símbolo & que en los sistemas Linux significa que esta corriendo en background o sea por detrás, para ver si esta corriendo, podemos usar el comando ps que muestra la lista de procesos en Linux. ricnar456@ricnar456-desktop:~$ ps PID TTY TIME CMD 13262 pts/1 00:00:00 bash 13282 pts/1 00:00:00 python 13330 pts/1 00:00:00 ps Allí vemos que hay un proceso en python corriendo con el PID 13282 en mi caso. Ahora para disparar la función hay que enviarle el numero de señal, para ellos se utiliza el comando de shell kill, que en Linux sirve para eso, se le pasa el numero de señal y el PID del proceso y llamara a la función, veamos.
Allí vemos que cada comando kill que le envío con el numero de señal correcto en este caso 12 y el PID del proceso, dispara la función onSignal() que en dicho proceso nunca había sido llamada, solo estaba preparada para manejar el evento 12 de señal, al llamarlo varias veces vemos que dicha
función imprime 'Got signal 12 at' y pone la fecha y hora en que fue utilizada mediante una función interna llamada now() que devuelve fecha y hora. Esto se puede hacer en Windows en forma mucho mas limitada, pero es bueno conocer posibilidades que posee Python, aunque en este caso sean mas utilizables en Linux que en Windows. Vemos también que si al mismo proceso le envío un kill con un numero de señal diferente : ricnar456@ricnar456-desktop:~$ kill -11 13282 ricnar456@ricnar456-desktop:~$ Como no hay función asignada para ese numero, no hay respuesta. Generalmente hablando, las señales deben usar con mucha cautela, porque no todos los sistemas responden bien a ser interrumpido por señales y solo el thread principal puede instalar señales y responder a las mismas en un programa multithread. De cualquier manera vemos que es un sistema de comunicación entre procesos, y por eso su inclusión en dichas partes que tratan ese tema. OTRAS FORMAS DE CORRER PROGRAMAS MAS GENERALES Hemos visto que desde un script de Python hay varias formas de arrancar un programa, por ejemplo fork - exec, os.popen, os.system. Estas son muy útiles pero cada una tiene su punto débil, ya vimos que os.fork() no funciona en Windows, os.popen normalmente puede quedar bloqueado ya que queda pendiente de que se lea la entrada o salida redirigida y mientras esperara, ademas tanto os.popen() como os.system() corren comandos de shell, pero estos son propios de cada sistema por lo cual no son formas generales para arrancar programas desde un script que puedan portarse de un sistema a otro. Existen otras formas modernas para arrancar programas que veremos a continuación, entre ellas están variantes de os.spawn, os.startfile, subprocess etc OS.SPAWN La familia de llamadas os.spawn ejecuta un programa y creara un proceso tanto en Windows como en Linux, siendo la portabilidad su punto mas importante, aunque su sintaxis es un poco mas compleja que los anteriores, eso es porque nos provee mas control sobre el programa que lanzamos. Aquí hay varios de ejemplos que ya hemos visto de como abrir programas, con los comandos ya conocidos:
>>> print open('makewords.py').read( )
print 'spam' print 'eggs' print 'ham' >>> os.system('python makewords.py') spam eggs ham 0 >>> result = os.popen('python makewords.py').read( ) >>> print result spam eggs ham Vemos que en el primer ejemplo usando open() y read() leemos el contenido de un archivo en el segundo ejecutamos dicho archivo con os.system() y en el tercer caso usando os.popen() también lo ejecutamos redireccionando la stream de salida a una variable llamada result que luego imprimimos. Ejecutarlo con os.spawn() sera así. >>> os.spawnv(os.P_WAIT, r'C:\Python24\python', ('python', 'makewords.py')) spam eggs ham 0 >>> os.spawnl(os.P_NOWAIT, r'C:\Python24\python', 'python', 'makewords.py') 1820 >>> spam eggs ham Los argumentos de os.spawn y variantes son: os.P_NOWAIT El primer argumento puede ser este y significa que el proceso se crea y no se espera, apenas el nuevo proceso es creado se devuelve el control al script que lo creo, devolviendo el PID del proceso como valor de retorno (Funciona en Windows y Linux) Si en cambio el primer argumento es: os.P_WAIT En este caso el script que lo creo esperara hasta que el programa que se arranca, termine y devuelva su código de salida para recuperar el control y continuar (Funciona en Windows y Linux).
os.P_DETACH and os.P_OVERLAY Si el primer argumento es detach, el proceso creado sera desatacheado de la consola del proceso que lo crea y overlay significa que el programa sera reemplazado por el nuevo. (Funciona en Windows) Luego vemos que el segundo argumento sera el path donde buscara el archivo o un path con ejecutable y el tercero según la variante de os.spawn sera una tupla con lo que se tipearia, en la linea de comandos como el nombre del archivo, argumentos, en otras variantes va todo como argumentos separados entre comas, aquí vemos las diferentes variantes de os.spawn.
os.spawnl(mode, path, ...) os.spawnle(mode, path, ..., env) os.spawnlp(mode, file, ...) os.spawnlpe(mode, file, ..., env)
# Unix only # Unix only
os.spawnv(mode, path, args) os.spawnve(mode, path, args, env) os.spawnvp(mode, file, args) os.spawnvpe(mode, file, args, env)
# Unix only # Unix on
Vemos que son variantes parecidas a las de os.exec existe la posibilidad de pasarle environment variables, como ultimo argumento. Aquí vemos una uso de os.fork() de Linux y el os.spawn() que funciona en ambos para ellos hacemos un script llamado spawnv.py y un script para arrancar que solo imprima un texto llamado prueba.py.
Al correrlo vemos que el script spawnv.py fue creando cada proceso y al terminar de crear los 10 termino y luego los procesos esos siguieron corriendo y imprimieron CREADO aun después que el proceso spawn ya se había cerrado, recordamos el primer parámetro os.P_NOWAIT, por eso no espera y continua, si allí se hubiera usado os.P_WAIT seria diferente probemos.
Al ejecutarlo.
Vemos que en este caso queda esperando hasta que cada script arrancado termine, para continuar y seguir creando el segundo, el valor que devuelve es el status code si cambiamos el script de prueba a esto.
En este caso como al usar el parámetro os.P_WAIT, el valor que devuelve es el código de status de salida 43.
Y también vemos como el proceso principal solo termina cuando hayan terminado todos los proceso creados ya que queda esperando que termine cada uno para crear el siguiente. Aquí vemos la parte del script a la cual salta si el sistema no es Windows Vemos que lo primero que hace es un fork() o sea una copia de si mismo, así que habrá varios procesos similares padre e hijo corriendo. Al detectar si el pid es diferente de cero, lo que hace es que si es el proceso padre como en el el PID que retorna al crear un proceso es diferente de cero, imprimirá 'PROCESS XXX SPAWNED' donde reemplaza mediante format string y coloca el PID del proceso que creo, y en el caso que el proceso que llega ahí sea el hijo, en ese caso PID sera cero, por lo cual allí saltara al else donde mediante un os.execlp() arranca el script de prueba prueba.py.
Así se crearan 10 procesos hijos y cada uno arrancara el script prueba.py. CORRIENDO PROGRAMAS EN WINDOWS Vimos como os.spawn() nos permite arrancar programas tanto en Windows como en Linux, ahora existe un comando de consola para arrancar programas solo en Windows, que es muy cómodo que se llama start.
Vemos que si lo pruebo en una consola es un comando de la misma ya que arranca el script en otra consola donde puedo ver como spawnv.py va creando los nuevos procesos. Por lo tanto este comando puede usarse dentro de os.system() ya que este ejecuta comandos de consola. Un script como el siguiente: import os, sys for i in range(5): os.system('start prueba.py ' + str(i)) print 'Main process exiting.' Funcionaria perfectamente y arrancaría 5 veces el script prueba.py. Inclusive si lo arranco con dicho comando desde una shell de WING, arranca en una consola fuera de WING ..
Realmente el comando start, simula que lo que se tipea a continuación es tipeado en la caja de dialogo RUN o EJECUTAR que tiene el MENU INICIO de WINDOWS, pero en el path que estamos trabajando en ese momento
Si lo hiciera directamente en el menú ejecutar tendría que tipear el path completo para que lo hallara y corriera, pero el efecto es el mismo. C:\temp>start c:\stuff\website\public_html\index.html Vemos que si tipeo start y a continuación un archivo html, con su path si existe lo ejecutara abriendo el programa navegador que este asociado al mismo, lo mismo que si ejecutamos archivos mp3, abrirá el reproductor de mp3 asociado y por supuesto si ejecuto un archivo .py abrirá python y lo correrá, siempre abrirá el programa asociado al tipo de archivo que corro. Vemos en estos ejemplos que aprovechándonos del uso de start podemos ejecutar los archivos de diferentes extensiones desde un script de python y siempre se abrirá el programa asociado y usara dicho archivo.
También en ambas formas de correr un script de python usando start o sin usarlo existe una diferencia. Vemos que en la primera forma usando python xxxx.py arranca el script y espera mientras que
usando start xxxxx.py arranca el script y continua funcionando desentendiéndose del mismo. UNA MAS PARA WINDOWS STARTFILE En los últimos releases de Python se ha agregado un comando startfile que realiza el mismo trabajo que usar el comando start dentro de os.system() como vimos en el punto anterior.
Ambos son similares startfile, arrancara el programa asociado al readme.txt y en el se mostrara su contenido de la misma forma que si usáramos os.system ( start xxxxx) OTRAS OPCIONES PARA ARRANCAR PROGRAMAS EN LAS ULTIMAS VERSIONES DE PYTHON El modulo subprocess permite realizar la mayoría de los trabajos de crear nuevos procesos, permite conectar a sus input , outputs y streams de error y obtener los códigos de retorno. Este modulo es una versión mas moderna que permite reemplazar os.system, os.spawn*, os.popen, para mas información sobre el mismo se puede consultar el manual de python de las mas recientes versiones, igual es sencillo y como lo utilizaremos en las partes siguientes su uso se aclarara fácilmente con los ejemplos al usarlo. Hasta la parte 27 Ricardo Narvaja
PARTE 27-EJEMPLOS DE UTILIDADES EN PYTHON En esta y las partes siguientes veremos ejemplos de utilidades en Python por supuesto no son todas las que existen hay millones por ahí, solo buscar en google se vera que la cantidad de posibilidades y código usable en Python es muchísimo, en estas partes pondremos algunos ejemplos de código útil y que podemos entender y que en caso de ser necesario nos servirá para realizar algunas tareas. Es de mencionar que en estas herramientas explicaremos lo básico de su funcionamiento o la parte importante del mismo, cada uno puede sentirse libre de experimentar debuggear, tracear, para sacarse dudas si encuentra que hay partes que no le quedan claras, sabemos que el aprendizaje es que te expliquen, pero también experimentar y hay que sentirse libre de hacerlo en los ejemplos que iremos viendo. El primer ejemplo de programa útil que existe, es el divisor o splitter y mergeador de archivos, en Internet hay algunos que se pueden conseguir, pero todos tienen un punto débil, no hay ninguno o es difícil conseguir uno que funcione en todos los sistemas, quiere decir que si yo tengo una maquina donde dividí archivos en Windows, y los paso divididos a una maquina en Linux no podre unirlos allí, y lo mismo al revés, en Linux tenemos incluidos comandos que nos permiten dividir y unir archivos como son split y cat, pero si divido con split un archivo en Linux no podre unirlo en otra maquina en Windows, así que el desafío es utilizar un divididor-unidor que corra en Python y se pueda portar a cualquier sistema, ya que Python nos permitirá trabajar en diferentes sistemas con los archivos y unirlos y dividirlos perfectamente. Esto que parece tan trivial muchas veces nos pone un problema por ejemplo tengo un pendrive de 512 megas y un archivo de 2 gigas que hoy día no es tan raro y quiero pasarlo de una maquina que tiene Windows a una que tiene Linux y no están conectadas entre si, la solución seria subir 2 gigas a algún lugar lo cual es difícil de conseguir un lugar que acepte archivos tan grandes, y luego bajarlos en la otra maquina con Linux, eso es muy molesto, mas teniendo un pendrive, donde podría dividir el archivo en 5 partes por ejemplo y copiar de a una en el y pasarlo a la maquina de Linux uno a uno, y luego allí podría unirlo, pero que herramienta unirá en Linux archivos divididos en Windows, no se por ahí existe, pero una buena opción sera el unidor-divididor en Python que sera multiplataforma (funcionara en Mac también). El script para dividir se llama split.py, luego a continuación mostraremos el script join.py que sirve para unir las partes.
#!/usr/bin/python ########################################################################## # split a file into a set of parts; join.py puts them back together; # this is a customizable version of the standard Unix split command-line # utility; because it is written in Python, it also works on Windows and # can be easily modified; because it exports a function, its logic can # also be imported and reused in other applications; ########################################################################## import sys, kilobytes = megabytes = chunksize =
os 1024 kilobytes * 1000 int(1.4 * megabytes)
def split(fromfile, todir, chunksize=chunksize): if not os.path.exists(todir): os.mkdir(todir) else: for fname in os.listdir(todir): os.remove(os.path.join(todir, fname)) partnum = 0 input = open(fromfile, 'rb') while 1: chunk = input.read(chunksize) if not chunk: break partnum = partnum+1 filename = os.path.join(todir, ('part%04d' fileobj = open(filename, 'wb') fileobj.write(chunk) fileobj.close() input.close( ) assert partnum ', demos[name]( ) Veremos otro ejemplo donde le damos a a elegir un color y luego el que el usuario elige lo utilizamos como color de fondo en un widget, veamos se llamara setcolor.py
Vemos que aquí hay una instancia en Tk() o sea que el parent es explicito y a dicha instancia se le crea un botón que tendrá una función callback que se llama setBgColor(), dicha función llama al dialog askcolor y usando solo de los valores de retorno el valor hexadecimal que se almacenara en hexstr, imprimirá ese valor y luego lo agregara como configuración usando el método config para cambiar el color de fondo como hemos visto. push.config(bg=hexstr) Allí a la instancia del botón le aplica el método config pasandole como argumento bg=hexstr o el color que elijió el usuario, ejecutemoslo para ver como va.
Cada vez que apretamos el botón nos aparece el dialogo para elegir color y al elegir uno y aceptar cambiara de color el fondo del mismo y imprimirá los colores que vayamos eligiendo.
El siguiente ejemplo se llamara dlg-custom.py y mostrara algunas customizaciones en diálogos.
Vemos que si la cantidad de argumentos es mayor que 1 la variable makemodal sera True y False si es menor que uno. En el caso normal en el cual no se le pase argumentos vemos que hay una ventana raiz que esta instanciada explicitamente en Tk() y root es una dicha instancia, a la misma se le agrega un botón y se corre con mainloop()
Vemos que la función callback del botón se llama dialog y esta definida aquí.
Vemos que crea una instancia en TopLevel que sera una ventana independiente que se le crea un Label y un Botón y si makemodal es falso o sea si no hay argumentos, el comando de ese botón win.destroy quitara esta ventana sola, dejando la ventana raiz viva y imprimiendo 'dialog exit', veamoslo en funcionamiento sin argumentos.
Cada vez que abrimos y cerramos la ventana TopLevel después de cerrarse imprime 'dialog exit'.
Cambien como la ventana TopLevel es independiente y no bloquea la ventana principal puedo apretar nuevamente el botón popup las veces que quiera y se seguirán creando nuevas ventanas TopLevel o sea que estas ventanas se crean en modo NONMODAL. Ahora veamos cuando le pasamos algún argumento mas que es lo que ocurre.
Vemos que la diferencia es que ahora la ventana TopLevel no deja usar la ventana principal hasta que no aceptes el OK, creándose en modo MODAL, mientras que antes eran completamente independientes. Vemos que para hacer que la ventana TopLevel sea MODAL se le aplico estas funciones
Allí están las tres la primera toma el foco, la segunda función deshabilita las otras ventanas del proceso, y la tercera las hace esperar hasta que se destruya para volver a poder usarlas. Bueno con esto es suficiente por hoy nos vemos en la parte 37 Hasta la próxima Ricardo Narvaja
PARTE 37 -USANDO BIND MAS PROFUNDAMENTE En la parte 30 de la segunda etapa mencionamos una de las posibilidades del método bind() que era capturar clicks del botón izquierdo del mouse o del derecho. En este tute veíamos este ejemplo:
Bueno entre la diferentes posibilidades que nos da bind() para reemplazar a command, veíamos que también llama a una función callback según determinados eventos, en ese caso, el clickeo de diferentes botones o la captura del doble clickeo. Ahora veremos mas posibilidades para bind() en el siguiente ejemplo llamado bind.py
Allí lo coloque en dos partes analicemoslo, vemos que hay varias definiciones de funciones callback para diferentes eventos que se pueden capturar con bind(), pero vayamos por partes y veamos como es la ventana principal.
Bueno vemos que realiza una instancia en Tk() explicita para crear la ventana principal, la variable tkroot sera una instancia de dicha clase. Luego crea una tupla para definir el tipo de fuente, en este caso ('courier', 20, 'bold') serán los campos de dicha tupla, que servirán para definir la fuente a usar. Luego creamos un Label instanciando en dicha clase y pasandole el parent donde queremos que se encuentre en este caso tkroot. El label tiene su texto 'Hello bind world' y mediante config se pasa el color de fondo bg y la tupla que define la fuente labelfont. widget = Label(tkroot, text='Hello bind world') widget.config(bg='red', font=labelfont) # red background, large font Luego de eso también con config se configura el tamaño inicial y se packea widget.config(height=5, width=20) widget.pack(expand=YES, fill=BOTH)
# initial size: lines,chars
También apreciamos que a esa instancia en Label se le aplica el método bind(), quiere decir que este sirve para agregarle callbacks, no solo a los botones, si no a cualquier tipo de widget mientras se lo apliquemos a su instancia.
Vemos que a widget que es una instancia en la clase Label y no es un botón, igual se le puede aplicar bind() ya que capturara los clics que se realicen en la misma, para diferentes eventos, llamará a 8 funciones callback diferentes, que tienen el nombre de acuerdo a como se generan.
Por ejemplo: widget.bind('', onLeftClick)
# mouse button clicks
Cuando se apriete el botón 1 o izquierdo se llamara a la función callback onLeftClick, luego al apretar el boton 3 se llamara a onRightclick, al apretar el botón del medio o 2 se llamara a onMiddleclick, para cuando se haga doble click se llamara a onDoubleLeftClick, para cuando se haga click en el botón izquierdo y sin soltar se arrastre se llamara a onLeftDrag, para cuando se aprete una tecla se llamara a onKeyPress, si se apreta la flecha para arriba se llamara a onArrowKey y si apretamos return se llamará a onReturnKey.
Vemos que las funciones callback en estos casos que son llamadas por bind(), deben estar configuradas para recibir un parámetro, que sera una instancia de la clase Event de Tkinter que aunque no se usa explicitamente, es usada internamente al poner en marcha el mainloop de eventos. Vemos que dentro de cada función callback y aprovechando métodos de la clase Event, se imprimen datos del evento que genero dicha llamada al callback veamos como corre y luego detallaremos mas.
Allí esta la ventana principal, veamos que ocurre si clickeamos con el botón izquierdo en dicha zona.
Vemos que al apretar con el botón izquierdo se llama a onLeftClick que es la función callback de ese evento, que imprime 'Got left mouse button click' y luego llama a una función definida showPostEvent() que imprime el nombre de la instancia y las coordenadas donde clickeamos mediante los atributos de Event, event.widget, event.x y event.y
Vemos que todas las funciones callback referidas a clickeo del mouse luego de imprimir que capturaron el clickeo llaman a la función showPostEvent() para detallar esos datos de la instancia y coordenadas de posición donde clickeamos.
Allí están todas las funciones callback correspondientes a clickeos del mouse, vemos que en la función que captura el doble click del botón izquierdo luego de llamar a showPostEvent() como las otras, llama a tkroot.quit() para cerrar el programa y la otra diferente es la que se dispara al apretar el botón del medio del mouse esa luego de llamar a showPostEvent() llama a otra función llamada showAllEvent(), veamosla.
Vemos que esta imprime todos los atributos ya que hace dir (event) y imprime cada atributo y su contenido que obtiene mediante getattr(), veamos probemos. Aquí vemos la salida Got left mouse button click: Widget=.11279056 X=258 Y=128 Got right mouse button click: Widget=.11279056 X=258 Y=128 Got middle mouse button click: Widget=.11279056 X=258 Y=127 __doc__ => Container for the properties of an event. Instances of this type are generated if one of the following events occurs: KeyPress, KeyRelease - for keyboard events ButtonPress, ButtonRelease, Motion, Enter, Leave, MouseWheel - for mouse events Visibility, Unmap, Map, Expose, FocusIn, FocusOut, Circulate, Colormap, Gravity, Reparent, Property, Destroy, Activate, Deactivate - for window events. If a callback function for one of these events is registered
using bind, bind_all, bind_class, or tag_bind, the callback is called with an Event as first argument. It will have the following attributes (in braces are the event types for which the attribute is valid): serial - serial number of event num - mouse button pressed (ButtonPress, ButtonRelease) focus - whether the window has the focus (Enter, Leave) height - height of the exposed window (Configure, Expose) width - width of the exposed window (Configure, Expose) keycode - keycode of the pressed key (KeyPress, KeyRelease) state - state of the event as a number (ButtonPress, ButtonRelease, Enter, KeyPress, KeyRelease, Leave, Motion) state - state as a string (Visibility) time - when the event occurred x - x-position of the mouse y - y-position of the mouse x_root - x-position of the mouse on the screen (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion) y_root - y-position of the mouse on the screen (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion) char - pressed character (KeyPress, KeyRelease) send_event - see X/Windows documentation keysym - keysym of the the event as a string (KeyPress, KeyRelease) keysym_num - keysym of the event as a number (KeyPress, KeyRelease) type - type of the event as a number widget - widget in which the event occurred delta - delta of wheel movement (MouseWheel) __module__ => Tkinter char => ?? delta => 0 height => ?? keycode => ?? keysym => ?? keysym_num => ?? num => 2 send_event => False serial => 89 state => 8 time => 6729035 type => 4 widget => .11279056 width => ?? x => 258 x_root => 460 y => 127 y_root => 501 Vemos que imprime event que dijimos que era una instancia de la clase Event de Tkinter
Si debuggeamos un poco y ponemos un breakpoint allí, cuando para podemos imprimir event como hace el programa.
También vemos que dir(event) nos da la lista de atributos y que si los imprimo, tendré toda esa información, solo al imprimir __doc__
Nos sale la info que muestra el programa, luego mediante getattr() obtiene el contenido de las variables de dicha instancia y las imprime. __module__ => Tkinter char => ?? delta => 0 height => ?? keycode => ?? keysym => ?? keysym_num => ?? num => 2 send_event => False serial => 95
state => 8 time => 6913701 type => 4 widget => .11279056 width => ?? x => 275 x_root => 477 y => 91 y_root => 465 Así que de esta forma se obtienen todos los atributos de la instancia event los que tengan valor se mostraran como el caso de x e y, y los que no se hayan inicializado mostraran ??. Vemos que coincide en la salida del programa vemos que cuando apretamos el botón del medio fue a showPostEvent() que imprimió las posiciones x e y que estaban en los atributos event.x y event.y
Luego de allí fue a showAllEvent() y por supuesto al imprimir la info de todos los atributos del mismo evento coincide.
Nos queda probar si con doble click realmente se cierra veamos.
Y si, se cierra al hacer doble click, cuando clickeamos sin soltar el botón izquierdo y arrastramos
Dicho evento es capturado y llamada la función callback correspondiente, y queda ver los eventos de teclas.
Vemos que captura la tecla que apretamos y la imprime mediante el atributo event.char, y tiene los callbacks especiales para tecla return y la flecha superior.
Hay mas eventos que pueden ser capturados por bind() que los que hemos visto en el ejemplo, estos son algunos: se dispara al soltar un botón se dispara apenas apretamos un botón se dispara cuando el puntero del mouse es movido y se dispara apenas el mouse entra o sale del área del widget, especial para resaltar áreas. se dispara cuando la ventana es reposicionada o cambia de tamaño se dispara cuando el widget es destruido y se dispara cuando el widget tiene o pierde foco y cuando una ventana es abierta y iconizada. , , y capturan cuando se presionan dichas teclas , , y capturan las otras teclas de flecha Bueno con esto terminamos el tema bind y eventos hasta la parte 38 Ricardo Narvaja
PARTE 38 – MESSAGE Y ENTRY En la parte 33 hicimos una tablita que mostraba las diferentes clases de widgets que hay, si la vemos nuevamente.
Vemos que solo hemos profundizado en esos 4 tipos de widgets y nos falta estudiar y ver como se configuran todos los restantes. MESSAGE En este momento le toca el turno al widget MESSAGE, que no hay confundir con las ventanas de dialogo que como vimos se importan de tkMessagebox, dichas ventanas son útiles pero son ventanas prehechas, cada una con una funcionalidad muy especifica, por ejemplo la ventana de dialogo showerror que vimos, se utiliza en caso de que haya que mostrar un error, lo mismo askyesno, para que oprima uno de los dos botones.
Pero estos diálogos son muy específicos y poco configurables, están prearmadas diferentes variedades para las opciones mas comunes, pero realmente saliendo de ello, son muy primitivos y poco configurables. (por ejemplo no podemos cambiarle el color a un mensaje de dialogo, o cambiarle la fuente, ya viene prearmado en forma estándar) Los widgets Message en cambio nos permite realizar mensajes, configurados a nuestro gusto y customizados. Aquí vemos un simple ejemplo de un widget de mensaje llamado message.py
Vemos que a través del método config le podemos cambiar el color de fondo, la fuente y puede tener texto de varias lineas. ENTRY El widget Entry es una simple entrada de texto de una linea, se usa tipicamente para ingresar texto que el usuario debe tipear, veamos un ejemplo en el script llamado entry1.py.
Lo primero que vemos es que usa el modulo quitter, que habíamos creado en partes anteriores por lo tanto deberá estar en la misma carpeta que este, o poder localizarlo de la carpeta de módulos de python, recordamos que dicho botón cuando lo apretábamos mostraba un mensaje de confirmación de si realmente queremos cerrar el programa o no.
Bueno miremos un poco el código
Vemos que el botón FETCH, tiene una función definida llamada de la misma forma fetch(). Vemos que hay una instancia en la clase Entry, y a dicha instancia se le aplican ciertos atributos de la clase, por ejemplo si hacemos click en insert y miramos en la pestaña source assistant de WING.
Vemos que es un método que insertara un texto para mostrar dentro del Entry, en este caso para avisarle al usuario que tipee palabras allí. Vemos que dicha instancia se packea con las opciones side=TOP y fill=X para que este arriba y se extienda en forma horizontal al estirar-
Vemos que en sentido vertical no se extiende.
Bueno el método focus, le dará el foco al widget Entry para que el cursor titile sin tener que ir a clickear dentro, pero acá vemos otro detalle interesante, usando el método bind(), que vimos en la parte anterior que servia para cualquier widget, se captura la pulsación de la tecla ENTER y se llama a la función callback fetch(), lo tenemos que hacer mediante una función lambda porque los eventos callback de bind() necesitan recibir el argumento event, como vimos, en la parte anterior, así que creamos una función lambda que recibe ese argumento event y llama directamente a fetch().
Por lo tanto, vemos que tenemos dos formas de llamar a la función fetch(), una mediante apretar el botón FETCH y la otra apretando la tecla ENTER.
Dentro de dicha función fetch(), capturaremos lo que esta tipeado en la ventana mediante el método get() que se le aplica a la instancia en Entry y luego se imprime ese texto mediante un format string, probemos a ver si es cierto.
Allí tipeo pepepe y apreto ENTER y lo imprime, y si en vez de apretar ENTER, clickeo sobre el botón fetch()
Vemos que funciona perfectamente, jeje.
Veo que si comento la linea que pone el foco en el widget Entry, al arrancarlo, no esta el cursor titilando que me invita a tipear, aunque si voy y clickeo en el espacio a tipear, recuperara foco y empezara a titilar, creo que es mejor que arranque con el cursor titilando lo vuelvo a descomentar.
Si no tenemos ganas de usar funciones lambda, de esta forma se puede realizar el mismo trabajo, creamos una función llamada pasaje(), que recibe el argumento event que siempre necesitan las
funciones que manejan eventos del método bind() y luego dicha función llama a fetch(), funcionando de la misma forma que en el ejemplo anterior. CONOCIENDO LOS WIDGETS ENTRY ent.insert(0, 'some text') value = ent.get( )
# set value # fetch value (a string)
Vimos en el ejemplo anterior los dos métodos que se le aplican a instancias en la clase Entry que sirven para mostrar texto como el caso de insert, cuyo primer argumento es la posición a partir de la cual se mostrara el texto, al poner cero, sera pegado al margen izquierdo, y el segundo argumento la string que mostrara. Luego el método ent sirve para capturar el texto en formato de string, aquí lo asignamos a una variable value. ent.delete(0, END) ent.insert(0, 'some text')
# first, delete from start to end # then set value
Aquí vemos que si queremos borrar el texto que muestra el Entry, podemos utilizar el método delete que borrara desde el primer argumento hasta el segundo argumento (END es un valor preasignado que significa el final del widget), y luego puedo volver a insertar nuevo texto, si hubiera vuelvo a insertar sin haber borrado, esta segunda vez se insertaría desde donde quedo el cursor titilando, como si se apendeara el texto nuevo al anterior existente. def fetch( ): print 'Input => "%s"' % ent.get( ) # get text ent.insert(END, 'x') # to clear: ent.delete('0', END) ent.insert(0, 'x') En el ejemplo anterior podemos cambiar la función fetch() y jugar con los insert y deletes para por ejemplo agregar una 'x' al texto, borrarlo si usamos delete o lo que se nos ocurra.
Un ejemplo mas complejo es el siguiente lo llamaremos entry2.py y combina Entrys y Labels.
Bueno primero corramoslo, luego lo explicaremos.
Vemos que en funcionamiento es muy similar al anterior tiene el botón FETCH y también apretando
ENTER se imprimen los contenidos de las cajas de texto, las cuales en este caso inicialmente están vacías, y también importa quitter para agregar el botón que confirma al apretarlo si queremos salir realmente del programa, veamos el código.
Vemos que se inicia verificando si corre como main y si es así, realiza una instancia en la clase Tk() , esta instancia root sera la ventana raíz. Luego llama a una función definida makeform() y le pasa como argumentos dicha instancia root y una variable fields que es una tupla definida al inicio aquí
Veamos que hace la función makeform()
Lo primero crea una lista vacía en entries y luego dentro de un for, vemos que realiza una instancia en Frame llamada row, ya que sabemos que Frame es mas flexible, por lo tanto cada uno de estos Frames creados dentro de este for, estará atacheado a la ventana principal o raiz. A este row, se le agrega un Label instanciando en dicha clase y un Entry sin texto.
Vemos que el texto del Label, sera el nombre del campo de la lista field que valga en cada ciclo del for, o sea la primera vez valdrá 'Name'.
Sabemos que las instancias de Frame hay que packearlas, aquí lo hacemos colocandola arriba o sea side=TOP y fill=X para que se extienda en forma horizontal cuando se estire la ventana. Luego se packean el Label a la izquierda y el Entry a la derecha, también con los parámetros expand=YES y fill=X para que si se estira ocupe el máximo que se estire Frame en forma horizontal. Vemos que el for creara los tres Frames, cada uno tendrá un Label y un Entry a su derecha, y al terminar va apendeando a la variable entries cada instancia en Entry y al volver tendremos en entries la lista con las tres instancias de cada Entry.
. Luego vemos que cualquier return en la instancia root sera capturado por el método bind() y mediante la función lambda recibirá el argumento event y llamara a la función callback fetch() pasandole como argumento ents, o sea la lista de instancias en Entry. Luego crea el botón FETCH en root, dicho botón tendrá a fetch() como función callback y le pasara ents como argumento y luego agrega el otro botón Quitter. Así que hasta ahora resumiendo, tenemos en la ventana raíz o root que es instancia de Tk(), los dos botones FETCH y QUIT y luego tres instancias de Frame cada una con un label y entry, tanto si apretamos el botón FETCH como si apretamos la tecla ENTER se llamara a la función fetch(), veamos que hace la misma.
Vemos que la lista con las tres instancias se asigna a entries y en un for a cada una de estas instancias se le aplica el método get(), para obtener el texto que tiene cada una y se imprime. Vemos que fue necesario pasarle a la función callback una lista con las tres instancias, pues las necesita para poder aplicarle el método get() y obtener el texto tipeado en cada una. Bueno en la parte 39 seguiremos con mas Entry. Hasta la 39 Ricardo Narvaja
39-MAS SOBRE ENTRY -VARIABLES TKINTER Recordamos de partes anteriores la definición de MODAL, que era una ventana independiente que no retornaba el control al programa principal, hasta que no se cerraba, no permitiendo mientras usar las otras ventanas del programa, veamos un ejemplo de modal con entry se llamara entry2modal.py.
Acá vemos el código, lo primero que nos damos cuenta es que importa del script anterior entry2, por lo tanto debe estar en la misma carpeta o poder acceder al mismo de la carpeta de módulos. Bueno de entry2 importa la función makeform() que creaba los entries/labels, la función fetch(), y fields veamos para acordarnos como era entry2.py.
Allí vemos lo que importa de entry2, por supuesto lo que esta en el if__name__ == '__main__', no se ejecutara porque al ser importado este modulo no correrá como main, solo importara las funciones mencionadas y la tupla fields. Bueno veamos que hace el nuevo script con esto:
Allí comenzara haciendo una instancia en Tk(), llamada root para la ventana raíz, y a la misma le colocara un botón con el texto 'Dialog' y cuya función callback sera ask(). O sea que al clickear en el botón se llama a la función ask() la cual dentro crea una variable global llamada popup, y realiza con ella una instancia en la clase TopLevel, para hacer una ventana separada, tipo popup justamente como vimos que eran los widgets TopLevel.
Luego vemos que se llama a la función makeform()que importó, pasandole como parent, esta ventana popup, o sea que en este caso en vez de estar colocado en la ventana raíz como en el ejemplo anterior lo que cree mainform() estará en esa ventana TopLevel, recordemos en el ejemplo anterior entry2, que se llamaba a mainform() usando root como parent.
Y ahora
Ahora usamos popup como parent, por eso no se crearan los entries/labels en la misma ventana raiz, si no en una ventana aparte.
Luego vemos que en este caso no se usara bind() para capturar la pulsación de la tecla ENTER sino solamente al clickear en el botón OK, se llamara a la función callback de dicho botón llamada show(), a la cual se le pasan las instancias de entry llamadas ents, y que imprimirá el contenido de lo que tipeamos.
Allí show() llama a la función fetch(), la cual mediante gets(), leía el contenido tipeado en cada entry, y luego lo imprimía mediante un format string como en el ejemplo anterior y luego esta ventana se destruye mediante popup.destroy() que cierra la ventana MODAL y deja solo la principal la cual retoma el control. Como comentario vemos que para colocar la nueva ventana en modo modal, recordamos de la parte 36, la forma de hacer que una ventana TopLevel sea MODAL. DE LA PARTE 36
------------------------------------------------------------------------------------------------------------------------
Volviendo al ejemplo actual, aplica los mismos métodos para convertir la nueva ventana en MODAL y que hasta que no se destruya no se pueda volver a usar ninguna otra ventana del programa.
Si lo probamos vemos que funciona tal cual lo esperado, al abrirlo nos aparece la ventana principal.
Al apretar en el botón Dialog se crea la nueva ventana Toplevel.
Dicha ventana esta en modo MODAL, si clickeo en la ventana principal, no pasa nada, así que tipeo en los entries.
En este caso la tecla ENTER no hace nada, ya que no se utilizo el método bind() para capturar su pulsación.
Al apretar en el botón OK.
Se imprime lo que se tipeo y luego se destruye la ventana popup, retomando el control la ventana principal, obviamente debe ser realizado en este orden, si destruimos la ventana y luego queremos usar gets() para leer lo tipeado, no sera posible, pues al destruirla se destruirán las instancias en los entries, y no se podrá hacer gets() de algo que no existe dando error. Para salvar ese problema o bien llamamos a fetch antes de destruir la ventana o usamos variables Tkinter que es el próximo tema a tratar. VARIABLES TKINTER Los widgets tipo Entry (y otros) soportan la noción de variable asociada, Dichas variables una vez asociadas, por ejemplo al texto que se muestra en pantalla, si cambiamos su valor, cambiara el texto mostrado inmediatamente y cambiando el texto también cambiara el contenido de la variable asociada a ello. Estas variables asociadas son instancias de clases en el modulo Tkinter, estas clases son StringVar, IntVar, DoubleVar, y BooleanVar, y uno elije la clase según el contexto de donde la usara. Por ejemplo una instancia de la clase StringVar puede ser asociada con el texto de un Entry como veremos en el ejemplo entry3.py donde se aclarara el concepto.
Como siempre mostramos el código y lo iremos explicando poco a poco
Analicemos el código, vemos que comienza, haciendo como antes una instancia en root, y llama a la función makeform() que en este caso no la importa si no que la define en el mismo script. En este caso le pasamos root como parent, por lo tanto, lo que cree dicha función estará incluido en la misma ventana raíz.
Vemos que crea un botón con el texto 'Fetch', que llama a su función callback llamada fetch() pasandole como argumento lo que retorna de la función makeform(). También esta el otro botón Quitter que importó y ademas captura mediante bind() si apretamos la tecla ENTER para también llamar a la función fetch(), vemos que por acá no hay nada nuevo solo nos queda analizar lo que hace la nueva función makeform().
Vemos que hace tres instancias en Frame llamadas form que es la principal y en ella instancia otros dos Frames llamados left y rite. Después crea una lista vacía llamada variables y dentro de un for que tomara los tres valores que hay en la tupla fields, para cada uno de ellos creara un label usando como parent left y un entry usando como parent rite.
Luego packeara ambos widgets creados con side= TOP .
Luego viene el punto nuevo
Allí instancia var en la clase StringVar, y mediante el método config aplicado a la instancia ent del Entry, configura `para que esta variable, se enlace con el texto de los mismos.
Y luego setea su valor para que el texto sea 'enter here' y apendea esta variable a la lista variables, quiere decir que por cada field, habrá un label y un entry y cada uno de estos tendrá una variable asociada al texto del entry dentro de variables, lo cual retornara al salir de la función.
Luego la función fetch() mediante get() obtendrá y luego imprimirá el contenido de estas variables, ya vimos que usando set(), podemos cambiar su contenido y cambiara el texto mostrado, como al revés si tipeamos algo se actualizara la variable a dicho valor, corramoslo para ver si es cierto.
Allí vemos lo que acabamos de explicar, al setear dichas variables, que están enlazadas con el texto que se muestra, cambiara el mismo, y viceversa, al tipear ahora nuevo texto y apretar fetch, o ENTER imprimirá dichas variables con su nuevo contenido.
Por lo tanto vemos como novedoso que en ves de guardar las instancias de los entries como en el ejemplo anterior y mediante get() hallar sus valores, en este caso usando ent = Entry(rite) var = StringVar( ) ent.config(textvariable=var)
# link field to var
Se instancia en Entry, luego se instancia en StringVar y se realiza el link o asociación entre ambas mediante la ultima instrucción config que asocia la instancia ent con la instancia var, quedando esta ultima como variable del texto de dicho entry. Por supuesto a dichas variables se le puede aplicar el método get() para leer su contenido y el método set() para modificarlo.
var.set('text here') value = var.get( ) La versión MODAL de entry3 se llamara entry3-modal.py
Vemos que importa el nuevo makeform() que devuelve variables, salvo eso es igual al entry2modal.py, ya vimos que ese llamaba a makeform() pasandole como parent una instancia de TopLevel, en vez de la raíz, de forma que se creara una ventana separada.
Con esos tres métodos se convierte en MODAL para que hasta que no se destruya la nueva ventana, la principal no se pueda utilizar, y por supuesto la función callback show()
Que en este caso, si puede destruir la ventana nueva y después obtener los valores de las variables, pues en ellas, persiste guardado el ultimo valor que quedo, así que no tenemos el problema de olvidarnos de recuperar los valores tipeados, y que se destruya la ventana con los entries, usando variables Tkinter. Hasta la parte 40 Ricardo Narvaja
40-CHECKBUTTON- RADIOBUTTON Y SCALE Recordamos los tipos de widgets que vimos hasta ahora:
Allí vemos en celeste los tipos de widgets que ya vimos, y seguimos avanzando ahora le toca el turno a los tres que siguen en la lista CHECKBUTTON,RADIOBUTTON y SCALE.
CHECKBUTTONS: Los checkbuttons son una especie de simple choice donde cada uno de los botones, tendrá asignado una variable Tkinter, como vimos en la parte anterior, si cambiamos el estado del botón clikeandolo, se cambiara el contenido de la variable, y si cambiamos el contenido de la variable se cambiara el estado del botón que se muestra en la ventana. Los checkbuttons cuando se usan en grupo, permiten que varios puedan estar seleccionados a la vez, siendo cada uno una opción individual que no afecta a los otros. Por lo demás los checkbuttons, al igual que los botones comunes, también tienen la posibilidad de llamar a una función callback de forma tal que cuando se clickee en ellos se llame a la misma, vemos un ejemplo sencillo de checkbuttons llamado demoCheck.py.
Analicemos el código paso a paso, lo primero que vemos, es que en este caso usará una clase que creará, lo cual a pesar de parecer mas complejo, nos permitirá como en el caso del botón Quitter, usarla en cualquier otro programa fácilmente solo importando y realizando instancias en dicha clase.
Allí vemos que importa Quitter y ademas importa de la parte 36 el modulo dialogTable.py por lo
cual debe estar en la misma carpeta o donde este accesible.
Recordamos que era un diccionario que tenia como key, ciertas palabras como 'Open','Color', etc y los valores asociados eran tipos de diálogos que existen en python. Bueno lo primero que hace entonces es verificar si es llamado como main, y si es así, realizara una instancia en la clase Demo y luego llamara a mainloop() para ponerla en marcha, veamos en que consiste la clase Demo.
Vemos que como Demo es una clase que hereda de Frame, debemos al llamar a la misma, satisfacer su método constructor, al cual como recordamos debemos pasarle el parent y lo argumentos que necesite, en este caso el parent es None, pues usara Tk() como parent en forma implicita, luego de realizar la llamada al método constructor de Frame, siempre se packea, luego llama al método tools() que es un método de esta clase, veamos que hace. .
Aquí se realiza una instancia en Frame llamada frm, y se packea, luego se agrega un Botón, el cual tendrá el texto 'State', y el otro botón sera el conocido Quitter. Al volver del método tools(), se crea un Label en el Frame principal (ya que no le le pasa la instancia frm como parent), hasta aquí es todo visto, ahora vendrá la parte donde le agrega los checkbuttons.
Primero crea una lista, llamada vars, por supuesto al llamarla self.vars podrá usarla dentro de la clase, en todos los métodos que existen, y luego realizara un for donde key tomara los valores de las keys del diccionario demos. Luego realiza una instancia para crear una variable Tkinter, en este caso, var se instanciará en la clase IntVar(), luego creamos el checkButton.
Vemos que una de las opciones de configuración del mismo se llama variable y hará que se enlace con la variable Tkinter que instanciamos, la cual mantendrá el resultado del estado del checkbutton.
Ademas tiene función callback como si fuera un botón tradicional, se configura mediante command
Por lo tanto el for creara un Checkbutton por cada key del diccionario y la función callback de cada uno sera demos[key], o sea su valor asociado, que era el llamado al dialogo que se debe mostrar.
Vemos que en cada ciclo del for, apendea estas variable Tkinter que están enlazadas con el estado del botón, a la lista self.vars. Como el otro botón, tiene como función callback el otro método de la clase llamado report().
Al apretarlo imprimirá el contenido de los checkbuttons el cual obtiene mediante get(), recordamos
que como son variables Tkinter enlazadas si usáramos set() se cambiaría el estado de los mismos. Veamos si funciona como interpretamos:
Allí vemos ubicado el Label con el texto 'Check demos' y a su derecha el botón State y Quit que se habían packeado en el Frame a la derecha, antes de colocar el Label que se ubico en el espacio restante. Luego están los checkbuttons, apretemos el primero a ver que ocurre.
Vemos que como si fuera un botón normal llama a su función callback, que en este caso era el valor asociado a la key 'Color' en el diccionario demos, o sea el dialogo askcolor.
Pero ademas de que se abre el dialogo el botón queda marcado.
Si apretamos el botón State nos debería imprimir el estado de los botones, ya que imprime el contenido de las variables Tkinter asociadas el estado de los mismos.
Por supuesto los valores asociados al estado del checkButton, son cero si está sin marcar o uno si esta marcado.
Al apretar los otros checkbuttons se disparan los otros tipos de diálogos, cada vez que alguno cambia de estado, cambian las variables Tkinter asociadas y al imprimir dicho valor, se habrá actualizado.
Si desde una consola python nos trasladamos al path del script y por ejemplo tipeamos python.
Desde allí importemos la clase Demo del modulo demoCheck.
Ahora que esta importada la clase podemos interactivamente crear una instancia en la misma.
Vemos que solo instanciando y sin siquiera llamar a mainloop, al estar en una consola de python, se genera la ventana, si clickeo en algún checkButton para cambiar su estado.
Puedo verificar el estado de los checkboxes directamente llamando al método report manualmente, sin apretar el botón State, como la instancia es d, usando d.report(), podremos llamar a dicho método y nos devuelve el estado de los checkbuttons.
Cuando la mayoría ve el ejemplo anterior se pregunta, para que tener variables asociadas si tenemos funciones callback donde podemos hacer lo que queremos al apretar los botones? Primero no todos los checkbuttons, se usan con funciones callback, muchas veces se usan para tomar decisiones y testear el resultado de lo que marco el usuario, y no necesariamente tienen que tener una función callback en si misma, por lo demás el uso de la variable, nos permite saber su estado y cambiarlo inmediatamente, pues en cualquier punto del programa esas variables se podrán consultar, y cambiar su estado, siendo mucho mas general que el uso del alguna variable que se podría setear dentro de la función callback y que podríamos hacer que cambie cada vez que se corre la función, de cero a uno y viceversa, creo que es mucho mas complicado, teniendo las herramientas para hacerlo en forma automatizada. Veremos a continuación la comparación entre dos ejemplos sencillos de checkButton, el primero seteando manualmente una variable que refleja el estado de los mismos y el segundo el mismo ejemplo usando variables Tkinter. El primero se llamará demo-check-manual.py
Vemos que se crea una lista llamada states que originalmente estará vacía, y dentro de un for se crearan 10 checkbuttons y cada uno tendrá la misma función callback llamada onPress a la cual se le pasa el numero i que ademas de ser el numero de ciclo dentro del for, identificara a cada checkButton, ya que se le pone como texto, a cada uno str(i) para que muestre el numero en forma de string. También dentro del for se van apendeando los estados iniciales de cada checkbutton, dentro de la lista states, y dichos estados se cambian, al clickear el cualquiera de ellos se llama a la función
callback y con states[i]=not states[i] se invierte el estado si era cero pasa a uno y si era uno pasa a cero. Al salir se imprime el ultimo estado de los checkbuttons. Veamoslo funcionar:
Ahora marquemos algunos checkboxes y cerremos para que imprima el estado.
Al cerrarse imprime
A pesar de que funciona es complicado de actualizar y manejar, veamos el mismo caso usando variables Tkinter se llama demo-check-auto.py
Alli vemos lo que realmente necesita para usar variables Tkinter, una instanciación en IntVar() y que dentro de la configuración del checkbutton enlacemos dicha instancia con variable=var. Luego sera muy sencillo, sin funciones callback ni nada, se apendean estas variables Tkinter dentro de la lista states y luego cuando se cierra el programa, se imprimen los valores que se obtienen con get() de cada una de estas variables.
Marco algunos checkbuttons y al salir se imprimen las variables Tkinter que reflejan el estado de los mismos.
RADIOBUTTONS Los radiobuttons, se diferencian de los checkbuttons que ademas de ser botones que pueden tener un estado 1 o 0 como los anteriores o sea estar marcados o no, son usados en grupo y solo uno puede estar seleccionado de todos, al seleccionar uno nuevo, se deselecciona el que estaba anteriormente, o sea que sirve para elegir solo una de una cantidad de opciones, al estilo múltiple choice. De la misma forma que los checkbuttons, los radiobuttons tienen en su configuración una variable para asociar con variables Tkinter y un command para ejecutar funciones callback, veamos el siguiente ejemplo llamado demoRadio.py.
Vemos que es muy parecido al ejemplo inicial de los checkbuttons, importa Quitter y de dialogTable importa demos, y crea una clase llamada Demo que hereda de Frame, y a la cual luego de packearla le agrega un Label con el texto 'Radio demos' y luego realiza la instancia de una variable Tkinter en StringVar.
Y por demás tiene una función callback definida aquí.
Vemos que en el método onPress() primero halla el valor de la variable Tkinter y lo imprime y también como la variable Tkinter en este caso es una string y nos devuelve el nombre del radiobutton que esta activado, como puede estar activo uno solo por vez, no hay problema, siempre sabremos cual es el único que esta activo en cada momento. Ese mismo nombre lo usamos para llamar a su valor asociado, demos[pick] abrirá el dialogo asociado al botón que acabamos de apretar, por ejemplo si clickeamos en el radiobutton Input, get() nos devolverá 'Input', así que demos['Input'] llamara al valor asociado a esa key, que es el dialogo que vemos en la imagen siguiente.
Ahora tipeamos un numero y aceptamos
Vemos que el mismo método onPress(), imprimió los resultados sin tener que apretar ningún otro botón, me muestra cual es el radiobutton activo, en este caso Input y llama a demos[Input], que muestra el dialogo para ingresar un numero y luego imprime el numero que tipeamos, todo dentro de la función callback del radiobutton Por lo demás vemos que a diferencia del checkButton que había que crear una variable Tkinter para cada botón, aquí se crea una variable Tkinter sola para todo el conjunto, ya que como solo uno de los botones puede estar encendido a la vez, dicha variable retorna el texto del mismo, y no se necesita mas, si se clickea en otro, la variable cambiara de nombre al nuevo botón clickeado, y si la seteamos con set() debemos pasarle el nombre del radiobutton que queremos que este activo y cambiara a este en la ventana.
El botón State en este caso, llama al método report() que obtiene con get() el nombre del Radiobutton que esta activo.
SCALE (SLIDERS) Los widgets Scales o Sliders sirven para elegir entre un rango de valores numéricos a través de los cuales se puede deslizar. De la misma forma que los checkbuttons y los radiobuttons, los widgets scale, tienen un command para configurar una función callback que se llamara al moverlo, y una variable para asociar el resultado a una variable Tkinter, la cual se podrá leer mediante get() y cambiar el estado del slider mediante set() Veamos un ejemplo que se llamara demoScale.py
Vemos que es muy parecido a los ejemplos anteriores aquí también importa demos de dialogTable, y también crea una clase Demo donde colocara un Label con el texto 'Scale demos'. Luego vemos que realiza la instancia de la variable Tkinter.
Vemos que tiene dos scales, ambos se pueden mover, pero como ambos están asociados a la misma variable Tkinter, cuando uno cambia la misma al moverse, esta cambia de valor y modifica el otro scale para que tenga la misma posición, pues comparten la misma variable, así que ambos siempre muestran lo mismo, pues al setear la variable cambiando uno de los dos, se cambia el valor del otro automáticamente. Vemos que ambos scale comparten el mismo método callback
O sea que al moverlos ambos imprimirán el valor en que quedaron, pues los scales, devuelven a su método callback, dicho valor que se lo pasa como argumento.
Ya habíamos visto que ambos comparten la misma variable Tkinter.
Ambos también comparten el rango a través del cual se pueden deslizar.
Sin embargo vemos que el segundo se orienta en forma horizontal mientras que el primero al no especificarse lo hará por default en forma vertical.
La opción tickinterval, me permite configurar el intervalo de los valores que se muestran, por default el primer slider no muestra intervalos, mientras que el segundo si, configurándolo en 2, se muestran los valores de 2 en 2 a pesar de que puede moverse de a uno.
La opción showvalue=YES hace que nos muestre el valor actual al lado del slider, por default es YES si lo cambiamos a NO en el de abajo no muestra el valor actual.
La opción resolution aunque aquí no se uso marca la cantidad de unidad que salta el slider al moverse, por default es 1, si ponemos el de abajo en resolution=2, solo podrá moverse de dos en dos, y aunque el de arriba lo pongamos en 1, el de abajo no podrá tomar ese valor.
Volvamoslo a como era el ejemplo original.
Vemos que al apretar el botón Run Demo.
Llama a su función callback onRun() que lee el valor de la variable Tkinter con get() y lo imprime y luego llama a la función demos.keys()[pos] que me devuelve la key correspondiente a esa posición y la guarda en pick, y luego con ella ya puede llamar al dialogo que es el valor asociado demos [pick] para ademas de mostrar el dialogo, imprimir el resultado del mismo.
El otro botón State, muestra cual es el estado actual de los sliders, imprimiendo la variable Tkinter.
El ultimo ejemplo sera un scale simple sin variables, sabemos que para todos estos ejemplos en que usamos variables Tkinter, se puede realizar el mismo trabajo sin ellas, en el caso de radiobuttons y checkbuttons las variables son muy útiles, en el ejemplo que vimos de scale, la variable Tkinter mantiene sincronizadas ambas scales, en el próximo caso no es necesario sincronizar nada, y se puede obtener el valor de la scale con get() directamente de la instancia, sin usar variables Tkinter.
Vemos que en este ejemplo el rango va desde -100 a 100
Se setea con from y to, luego tickinterval=50, hará que la escala mostrada vaya de 50 en 50 y resolution=10 hará que se pueda saltar el slider de 10 en 10.
Vemos que podemos moverlo de 10 en 10, y quedo allí en la posición 10, si apretamos el botón State llamara a la función report() que imprimirá el valor actual del scale que obtiene mediante get().
Vemos que para estos casos de scales simples, como no necesitamos sincronizar los scales, o actualizarlos mediante el programa, no es tan necesario usar variables Tkinter, aunque ya vimos que de cualquier manera definirlas son dos lineas, y no agrega mucho al programa y puede traer comodidad según el caso, mientras que para checkbuttons y radiobuttons, son mucho mas cómodas que trabajar sin ellas. Hasta la parte 41 Ricardo Narvaja
41-DIVIRTIENDONOS DESDE LA CONSOLA HACIENDO GUIs INTERACTIVAMENTE Antes de seguir adelante con mas widgets, quería mostrar que para los que estamos practicando, hay un método interactivo de construir y practicar con ventanas que es muy divertido y funciona tanto en Windows como Linux, o sea que cada widget que vamos agregando o modificandole cosas se agregara y podemos ir viendo que efecto causa cada instrucción que tipeamos, y por supuesto que efecto causara en un script. La base es que cuando tipeamos comandos Tkinter en una consola, no se necesitaba mainloop, porque las ventanas que aparecen usan el loop de la consola, por lo tanto podemos aprovecharnos de eso, para poder probar GUIs y agregarle botones, entries, etc, y ver en cada paso que efecto producen, lo cual nos hará perderle el miedo a Tkinter. Por ejemplo abro una consola de Windows o un terminal de Linux y tipeo python.
Luego importo Tkinter >>> from Tkinter import * Luego haré una instancia en Tk() para hacer una ventana principal root=Tk()
Veo que interactivamente al realizar la instancia root=Tk() me aparece la ventana principal, sin necesidad de esperar que este el mainloop() al final corriendo que obviamente si necesitaremos en un script, pero para divertirnos y probar cosas creo que sera muy útil. Lo primero que haremos sera agregar un Label, para practicar con el, si tengo problema en acordarme toda la configuración, pues de ultima hago una instancia mínima y luego le voy cambiando detalles con config, lo cual también es divertido porque vemos que efecto produce cada cosa. Instanciemos un Label. lbl=Label(text='PRACTICANDO')
Alli realizamos la instancia y aun no se ve porque debemos packearlo para ubicarlo y alli se vera. lbl.pack(side=TOP)
Bueno lo packee TOP a ver que pasaba y allí esta, lo estiro o lo achico no hay problema, podemos intentar con config ver si podemos cambiarle el color. lbl.config(bg='RED')
Jeje es divertido, podemos cambiarle la fuente. lbl.config(font=('times',30))
Esta bueno también saber que si algo no nos convence lo podemos cambiar, pongamos otro color a ver que tal. Si queremos elegir un color podemos llamar en la misma consola a askcolor, ya que lo que devuelva ya vimos que sera el valor hexadecimal del color. Importamos el modulo tkColorChooser y de el la función askcolor la cual llamamos y guardamos el valor que devolverá en una variable pepe, ahora elegimos el color.
Al aceptar veo que el color que elegí que esta en pepe es
Así que ahora puedo configurar el Label con dicho color. >>> lbl.config(bg='#b7c081')
Creo que mi color estuvo bueno todo esto puede servir para que en el momento de hacer el script ya sepas exactamente que colores, tamaños y lineas de código usar, pues a las que tipeaste acá le agregas el mainloop al final y correrá de la misma forma.. Ahora agregaremos un entry sencillo, en cualquier momento si no nos acordamos algo, podemos abrir otra ventana de comando importar Tkinter y allí usarla de help.
Uso otra ventana, porque por ahí al salir de la ayuda cierro sin querer la consola de python y no quiero que eso pase, así que puedo fijarme allí igual es muy sencillo instanciar un Entry, lo haremos en varios pasos así vamos viendo que efecto tiene cada uno, podría hacerse todo en una linea, pero mejor se aprende así. Allí instancio en Entry ent=Entry(root) Ahora para que se ubique debo packearlo, pues aun no se ve. >>> ent.pack(expand=YES,fill=X)
Bueno ya tenemos a la vista el entry, si no nos acordamos como se usaba tipeamos help en la otra ventana.
Aunque me gusto antes la opción de configuración justify
Veo en google que con ella se puede centrar el texto asi que la uso ent.config(justify=CENTER)
Veo que de esta forma el texto se va centrando. Podremos aun insertarle un texto, veamos, borro lo que tipee antes a mano, y tipeo ent.insert(0,'TIPEA LOCO')
Vemos que el texto que le colocamos, sigue la misma norma de justify, el 0 que es el valor inicial es a partir de donde puede tipear, y allí ubica el texto. Si lo estiro veo
Por lo tanto conviene que el color de fondo sea todo el mismo así que le cambio el color de fondo al root también le coloco el mismo. root.config(bg='#b7c081') Con eso configure el color de fondo de la ventana root que sea el mismo del Label asi queda mejor.
Hasta ahora lo que tipee fue esto desde el inicio si quisiera hacer un script copiandolo y poniéndole el mainloop al final funcionaria de la misma forma. >>> from Tkinter import * >>> root=Tk() >>> lbl=Label(text='PRACTICANDO') >>> lbl.pack(side=TOP) >>> lbl.config(bg='RED') >>> lbl.config(font=('times',30)) >>> lbl.config(bg='#b7c081') >>> ent=Entry(root) >>> ent.pack(expand=YES,fill=X) >>> ent.config(justify=CENTER) >>> ent.insert(0,'TIPEA LOCO') >>> root.config(bg='#b7c081') Ahora puedo cambiarle el borde con relief='sunken' y para que se vea hago el borde mas ancho. root.config(relief='sunken') root.config(borderwidth=25)
Como ya tenemos un botón quitter, podemos copiar ese modulo al path donde estamos trabajando e importarlo y agregarlo >>> from quitter import Quitter >>> qt=Quitter(root) Ahora podemos packearlo donde queremos >>>qt.pack(side=LEFT)
Ahora ya tenemos el botón de salir, así que podemos quitarle el borde de la barra de títulos que no nos interesa jeje. >>> root.overrideredirect(1) Probando con algo que encontré en google, sobre la instancia root, el método overrideredirect, con el valor 1, terminamos sin bordes jeje, que era lo que buscábamos, la ventana parece que desapareció pero si buscamos esta ahí abajo jeje. Si apretamos el botón.
Mejor cancelemos si no tendremos que hacer todo de nuevo jeje. Ahora tenemos que capturar lo que se tipea y hacer algo con ello, podemos colocar un botón que al apretarlo, tenga un método callback que lo muestre, veamos.
Veo que definí una función llamada func() que imprime ent.get() o sea el texto que se ve en el entry. >>> def func(): print ent.get() Luego cree el botón con su texto y su función callback llamada func. >>> btn=Button(text='Apreta aca', command=func) Y lo packee a la derecha >>> btn.pack(side=RIGHT) Vemos que al apretar el botón lo que esta tipeado en el entry se imprime, si lo cambiamos por otro texto.
Vemos que hasta aquí funciona, que pasaría si cada vez que tipeo un texto, ese mismo pasara a ser el texto del Label principal, jeje, que loco. def func1(): lbl.config(text=ent.get()) Definimos una función nueva que capture el texto del entry y que lo coloque mediante config como texto de la instancia del label principal lbl. Cambiamos la función callback del botón. btn.config(command=func1) Ahora al tipear un texto y apretar jeje
No van a decir que no es divertido la de guarradas que se pueden hacer desde la consola jajaa. Recopilando todo lo tipeado es >>> from Tkinter import * >>> root=Tk() >>> lbl=Label(text='PRACTICANDO') >>> lbl.pack(side=TOP) >>> lbl.config(bg='RED') >>> lbl.config(font=('times',30)) >>> lbl.config(bg='#b7c081') >>> ent=Entry(root) >>> ent.pack(expand=YES,fill=X) >>> ent.config(justify=CENTER) >>> ent.insert(0,'TIPEA LOCO') >>> root.config(bg='#b7c081') >>> root.config(relief='sunken') >>> root.config(borderwidth=25) >>> from quitter import Quitter >>> qt=Quitter(root) >>> qt.pack(side=LEFT) >>> root.overrideredirect(1) >>> def func(): print ent.get() >>> btn=Button(text='Apreta aca', command=func) >>> btn.pack(side=RIGHT) >>> def func1(): lbl.config(text=ent.get()) >>> btn.config(command=func1) Un comentario antes de terminar, a los que les gusta trabajar desde editores de python y no desde la consola, he probado las Shell Interactivas de los programas que usamos normalmente como editores ni el WING, ni el ACTIVE PYTHON, ni el SPE, se puede trabajar con Tkinter es forma interactiva como en la consola, pues hasta que no colocas el mainloop no se ve nada, en vez de poder irse agregando paso a paso cada widgets y verlo, como en este tute, la única excepción y lo comento, pues quizás a alguien le gusta trabajar y practicar en forma interactiva desde un editor con un buen autocompletado y ayuda, es el PyScripter mostrare como se hace ya que tiene una opción para trabajar de la misma forma interactiva en Tkinter que en la consola.
En el PyScripter en las opciones:
Allí vemos que hay varias opciones de engine, la que se usa normalmente es Internal o Remote, que son las tradicionales, pero luego hay dos mas Remote(Tk) que es la que se usa para trabajar con Tkinter y Remote(Wx) para hacer GUIs interactivamente con wxPython. Cuando cambiamos a esta opción vemos que en la ventana interactiva nos dice:
Y ahora vemos que si tipeamos lo mismo que en la consola.
Nos va apareciendo en forma interactiva la ventana que vamos creando, lo bueno que aquí tenemos autocompletado y ayuda, por ejemplo ahora que tenemos una instancia llamada root, si escribimos root y el punto saldrán los métodos que podemos aplicarle.
La ventanita de autocompletado sale sola cuando tipeamos el punto pero podemos hacer que aparezca en cualquier momento apretando CTRL mas SPACE.
Aquí vemos que tipeamos 'ro' y llamamos a la ventana y me muestra entre las opciones el comando round que podría tipear y root que es la instancia creada. También tiene ayuda con las funciones, al abrir un paréntesis, solo se dispara la ayuda si uno quiere llamarla en otro momento con SHIFT mas CTRL mas SPACE aparecerá cuando queramos.
Vemos que permite trabajar igual que en la consola en forma interactiva, pero con el plus de tener ayuda y autocompletado, lo cual es un gran plus, jeje. Bueno hasta la parte 42 Ricardo Narvaja
42-CORRIENDO CODIGO DE GUIs TRES FORMAS Vimos que en la partes anteriores, hemos creado scripts cuyo nombre comienza con la palabra demoXXXX.py que son widgets, y por lo tanto como tienen clases que son completamente funcionales, pueden ser llamadas y utilizadas desde otros scripts, como ocurría con nuestro famoso botón Quitter.
Allí en las pestañas vemos que hay unos cuantos de ellos y a continuación veremos tres formas de usarlos. ATACHEANDO En este ejemplo importaremos 'demoDlg.py', 'demoCheck.py', 'demoRadio.py', 'demoScale.py' que se han realizado en partes anteriores, así que conviene ponerlos en la misma carpeta del script actual o en un path accesible por el mismo, igual los volveré a adjuntar al tute actual, así no tienen que ir a buscarlos a partes viejas.
Allí tenemos el código completo, vemos que nos enfrentamos a un problema, que mientras íbamos escribiendo los módulos, por lo menos yo me di cuenta, y es que a las clases las llamo a todas con el mismo nombre Demo, por lo tanto no podemos importar todos los módulos con from xxxxx import Demo from yyyyy import Demo Pues cuando llamemos a Demo dará error al haber varias clases del mismo nombre, la única forma de diferenciarlas seria importarlas con import XXXXX y llamarlas con XXXXX.Demo de esta forma no habría problema, y es la forma en que se debería realizar en esos casos, pero aquí el autor empleo un truco, para poder manejar esto, importa usando strings con los nombres de los módulos de la siguiente forma. demo='demoDlg' module = _ _import_ _(demo) part = module.Demo(root) En la linea resaltada, importa usando una string con el nombre del modulo, usando __import__ que permite hacerlo y al resultado asignado que se guarda en la variable module se puede utilizar de la misma forma que si hubiéramos importado demoDlg Seria como si pudiéramos hacer import 'demoDlg' part = 'demoDlg'.Demo(root) De cualquier manera sabemos que este ultimo código no es legal en Python, pues no se puede aplicar import a una string, pero lo colocamos para darnos una idea de como trabaja el código que
usa. Define una lista, con strings con los nombres de los módulos que mas adelante importara usando el método que comentábamos antes, ademas crea una lista vacía llamada parts.
Luego comienza haciendo una instancia en Tk() llamada root, a la cual le coloca un Label con el texto 'Multiple Frame Demo', un Botón con el texto 'States', y un Botón Quitter, para confirmar la salida.
Luego llama a la función addComponents() pasandole como argumento la instancia root de la ventana principal creada hasta ahora, veamos que hace dicha función.
Tomara los nombres de los módulos que están como string en la lista demoModules, y los importara usando __import__ como vimos anteriormente, y al resultado que se guardo en module, se lo usa como si fuera el modulo importado, por lo tanto module.Demo(root) es similar a si hubiéramos importado el modulo con import demoDlg y luego realizáramos la instancia en la clase Demo de dicho modulo demoDlg.Demo(root)
El resultado es que realizaremos instancias en todos las clases Demo de todos los módulos dentro del for, pasandole como parent root, así que ubicara todo en la misma ventana y se apendearán las instancias en la lista parts.
Cambien vemos que la función callback de el botón State es dumpState() veamosla:
Toma dentro de un for cada instancia de Demo, y imprime __module__ , o sea el nombre del modulo de dicha instancia y a continuación le agrega los dos puntos y si tiene dicha clase tiene atributo report, lo cual obtiene con la función hasattr() lo llamará
Si existe report la llamará para que imprima el reporte de dicha clase, veamoslo en funcionamiento. Recordemos los módulos que le estamos agregando demoDlg.py
demoCheck.py
demoRadio.py
demoScale.py
Por supuesto al correr nuestro script juntara todos en uno solo ya que a todos los atachea a root.
Vemos debajo del Label 'Multiple Frame Demo' y el botón States general, los cuatro widgets atacheados, primero el demoDlg.py, luego el widget de los checkbuttons llamado demoCheck.py, luego el de los radiobuttons que se llamaba demoRadio.py y finalmente el de los scales que se llama demoScale.py. Vemos que todos funcionan en la forma que los programamos individualmente:
O
Al ingresar la cifra y aceptar, luego al apretar el botón State de ese widget, muestra el estado de los checkbuttons.
También los radiobuttons funcionan.
Al elegir un color.
Y los scales también funcionan.
Allí coloco los sliders en 2 por ejemplo y apreto el botón Run demo
Al aceptar sale también la info, al apretar el botón States general este debería llamar a los reportes de todas las instancias en las clases Demo para mostrar en que estado esta cada una, veamos.
Vemos que nos muestra que demoDlg retornó None, que demoCheck esta en estado 01100 que demoRadio esta seteado en Color y que los sliders de demoScale están en 2. Vemos que en esta primera forma de reusar los scripts que teníamos hechos y que eran widgets, todo esta en una sola ventana, y a veces esto puede ser útil en ventanas multiproposito, que realizan muchas tareas a la vez en la misma, veamos el siguiente caso de como usar los mismos widgets de otra forma.
VENTANAS INDEPENDIENTES Una forma de utilizar los widgets que tenemos, es hacer que sean todos independientes, lo cual como vimos se realiza instanciando en TopLevel el ejemplo se llama demoAll-win.py.
Vemos que aquí la ventana raíz no esta instanciada explicitamente, sabemos que no es necesario, cuando no se realiza, la misma se hará implícitamente en Tk(), y contiene un Label y un Botón.
Allí los vemos, no se pasa parent aquí, así que se agregan a la ventana raiz, por lo demás hay un for por alli.
Este directamente realiza una instancia en TopLevel en cada ciclo, usando strings como vimos en el ejemplo anterior.
Vemos por ejemplo que allí importara el primer modulo, por ejemplo demoDlg, usando la string que hay en la lista demoModules.
Luego crea una instancia en TopLevel() llamada window y a la misma la usa como parent, al instanciar en la clase module.demo en el primer ciclo. (que es lo mismo que demoDlg.demo en el primer ciclo del for)
Así que creara 4 instancias igual que en el ejemplo anterior, solo que en vez de pasarle root como parent que es una instancia de Tk(), se le pasa window que es una instancia de TopLevel, y eso hará la diferencia de que las ventanas sean todas independientes.
Luego vemos que el botón States de la ventana raíz, tiene como función callback, a allstates() que al igual que en el ejemplo anterior, imprimirá los reportes del atributo report si este existe, veamos como corre.
Por supuesto son todas ventanas independientes, si hacemos click en alguna de ellas, la misma tendrá foco, y funcionara, y el botón States general me dará le estado de los reportes de cada widget.
Allí vemos que nos da el reporte perfectamente, recordemos que en este caso como son ventanas TopLevel, son independientes, pero todas pertenecen al mismo proceso. CORRIENDO TODOS LOS WIDGETS COMO PROCESOS DIFERENTES La tercera forma que no veremos por ahora, es correr todos los scripts como programas separados, ya no como ventanas separadas en el mismo proceso como en el caso anterior, en el cual solo había un proceso con 5 ventanas independientes, en este último habrá 5 procesos diferentes uno por cada widget, por ahora dicho ejemplo no nos interesa demasiado, pero dejamos en claro la ultima de las posibilidades que menciona el libro, para reusar código. Hasta la parte 43 Ricardo Narvaja
43-IMAGENES EN TKINTER En Tkinter las imágenes se utilizan creando objetos tipo PhotoImage o BitmapImage, instanciando en dichas clases y luego estos objetos se pueden atachear a otros widgets mediante el atributo image. Botones, Labels, Canvas, text y menus pueden mostrar imágenes en esta forma, asociando este objeto imagen que ha sido creado, para ilustrar esto haremos el ejemplo llamado imgButton.py.
Creare una carpeta en C:\gifs o donde ustedes mas les guste, para almacenar las imágenes, el path de esta carpeta esta en el script así que debe coincidir, para que pueda hallar el gif.
Vemos que allí debemos cambiar el path a nuestra carpeta donde colocamos el gif, que esta adjunto al tute, luego el script es sencillo, hace una instancia en Tk() para crear una ventana raíz.
Luego crea un objeto imagen, instanciando en la clase PhotoImage.
Vemos que le podemos pasar file=xxxx el path y nombre del archivo para crear el objeto imagen.
Y luego instanciamos el Botón al cual le pasaremos el parent que es win, y la imagen que tendrá image=igm. Si lo corremos vemos que se crea un botón con una imagen, dicho botón podemos apretarlo, aunque en este caso no le colocamos función callback pero funcionaria perfectamente si la tuviera.
En el siguiente ejemplo llamado imgCanvas.py veremos como atacheamos la imagen a un Canvas, que aunque aun no lo hemos estudiado aun, veremos que para todos los tipos de widgets, el trabajo es el mismo, crear el objeto imagen y atachearlo mediante image al widget que queramos.
En este caso también instancia en PhotoImage pasandole el path y el nombre del archivo gif que mostrara.
Allí vemos que se crea una instancia en Canvas pasandole el parent que es win, luego se packea y con el método create_image() se agrega al mismo, ya veremos mas en profundidad cuando
toquemos el tema Canvas, pero queda como ejemplo, para ver que siempre se pueden utilizar los objetos imagen atacheandolos a los widgets existentes
DIVIRTIENDONOS CON IMAGENES El ejemplo se llamara buttonpics-func.py
Recordamos el modulo glob de Python que vimos en la parte 20 de la etapa 2.
Importa glob, ademas del script demoCheck.py correspondiente a los checkbuttons de la parte anterior y también importa random.
Vemos que después de instanciar en Tk() para crear la ventana raíz, agrega al mismo un Label y un Botón que tiene una función callback llamada draw(), y luego packea ambos. Luego realiza una instancia en demoCheck.Demo, lo cual agregara un widget del tipo demoCheck.
Glob nos retornaba una lista en este caso de todos los gif, que hay en la carpeta de gifs que configuramos al inicio.
Liego usando map, sabemos que aplicara la función de su primer argumento, a la lista de archivos gifs, devolviendonos en images, una lista con los valores que retorna la función aplicada a cada gif.
Vemos que en images tenemos la lista, cada campo de la misma tendrá una tupla con el nombre y path del archivo y el otro campo la instancia en la clase PhotoImage. >>> images [('C:\\gifs\\ora-lp.gif', ), ('C:\\gifs\\ora-pp.gif', )] >>> Esto ocurre porque la función que se le aplica a cada archivo, devuelve una tupla con el mismo nombre del archivo como primer campo y la instancia como segundo.
Luego imprime los nombres de los archivos que encontró y pone a correr el mainloop, lo único que queda ver es que hace la función callback draw().
Vemos que usa la función random.choice(), para elegir un par nombre, instancia y los asigna a name y photo respectivamente.
Vemos que name lo usa para configurar el texto del Label usando config, y la instancia para agregarle ese objeto imagen al botón, también usando config y image=xxxxx. Veamoslo correr.
Al apretar el boto 'Press me' se llamara a la función draw() que le colocara al azar una de las imágenes gifs que halle, y el nombre de la misma en el Label.
Si vuelvo a apretar varias veces el botón que ahora tiene la imagen, cambiara la misma por otra que este en dicha carpeta.
Por lo demás no necesitamos comprobar que el demoCheck y sus checkbuttons funcionan perfectamente jeje. Vemos que funciona perfectamente, el ultimo ejemplo se llama buttonpics.py y es similar al anterior solo que refactorizado utilizando clases para poder atachearlo mas adelante a otros widgets.
Vemos que en este caso crea una clase que hereda de Frame, a la cual luego de llamar al método constructor de Frame, y packear como es debido, realiza una instancia para crear el Label y el Botón, cuya función callback sera el método self.draw() Luego se packea ambos, se le agrega el demoCheck y de la misma forma que en el ejemplo anterior se usa map, para aplicarle la función a cada archivo gif, dicha función devolverá una tupla con el nombre del archivo gif y la instancia para cada gif que encuentre, dentro de una lista creada por map. El método draw() al igual que antes setea el texto del Label usando config, y le coloca el nombre del gif, y al botón le pone la imagen del mismo, con config (image=xxxxx)
Vemos que funciona igual que antes y al apretar el botón
Por supuesto a pesar de que crear clases es un poquito mas trabajoso, nos permitirá reusar el código agregándolo a otros widgets fácilmente, como ocurrió con Quitter, demoCheck, etc. Hasta la parte 44 Ricardo Narvaja
44-VIENDO Y PROCESANDO IMAGENES CON PIL En la parte anterior vimos que los scripts de Python pueden usar imágenes asociadas a objetos en los distintos widgets . Al momento de escribir este tutorial, usando ese método, Tkinter puede mostrar imágenes GIF, PPM y PGM creando objetos PhotoImage y también los archivos de estilo X11 bitmap (.XBM) , creando un objeto BitmapImage de la misma forma. De cualquier forma vemos que existe una limitación ya que existen muchísimos formatos (JPEG por ejemplo) que Tkinter no puede manejar directamente, aunque no es una limitación de Tkinter en si, sino de las librerías Tk, seguramente con el tiempo se irán expandiendo para poder crear objetos imagen de otros formatos de archivo, pero al día de hoy, si queremos hacerlo o necesitamos convertir las imágenes a uno de los formatos anteriores con un programa procesador de imágenes, o bien instalar el paquete PIL que es una extensión que nos permitirá trabajar con esos formatos. PIL son las iniciales de Python Imaging Library es un sistema open source que soporta mas de 30 formatos diferentes de imagen (entre ellos GIF, JPEG, TIFF y BMP). Ademas de permitirnos trabajar como lo veníamos haciendo, creando objetos de archivos de imagen para usarlos en los widgets, provee también herramientas de procesamiento de imágenes como transformaciones geométricas, creación de thumbnails, conversión de formatos y mucho mas. En la pagina http://www.pythonware.com/ en el search si ponemos PIL podemos encontrar las ultimas versiones.
Downloads The following downloads are currently available: PIL 1.1.6 • • • • •
Python Imaging Library 1.1.6 Source Kit (all platforms) (440k TAR GZ) (December 3, 2006) Python Imaging Library 1.1.6 for Python 2.2 (Windows only) Python Imaging Library 1.1.6 for Python 2.3 (Windows only) Python Imaging Library 1.1.6 for Python 2.4 (Windows only) Python Imaging Library 1.1.6 for Python 2.5 (Windows only)
Es un instalador común, ejecutándolo no tenemos problemas para instalarlo, respetamos el path donde esta instalado Python que lo detecta bien. Lo que en la parte 43, realizábamos creando un objeto usando PhotoImage y luego ese objeto lo usábamos en los argumentos al crear el widget, pasandole image=xxxxxx donde xxxxxx era el objeto creado, en el ejemplo siguiente sera image=imgobj.
Usando PIL
Vemos que lo que realmente estamos haciendo es usar la clase PhotoImage que trae incluida ImageTk que es un modulo de PIL, en vez de la que viene por default en Tkinter, el resto es similar, podemos crear objetos imagen con ella, y luego usando image=xxxxxx en los argumentos, funcionara de la misma forma que antes, es una simple sustitución de la clase PhotoImage standard que viene incluida en Tkinter por una mas completa que es la que trae el paquete PIL que se llama de la misma forma. O sea que si en el código de los scripts de la parte 43 le agregamos al inicio from ImageTk import PhotoImage El resto sera exactamente igual y no necesitaremos hacer mas cambios, usara esta clase en vez de la original de Tkinter y creara los objetos al instanciar en ella de la misma forma.
El paquete PIL puede hacer mucho mas de lo que explicamos en esta reseña, si uno mira su ayuda, vera que tiene todo tipo de herramientas incluidas para procesar imágenes de diferentes formatos, inclusive conversores, cambios de tamaño y transformaciones algunas de las cuales pueden correr directamente desde la linea de comandos. En la etapa anterior vimos algunos ejemplos de como utilizar imágenes en algunos widgets, veremos otros ejemplos como el siguiente llamado viewer-tk.py.
En este primer ejemplo usamos el PhotoImage standard de Python ya que como el formato del archivo es gif el cual colocamos en la carpeta de gifs para que lo halle fácilmente, no tenemos problema para crear un objeto de el, usando las librerías incluidas en Tkinter.
Vemos en el código, que si no se le pasa argumentos levantara el archivo 'newmarket-uk-2.gif', que acabamos de guardar, pero si lo llamamos con un argumento, imgfile tomara el nombre del archivo de dicho argumento. Luego con os.path.join unirá el path con el nombre del archivo, que si lo pasamos como argumento debemos tener cuidado de que este en la carpeta de gifs, pues allí lo buscara. Luego realiza una instancia en Tk() para crear la ventana raíz, a la cual mediante title, le configura el titulo de la misma, para que muestre el nombre del archivo a mostrar. Y luego como realizábamos anteriormente creamos el objeto imagen y un Label donde le pasamos como argumento image=imgobj, para que muestre dicha imagen. Vemos que antes de salir usa el método width() y height() para obtener las medidas de la imagen las cuales imprime.
Vemos que al no ponerle argumentos mostró la imagen por default y su tamaño, y si le pasamos como argumento el nombre de alguno de los otros gifs que hay en la carpeta.
Vemos que cualquier gif que haya en la carpeta lo mostrara así como su tamaño. Por supuesto este ejemplo solo funciona con los tipos de imágenes soportados en forma standard por Tkinter como gif, si queremos ampliar para mostrar mas tipos de imágenes, debemos realizar la versión con PIL que veremos a continuación llamada viewer-pil.py, la cual puede trabajar con imágenes jpeg y muchos mas formatos, para probarlo colocamos una imagen jpeg en la carpeta de imágenes.
Si lo ejecutamos desde la linea de comandos vemos que a diferencia del anterior, este puede mostrar los JPEG y muchos mas formatos de imagen.
Por supuesto la versión PIL también puede mostrar los gifs.
Y en esta versión vemos que puede mostrar, el tamaño después de cerrar la ventana.
El próximo ejemplo sera mostrar todas las imágenes de un directorio, usando algunas de las herramientas que vimos en su momento para trabajar en directorios, se llamara viewer-dir.py
Antes de analizar el código veamos como funciona:
Vemos que crea ventanas independientes para cada imagen que encuentra en la carpeta y un botón QUIT ALL para quitarlas a todas. Ahora miremos el código paso a paso.
Vemos que hay un path por default donde buscara imágenes, pero si se le pasa un argumento, este sera el path donde buscara las gifs. imgfiles = os.listdir(imgdir) Vemos que en imgfiles creara una lista con todos los archivos que hay en la carpeta.
Luego vemos que crea una ventana raíz, instanciando en Tk() y le pone el titulo 'Viewer' y le agrega el botón 'Quit All' cuya función callback es main.quit y le configura la fuente en este caso courier tamaño 25. También crea una lista vacía llamada savephotos donde ira guardando los nombres de los archivos que realmente sean imágenes que pueda mostrar.
Luego dentro de un for en el cual imgfile, tomara todos los nombres de los archivos de la lista imgfiles, usara os.path.join para agregarle al path de la carpeta de trabajo donde están las imágenes, el nombre del archivo, luego crea una ventana TopLevel que sera independiente, y le pone el titulo a dicha ventana usando title, dicho titulo sera el nombre del archivo a mostrar. Luego dentro de un try -except intenta crear un objeto imagen y si no hay error, le agrega un Label pasandole como parent la ventana TopLevel que creo para que se agregue en ella, y imprime el path y nombre del archivo y el tamaño. Si hasta aquí llego sin error es porque el archivo que esta manejando es un formato de imagen aceptado, y si es así apendea a la lista savephotos, el objeto que creo imgobj, si no es un archivo de imagen de formato soportado, ira al except pues provocara un error al tratar de crear un objeto imagen en un formato incorrecto y en ese caso crea un Label mostrándonos el error.
Allí creo una segunda carpeta gif2 donde coloco imágenes y un par de archivos que no lo sean, en este caso un exe y un doc, para que no pueda crear objetos imágenes de ellos y de error, ademas de probar que si le paso como argumento el path de otra carpeta funcionara.
Llamando al script sin argumentos vimos que muestra las imágenes de la carpeta gifs que es la carpeta por default, pero si le pasamos como argumento el path a otra carpeta.
Vemos que en esta caso ademas de aceptar el path y mostrar las imágenes de esta nueva carpeta como ventanas TopLevel, también nos muestra una ventana por cada archivo que no es de imagen, y dio error al tratar de crear un objeto imagen con el.
CREANDO THUMBNAILS CON PIL Entre las diferentes posibilidades que trae incluidas el paquete PIL, esta la de crear pequeños THUMBNAILS a partir de una imagen original. El ejemplo que veremos a continuación se llama viewer_thumbs.py como el código es un poco extenso, lo pongo en partes, igual lo explicaremos paso a paso.
Bueno vemos que es un script extenso así, que lo iremos debuggeando para ir viendo paso a paso lo que va haciendo, allí puse un breakpoint donde comienza a ejecutar después de realizar las definiciones.
Allí paró, como esta ejecutándose como principal, entrara dentro del if, apretando f7 que es STEP
INTO ingreso al mismo.
Allí comprobará si hay argumentos, si resolvemos primero el paréntesis (len(sys.argv) > 1 and sys.argv[1]) El len de sys.argv no es mayor que uno o sea es Falso porque no tiene argumentos en este caso y al realizar el AND con sys(argv[1]) que es falso, el resultado del AND de dos miembros falsos es falso.
Por supuesto al hacer OR entre el resultado del paréntesis que es falso y el path por default que es verdadero, el resultado sera lo verdadero o sea el mismo path por default.
Quiere decir que cuando no haya un argumento usara el directorio por default, en el caso que el resultado del paréntesis fuera verdadero y hubiera argumento este seria otro path donde trabajar.
Siempre la función OR siendo los dos argumentos True, devuelve el primero de ambos, de forma que siempre usara el nuevo path que ingresemos en vez del path por default. Bueno continuemos con nuestro ejemplo, sigamos con el path por default como lo ejecutamos originalmente.
Vemos que en la siguiente linea llama a la función viewer(), pasandole como argumento el path y Tk que es la clase conocida por nosotros para crear ventanas raiz. Así que entramos a la función con F7.
Vemos que kind como tiene asignado Tk, lo que realmente esta haciendo es instanciar win en Tk() para crear la ventana raíz, podría haberlo puesto directamente como win=Tk() Ahora apretamos F6 porque no necesitamos entrar dentro de la instanciacion, así salteamos a la siguiente linea directamente.
Vemos que win como dijimos, es realmente una instancia de Tk(), así que en la siguiente linea usando title, le configurara el titulo que sera 'Viewer:' a lo cual le agregara el path a la carpeta donde estamos trabajando.
Bueno llegamos al llamado a otra función makeThumbs() a la cual se le pasa el path de la carpeta de trabajo como argumento.
Entremos con f7 para ver que hace dicha función
Vemos que la función tiene otros argumentos, que tienen valores por default, el tamaño o size que viene seteado en (100,100) y un subdirectorio 'thumbs' que creara dentro de la carpeta de trabajo para guardar los thumbnails.
Vemos que en thumbdir unirá el path de trabajo con el subdirectorio thumbs y luego verifica si este existe con os.path.exists() y si no existe lo crea con os.mkdir(), paso con F6 estas instrucciones y verifico que el subdirectorio se creo.
Luego crea una lista vacía thumbs
Ahora dentro de un for usa os.listdir(imgdir) para crear una lista con los archivos que hay en la carpeta de trabajo y imgfile tomara en cada ciclo los nombres de cada archivo de esa lista.
Si entramos dentro del for, imgfile, tomara el valor del nombre del primer archivo que halla en la carpeta de trabajo.
Y thumbdir verificamos que es el path al subdirectorio thumbs
Al unir ambos quedara el path mas el nombre del archivo en uso subdirectorio.
Luego verifica si existe ese archivo creado allí
Como la subcarpeta thumbs esta aun vacía, por supuesto va al else.
Allí imprime que esta creando el archivo
pero dentro de dicho
Así que tenemos dos paths: thumbpaths es el path del archivo dentro de la subcarpeta 'thumbs' y imgpath es el path original del archivo dentro de la carpeta de trabajo “c:\gifs”
Luego usara Image que es otro modulo de PIL que importo al inicio, y se necesita para hacer los thumbnails, usando su método open() abro el archivo JPEG.
Luego para disminuir su tamaño uso el método thumbnail que reduce de tamaño la imagen, uno de los argumentos es size que era (100, 100) y el otro Image.ANTIALIAS es el mejor filtro para realizar la reducción, hay otros disponibles, pero usando este saldrá perfecto.
Luego guarda el archivo reducido con el método save() de Image
Y al final apendea a la lista thumbs el nombre del archivo y la instancia del thumbnail como tupla que sera un campo de la lista thumbs.
Vemos que si fuera el caso de un archivo de un formato que no es de los que maneja PIL, dará error y ira al except donde imprimirá que lo saltea 'skipping', en nuestro caso no colocamos en la carpeta de trabajo gifs ningún archivo que no sea de imagen, pero si hubiera exes, o docs o cualquier otro formato que no sea de imagen lo salteara. Este for se repetirá por cada archivo que encuentre, alli vemos el primer thumbnail que creamos.
Vemos la comparación entre el tamaño del original y el thumbnail
Como no necesitamos repetir el mismo proceso paso a paso para cada archivo porque ya entendimos como trabaja esta función, apretamos f8 que es STEP OUT que nos llevara fuera de la misma adonde retorna al salir de ella, luego de crear y guardar un thumbnail por cada imagen que
encuentra en la carpeta de trabajo.
Volvimos lógicamente a la instrucción siguiente desde donde se llamaba a la función makeThumbs(), de esta forma ya salimos de la misma y todos los thumbnails se habrán generado.
Ahora testea y como cols aun no existe entra a asignarle un nuevo valor
Vemos que a col le asigna el resultado de una operación matemática, lo primero es que obtiene el largo de la lista thumbs para saber cuantos thumbnails fueron creados.
En nuestro caso creo cuatro thumbnails, luego le aplica sqrt que es la raíz cuadrada de ese numero o sea que la raíz cuadrada de 4 es 2.0.
Luego a dicho resultado el aplica ceil que es el redondeo hacia arriba para quitar los decimales, o sea que quede con decimal 0.
Por lo tanto el resultado de toda esa cuenta sera 2, ya que en nuestro ejemplo la cantidad de thumbnails es cuatro, a eso le aplica la raíz cuadrada que es 2.0, a ello le aplica el ceil que lo deja en 2.0, y int que lo dejara en 2 al transformarlo a entero
Llegamos a la ultima parte donde crea una lista vacía savephotos[]
Luego dentro de un while mientras haya thumbs
Allí mediante indice slice realiza una doble asignación, donde tumbsrow, tendrá las dos primeros campos de la lista y thumbs disminuirá pues quedara con solo los últimos dos.
thumbsrow, thumbs = thumbs[:cols], thumbs[cols:] Esto en nuestro caso sera equivalente a thumbsrow = thumbs[0:2] thumbs =thumbs[2:] De esta forma como trabajará con thumbsrow que tiene los dos primeros, quedan en thumbs los otros dos para cuando se repita el ciclo, volver a aplicar esta instrucción nuevamente lo que hará que en el segundo ciclo thumbsrow tenga los dos últimos campos para trabajar y thumbs quede vacío, para terminar el ciclo y salir del while. >>> thumbsrow [('newmarket-uk-1.jpg', ), ('newmarket-uk-2.gif', )] Allí vemos que thumbsrow tiene los dos primeros campos, uno por cada thumbnail, y cada campo era una tupla con el nombre del archivo y la instancia.
Luego realiza una instancia en Frame pasandole como parent win y la packea.
Luego dentro de un for imgfile y imgobj tomaran los dos valores del primer campo de thumbsrow, correspondiente al nombre del primer archivo y su instancia.
Aquí vemos otra opción del método que vimos en la parte 43, ademas de instanciar en Photoimage, pasandole el path y nombre al archivo para que cree un objeto imagen, también se puede hacer con una archivo abierto con Image.open (este ya fue convertido en thumbnail) y el efecto es el mismo, se creara un objeto imagen del mismo y luego crearemos un Botón al cual mediante image=xxxxx se le añadirá la imagen del thumbnail.
Luego mediante una función lambda realiza una instancia en la clase ViewOne, a la cual le pasa el path de la carpeta de trabajo y el nombre del archivo , esta función se asigna a handler, la cual la usaremos como función callback del botón, ya que lo configuramos en la siguiente instrucción, usando config (command=handler)
Vemos que la clase ViewOne hereda de TopLevel, por lo tanto al instanciar en ella se crearan ventanas independientes.
Vemos que llama al método constructor de TopLevel, y luego configura el titulo de esta ventana con el nombre del archivo.
Luego vemos que crea un objeto imagen pero en este caso no del thumbnail si no de la imagen original pasandole el path y nombre del archivo original y luego lo mostrara en un Label, usando image=imgobj, luego packea y imprime los valores Por lo tanto el efecto final es que al clickear el botón se llama a su función callback, que creara una ventana independiente para mostrar en un Label, la imagen en tamaño original.
Luego va apendeando los objetos imagen de los archivos originales en la lista savephotos.
Luego de que realiza el mismo trabajo para los dos thumbnails que había en thumbsrow, como thumbs aun tiene 2 campos sobre los cuales no se ha trabajado mediante la doble asignación los pasara a tumbsrow y vaciara thumbs. thumbsrow, thumbs = thumbs[:cols], thumbs[cols:] Para estos dos últimos creara un Frame nuevo de manera de que quede una cuadricula de 2 por 2. Veamos si lo corremos desde una consola, previamente borro los thumbnails creados en la carpeta thumbs para ver si los crea nuevamente.
Vemos que allí los volvió a crear y muestra los botones con sus objetos thumbnails en ellos, si aprieto alguno de los botones, vemos que crea una ventana TopLevel con la imagen original en un Label.
Y a la vez me imprime el tamaño original.
Veamos que ocurre si lo corremos en la otra carpeta gif2 donde había doc y exe pasandole como argumento dicho path.
Vemos que trabajo en forma similar y que salteo los archivos que no son de imagen, en este caso los exe y los doc, imprimiendo skipping y el nombre del mismo. Una variación de este script que es similar pero no guarda los mismos es la siguiente llamada viewer-thumbs-nosave.py
Vemos que este script importa el anterior, pero define una nueva función makeThumbs() que no guarda los thumbnails.
Vemos que esta parte es idéntica al anterior, pero la única diferencia es la linea resaltada, donde reemplaza la función que tenia el modulo anterior, por la nueva makeThumbs() de este modulo, recordemos que al ser funciones, no existe la herencia y la posibilidad de customizar métodos, lo cual se puede hacer si fueran métodos de clases que se heredan, aquí hay que asignarlo para que use la nueva funcion.
Vemos que al igual que en el script anterior abrirá con Image.open el archivo y luego usando el método thumbnail lo reducirá de tamaño, en este caso sin aplicar el filtro Image.ANTIALIAS .
Luego guardara en thumbs al igual que en el anterior, una lista donde cada campo sera una tupla con el nombre y la instancia, para luego al salir de la función y devolver thumbs mediante el return poder crear los botones, de la misma forma que en el caso anterior, solo que ahora no guardara los archivos solo creara los thumbnails y los aplicara a los botones y luego creara los Frames necesarios y al apretar cada botón habrá de la misma forma una función handler que creara una ventana independiente donde los mostrara con su tamaño original, veamoslo correr. Borro los thumbnails de la carpeta thumbs, para verificar que no los guarde allí nuevamente y lo corro.
Y la carpeta thumbs luego de correrlo sigue vacía con lo cual logramos el mismo funcionamiento en forma sencilla y sin guardar solo como visor, ahora que no guarda, podemos aplicarlo sin temor en cualquier directorio, por ejemplo el de Mis imágenes, coloco el path entre comillas como argumento y veo que funciona perfectamente como visor de archivos de imágenes
Vemos que si le agrego mas imágenes a la carpeta y lo lanzo funciona haciendo mas botones y salteando los archivos que no son formato de imagen. Bueno creo que esta parte ya es lo suficientemente larga así que los espero en la siguiente Hasta la parte 45 Ricardo Narvaja
PARTE 45: MENUES TOP LEVEL Creo que todos sabemos lo que es un menú, la mayoría de los programas tiene en la ventana principal una lista de opciones por ejemplo ARCHIVO, EDITAR, VER etc que al clickear en ellas se despliega so contenido mostrando comandos que podemos elegir, por supuesto los nombres pueden cambiar, pero tenemos un idea de que navegando por los menúes, comandos y submenúes encontraremos las diferentes opciones del programa, para poder elegirlas y utilizarlo. En Tkinter existen dos tipos de menúes MENUES TOP LEVEL o MENUES BASADOS EN FRAMES, comenzaremos viendo el primero de ambos. MENUES TOP LEVEL En Python uno puede asociar una barra de menú con un ventana raiz Tk o una ventana TopLevel, esto en sistemas Windows o Linux colocará dicha barra en la parte superior de esa ventana, en Mac, esta barra reemplazara la barra superior que muestra el escritorio y que cambia según el programa que estas usando, el efecto final, es que en todos los sistemas tendremos el menú visible, para poder utilizarlo. Haremos para empezar un menú sencillo, en el Python de la consola para ir viendo paso a paso como va apareciendo cada elemento y el efecto que producen nuestras acciones: Recuerden que esto solo funciona desde la consola de DOS o terminal de Linux donde arrancaremos Python, si quieren usar algo mas avanzado ya vimos que el PyScripter tiene una opción para que se ejecuten comandos de Tkinter interactivamente. Bueno lo primero importemos Tkinter y hagamos una ventana raíz instanciando en Tk().
Pongamosle un nuevo titulo a esta ventana
Debemos configurar también la opción para utilizar menúes con config.
Bueno ahora debemos comenzar a crear el menú superior, para ello hagamos una instancia en la clase Menú, pasandole como argumento root o sea la ventana raíz donde lo vamos a adosar, El nombre de esta nueva instancia que creamos en este caso llamada top que corresponde al menú superior, debe coincidir con el que colocamos en el paso anterior en la configuración menu=top.
Allí realizamos la nueva instancia top, en este caso aun no se ve nada, porque tenemos que aun agregar los comandos que tendrá, sus nombres, funciones callback que se ejecutaran al apretarlos etc. Vemos que top es el menú superior ya que es la que se instanció pasandole como argumento root. Ahora debemos hacer una nueva instancia en Menú por cada columna que queremos que tenga, por ejemplo si queremos que tenga solo una que pertenezca a FILE, hacemos una instancia, si necesitaremos dos por ejemplo una columna que se llame EDIT, hacemos otra instancia, a estas nuevas instancias en Menú las llamaremos Menú Columna para diferenciarlas del Menú superior y son hijas de este. file = Menu(top) edit=Menu(top) Vemos que cada Menú columna sera una instancia de la clase Menú, pero serán hijos del Menú superior, pues como argumento se le pasa top como parent. (y no root como cuando instanciamos el menú superior)
Por ahora solo agregare una columna file, a la misma debemos agregarle los comandos para que se complete, antes de ello como cada comando debe tener una función callback para que al apretarlo ejecute dicha acción haremos una función que solo imprima un mensaje para probar. def handler (): print 'CLICKEANDO MENUES'
Allí cree una función que la usaré como callback llamada handler, luego agrego los comandos con add_command aplicándoselo al Menú columna llamada file.
Aun queda un pasito para ver la primera columna del menú y es usando top.add_cascade(label='File',
menu=file,
underline=0)
Que configura agregando al menú superior top, el nombre de la columna relacionándolo con la instancia file que habíamos hecho para esta primera columna del menú. -----------------------------------------------------------------------------------------------------------------from Tkinter import * root=Tk() top=Menu(root) root.title ('PRACTICANDO MENUES') root.config(menu=top)
file = Menu(top) def handler (): print 'CLICKEANDO MENUES'
file.add_command(label='New...', command=handler, underline=0) file.add_command(label='Open...', command=handler, underline=0) top.add_cascade(label='File',
menu=file,
underline=0)
---------------------------------------------------------------------------------------------------------------------Si no tienen ganas de tipear todo el código, pueden copiarlo de aquí y pegarlo luego de tipear Python, apretan ENTER y se ejecutara en bloque, pero creo que es interesante aprender paso a paso.
Allí vemos que al clickear se llama a la función handler que imprime el mensaje
Quiere decir que todo se resume a estos 4 pasos 1. Crear el Menú superior que tendrá como parent la ventana principal y configurar el atributo menú de dicha ventana. 2. Por cada columna crear un nuevo Menú columna que sera hijo del Menú superior y agregar al menú superior usando add_cascade. 3. Agregar comandos a cada Menú columna usando add_command y configurar las funciones callback de cada uno con su opción command. 4. Si queremos agregar otro submenú incluido dentro del menú columna se podrá agregar instanciando en Menú. pasandole como parent el Menú columna, para que se hijo de este.. Quiere decir que los menúes están anidados, unos son hijos de otros, y tienen niveles desde el menú superior, luego el nivel siguiente son los menúes columnas y luego si hay otro submenú cascada incluido dentro sera el nivel mas bajo. Veamos el primer script de ejemplo llamado menu_win.py.
Si lo ejecutamos vemos que en este caso tendremos un Menú superior, dos Menús Columnas FILE y EDIT con sus respectivos comandos y dentro de EDIT habrá un submenú que se llamara STUFF, y tendrá dos comandos dentro SPAM Y EGGS. Bueno ahora que sabemos como será miremos un poco el código para ver paso a paso como funciona.
Bueno luego de las importaciones de rigor y de definir las funciones empieza aquí, si esta siendo ejecutado como principal, entrara dentro del if y hará una instancia en Tk() llamada root a la cual le pondrá el titulo con title. Luego llamara a la función makemenu(), a la cual se le pasa esta instancia root como argumento ya que la necesita usar dentro de la función, para agregar los menúes en la ventana raíz. Vemos que root al llamar a la función se asigna a win que es el argumento de la misma, así que win es también la misma instancia de la ventana raíz en Tk().
Luego realiza la instancia en Menú para crear el Menú superior top.
Recordemos que el parent al instanciar el menú superior, debe ser la instancia de la ventana raíz o sea root o win en este caso. Luego a la ventana raíz debe configurarle usando win.config (menu=top) y de esta forma top sera la instancia de Menú superior para dicha ventana raíz. Así que siguiendo los mismos pasos, luego de crear la instancia del Menú superior, debe crear una instancia del primer Menú Columna en este caso hay mas de una columna en el menú pero empieza por la primera que es la que corresponde a FILE.
Allí vemos como realiza la instancia recordamos que estos Menúes columna son hijos del Menú
superior, por lo tanto la instancia de este debe pasarse como parent. Una vez creada la instancia para la columna FILE, se le agrega con add_command los comandos con su nombre y su función callback que en este caso es una función definida llamada notdone, que muestra el dialogo de error, en el comando Quit, se usa como función callback win.quit para cerrar la ventana y terminar.
Luego con add_cascade que es el último paso le agregamos a top, el nombre de la columna y menu =file termina de relacionarlo y lo ubica en su posición así que hasta aquí ya tenemos la primera columna del menú con sus comandos listos, ahora repetimos el mismo trabajo para la segunda. Allí instancia el segundo Menú columna que sera EDIT a la cual lógicamente le pasa como parent
la instancia top del Menú superior ya que es hijo de el.
Ya veremos la opción de configuración tearoff=0 mas adelante para que sirve, luego con add_command agrega dos comandos
Ambos CUT y PASTE también llaman a la función callback notdone. Luego agrega un separador que es la linea que separa esos dos comandos de lo que vendrá debajo, con add_separator
Y finalmente agrega a top el menú EDIT , configurandole el nombre y realacionandolo con add_cascade. Bueno vemos que en este menú EDIT le agrego los dos comandos CUT y PASTE aun le falta agregar el submenú STUFF que es lo que hará a continuación.
Vemos que crea un Menú mas, tal cual los otros dos anteriores agregando con add_command sus comandos SPAM y EGGS y con add_cascade lo relaciona a edit, lo que hace que este menú se ubique dentro de EDIT y no arriba como un tercer menú columna es que se le paso como parent al instanciar edit,y se aplico add_cascade también a edit, por lo tanto significa que sera hijo de este y estará dentro del mismo.
Vemos que es lo único que lo diferencia de los otros dos Menú columna, al pasarle como parent edit, y aplicarle add_cascade a edit, estará dentro del mismo, si cambio edit en ambos casos por top, ya no sera un submenú de edit, y sera un menú columna mas. Volvamos a la normalidad y que stuff sea un submenú
Al retornar de la función ya están todos los menúes creados y le agrega un Label.
El cual le coloca un texto 'Windows menu basics' luego lo packea y configura el borde con opciones que ya hemos visto.
Allí vemos lo que significa la opción de configuración tearoff=0, al quitársela al menú edit, aparece la linea de puntos arriba del primer comando del menú, al poner tearoff=0 desaparece.
Dichas lineas de puntos al clickear en ellas, el menú se ubica fuera de la ventana, por ejemplo clickeando en la linea de puntos del menú file.
Nos quedo probar la función callback notdone, al clickear en cualquier comando que no sea Quit.
Otra opción usada es underline que subraya los comandos para que haya una secuencia de teclas que sea similar a clickear en ellos.
Con ALT + F se abrirá el menú FILE y luego con ALT mas O se ejecutara la función callback de OPEN etc. En este ejemplo siguiente usaremos la función makemenu() del script anterior en uno nuevo que le agregara los mismos menúes a varias ventanas TopLevel se llamara menu_win-multi.py
Vemos que es muy sencillo, luego de hacer una instancia en Tk() llamada root, dentro de un for que se repetirá 3 veces, realiza instancias en TopLevel que serán ventanas independientes y para cada una de ellas llama a la función makemenu() pasandole como parent estas instancias de TopLevel para ubicarles en ellas los menúes, y al volver de la función a cada una le colocara un Label negro sin texto y al salir del for agregara un Botón para cerrar en la ventana raíz
Allí vemos, lógicamente la ventana raíz no tiene menú, pues en ella solo le agrego el botón, al llamar a la función makemenu(), le paso como argumento las instancias de TopLevel, no root. Si le agregamos una linea al salir del for, para que a la instancia root le agregue también su menú con makemenu(root)
Vemos que lo hace perfectamente, sea una ventana raiz o TopLevel, la función makemenu() le agregara el menú que preparamos en el script anterior. En la parte siguiente veremos el segundo caso de MENUES BASADOS EN FRAMES Hasta la parte 46 Ricardo Narvaja
46-MENUES BASADOS EN FRAMES Aunque son mucho menos comunes que los menúes Top Level, se puede crear una barra de menú como un Frame horizontal, lo cual tiene unas lineas mas de código que los menúes tradicionales Top Level, pero en ciertos casos puede servir, veremos algunos ejemplos de ello este se llamara menu_frm.py.
Antes que nada corramoslo a ver como funciona.
Vemos que es muy parecido al menú Top Level de la parte 45, miremos el código a ver la diferencia.
Luego de las importaciones comienza aquí, esta parte es idéntica al script de menúes Top Level, crea una instancia en Tk() llamada root, le pone el titulo con title, y luego llama a la función makemenu(), pasandole root igual que en la parte anterior, por supuesto luego de volver de esta función que es lo diferente, creara un Label en este caso con el texto 'Frame menu basics' y lo packeará y configurara con config, el tamaño, el color, y el borde SUNKEN. Por lo tanto veamos la función makemenu() que es la encargada de crear los menúes y es la que hace la diferencia.
En este caso vemos que en vez de realizar la instancia del Menú superior, lo que hace es realizar una instancia en Frame para crear la barra de menúes y la llama en este caso menubar y por supuesto como todo Frame luego de crearlo se packea.
Veamos la diferencia entre el código de crear el menú Top Level y el basado en Frame
TOP LEVEL
FRAME BASED
Recordamos que en el menú Top Level se realizaba una instancia en menú usando la del menú superior como parent, y se crea un menú columna, en el basado en Frame hay que crear un Menubutton, pasandole como parent la instancia en Frame de la barra de menúes llamada menubar. Luego instanciamos en Menú, usando el Menubutton como parent. El resto es similar con add._command le agregamos los comandos con sus funciones callback y en vez de usar add_cascade se utiliza el config del Menubutton para relacionar menu=file con lo cual se terminara de configurar. En la otra columna repite el mismo esquema
En este caso agrega un separador y el tercer menú cascada, vemos que es idéntico al del ejemplo de la parte anterior solo se diferencia en que se pasa edit como parent, por lo tanto este menú sera hijo del anterior.
O sea resumiendo lo importante es esto menubar = Frame(parent)
# Creamos un Frame para la barra de menú
fbutton = Menubutton(menubar, text='File')
# agregamos un Menubutton al Frame
file = Menu(fbutton) fbutton.config(menu=file)
# agregamos un Menú (su parent sera el Menubutton) # relacionamos usando el config del botón
Algunas de las ventajas de usar Frames es que puede usarse eventos bind en ellos, ademas de soportar configuraciones, colores, bordes, y poder personalizarse, por supuesto también tienen la propiedad de atachearse a otros widgets.
Vemos en la imagen que el Menubutton, mediante config, puede ser cambiado de color, y borde y ser personalizado fácilmente. Veamos este ejemplo:
Recordamos en la parte anterior de menúes Top Level como agregábamos menúes a ventanas independientes, en este caso usando Frame no solo podemos hacer eso mismo, podríamos crear varias ventanas independientes y a todas agregarle su menú, si no que también podemos crear varios menúes en una misma ventana.
Es una sola ventana raíz a la cual dentro de un for que se repite 2 veces se le agrega un menú y un Label cada vez, todo dentro de la misma ventana.
Y al salir del for se le agrega un botón para quitar con el texto Bye. Vemos que si quisiéramos adaptar el script de menú Top Level para realizar el mismo trabajo
Vemos que con menúes Top Level solo podemos agregar un menú por cada ventana independiente, se agrega un Label por cada ciclo del for, pero solo un menú por ventana, esa es una de las ventajas de usar Frames ya que es un widget mas y por lo tanto puede colocarse en cualquier lugar como si fuera un botón mas que quisiéramos agregar en algún lugar.
Veamos el siguiente ejemplo llamado menu_frm-multi2.py
Vemos que este caso es similar al anterior solo que aquí se realiza una instancia en Tk() y luego se realiza una instancia en Frame a la cual se le agregan los menúes y los Labels, quiere decir que los menúes basados en Frame no solamente funcionan en ventanas Top Level y Raíz, si no en cualquier Frame puede ser agregado un menú de este tipo. El siguiente ejemplo se llama mbutton.py en el cual se vera como se puede usar Menubuttons, sueltos, inclusive sin usar menú basados en Frame.
Vemos que en nuestro ejemplo actual se instancia el botón directamente en la ventana raíz root, en vez de en un Frame usado como menubar, por supuesto el resto es bastante similar.
Allí esta el Menubutton y al apretarlo mostrara los comandos.
Como ultimo ejemplo veremos los optionmenu en este ejemplo llamado optionmenu.py
Vemos que el optionmenu es un botón de menú que muestra siempre una de las opciones que puedo elegir del mismo.
Vemos que usamos variables Tkinter para setear el estado inicial de estos optionmenu.
También definimos una función llamada state que es la función callback de un botón, que al apretarlo imprime el estado de los optionmenu, usando get() para capturar el estado de las variables Tkinter e imprimirlo luego. Vemos que los optionmenu son en cierta forma parecidos a los Menubutton, solo que muestran siempre cual es la opción elegida, las opciones se pasan sencillamente como strings, luego de la variable Tkinter.
Bueno por hoy es suficiente hasta la parte 47 Ricardo Narvaja
47-LISTBOXES Y SCROLLBARS Si repasamos los tipos de widgets vemos que estamos llegando al final del estudio de los mismos, nos quedan solo 4 tipos para terminar de estudiarlos todos y empezar a programar, así que paciencia que ya vemos llegando, en esta parte veremos dos tipos de widget mas que son LISTBOXES y SCROLLBARS.
Recordemos que un listbox es una lista de opciones de las cuales se selecciona una..
Y scrollbar son las barras de scroll para subir o bajar dentro de un widget si es una barra en sentido vertical como en este ejemplo.
Por supuesto también hay scrollbar horizontales, para moverse en forma horizontal por el mismo. En el siguiente ejemplo veremos un ejemplo del uso de ambas en forma simultanea llamado scrolledlist.py.
Allí tenemos el código completo como siempre lo analizaremos paso a paso, a primera vista vemos que usa una clase con lo cual será código reutilizable, y se podrá agregar en otros widgets, importando la clase.
Vemos que si esta siendo ejecutado como principal crea mediante map una lista, veamos como construye la misma. options = map((lambda x: 'Lumberjack-' + str(x)), range(20)) Recordemos que map creaba una lista aplicándole la función del primer argumento, al segundo argumento y si este es un rango como en este caso de 0 a 19, pues aplicara la función a cada uno de los valores de ese rango y cada uno de los resultados de dicha función sera uno de los campos de la lista.. El primer campo se calcularía cuando x vale cero (lambda 0: 'Lumberjack-' + str(0) O sea que sumaria la string 'Lumberjack-' a str(0) quedaría como primer campo 'Lumberjack- 0' y así como x tomaría los valores de 0 a 19 los siguientes campos serian 'Lumberjack- 1' 'Lumberjack- 2' ..... 'Lumberjack- 19'
>>> options ['Lumberjack-0', 'Lumberjack-1', 'Lumberjack-2', 'Lumberjack-3', 'Lumberjack-4', 'Lumberjack-5', 'Lumberjack-6', 'Lumberjack-7', 'Lumberjack-8', 'Lumberjack-9', 'Lumberjack-10', 'Lumberjack-11', 'Lumberjack-12', 'Lumberjack-13', 'Lumberjack-14', 'Lumberjack-15', 'Lumberjack-16', 'Lumberjack-17', 'Lumberjack-18', 'Lumberjack-19']
>>> O sea que obviamente si queremos hacer un listbox debe haber una cantidad de elementos para mostrar para seleccionar uno de ellos y estas string serán dichos elementos. Si antes de continuar estudiándolo lo ejecutamos, vemos que hará un listbox con la lista que le pasamos como argumento y le añadirá un scrollbar para poder subir y bajar entre los elementos de la misma.
Allí entramos en el método constructor de la clase que hereda de Frame, ya sabemos que si queremos hacer un widget que va a ser reutilizado, lo mejor siempre es heredar de Frame, pues es la clase mas flexible y que puede contener múltiples widgets y adosarle nuevos fácilmente y se presta a la reutilización como ya vimos con Quitter y otros widgets que creamos en forma de clases, que podían atachearse por todos lados jeje. Lógicamente llamamos al método constructor de Frame y luego se packea inmediatamente.
Luego llama al método propio self.makeWidgets() pasandole como argumento la lista que creamos y que usará para armar el listbox.
Vemos que dentro del método crea dos instancias una en Scrollbar y otra en ListBox.
Las dos lineas siguientes son las que hacen la magia jeje de interconectar el scrollbar con el listbox, para que a través de la función callback de cada una se llame a un método de la otra y se mantengan sincronizados. sbar.config(command=list.yview) Vemos que al mover el scrollbar, su función callback llama a un método interno de Listview llamado yview que ajusta el listbox proporcionalmente según los argumentos que se le pasan, pero como estos provienen del movimiento del scrollbar, se acomodara en la misma proporción. A su vez cuando movemos el listbox se configura mediante yscrollcommand( en vez del clásico command) y esta llama al método interno de Scrollbar sbar.set que ajusta el scrollbar en la misma proporción.
Esto puede realizarse con cada widget que pueda ser scrollable, todos tienen la posibilidad y los mismos métodos para enlazarse con el scrollbar mediante yview si es vertical o xview si es horizontal, en la misma forma que lo hacemos con el listbox, ya veremos otros ejemplos luego. Por lo demás, para agregar las entradas de los listboxes se utiliza el método insert() list.insert(pos, label) Donde pos es la posición o sea un offset que comienza de cero y label el string correspondiente a esa posición.
Por lo tanto dicho trabajo lo realiza en ese for, a la instancia del listbox le aplica el método insert, y ha creado una variable pos que comenzara de cero y se ira incrementando de uno en uno, para usarla como el argumento numérico que señala la posición de cada string. También existe un comodín para poder agregar al final de la lista que se puede utilizar que es la palabra END, si uno quiere agregar al final directamente puede usar. list.insert('end', label)
list.insert(END, label) Y con ello agregara al final la string que corresponde y esta en label. Como los listboxes no tienen una función callback propia manejada con command, solo tiene la que lo relaciona con el scrollbar, por lo tanto debemos usar bind para capturar los clicks en ella.
Allí vemos que bind, capturara el dobleclick y se ejecutará su función callback que es self.handlelist, por lo demás la instancia list es asignada a una variable self.listbox para poder usarla fuera de este método.
Allí esta la función callback, usando el método curselection(), se obtienen el indice numérico que se eligió y con get() aplicado a ese index, la string correspondiente.
Luego llama a la función runCommand()
Que imprime el texto que seleccionaste.
Otra forma de obtener cual es el elemento seleccionado es usando label = self.listbox.get('active') o
label = self.listbox.get(ACTIVE) Directamente se obtiene el texto del elemento activo en un solo paso sin tener que obtener el indice numérico primero.
Vemos que en un solo paso se obtiene el texto elegido. Es de mencionar que los Listboxes puede funcionar perfectamente sin scrollbars, aquí los hicimos trabajar en conjunto para ver un ejemplo de ambos.
Aquí vemos un listbox sencillo creado en la consola para ir viendo como se va generando, inserto dos o tres campos de la lista mediante insert(), packeo y ya esta funcional, en este caso no necesitamos scrollbar.
Vemos que definiendo una función callback para BIND podemos hacer que imprima el texto que seleccionamos
Al hacer doble click imprime
Vemos que es muy sencillo, en el primer script que hicimos se complico un poco al hacer inter relacionar el scrollbar con el listbox, pero bueno así se aprende jeje. Un detalle a tener en cuenta cuando se usan scrollbars es que siempre de todos los widgets debe ser el que se packea primero
Esto es porque si se estira o achica la ventana, el que siempre se respeta es el primero packeado y es importante que asi sea, sino el scrollbar quedara sin abarcar toda la ventana. Bueno seguimos avanzando lentamente yo se que es imposible recordar todo esto de memoria, pero yo creo que el caso, es que cuando uno lo leyó y lo va a utilizar siempre puede ir a fijarse en el viejo tute, como era y refrescar lo aprendido, seguimos en la parte 48 con los widgets que nos faltan. Hasta la parte 48 Ricardo Narvaja
48-TEXTO Dentro de las posibilidades que tenemos entre los widgets tanto Text como Canvas son widgets mas elaborados, por lo tanto su programación tiene unos puntos mas para ver, que la creación de un simple Label, o un Botón, en un Text debemos poder editar texto y en un Canvas imágenes, por lo cual necesitamos conocer los métodos que estos widgets usan para realizar dichas acciones.
Si en una consola de Python hacemos una instancia en Text y luego la packeamos vemos que tenemos una especie de editor de texto en el cual podemos escribir, la base es sencilla.
Vemos que el método get() nos permite leer texto, debemos pasarle como primer argumento el inicio a partir de donde queremos leer en este caso '1.0' significa linea uno, desde el carácter 0, si tipeamos mas lineas.
text.get('3.0',END)
En este caso nos devolverá desde el inicio de la tercera linea hasta el final del documento.
Si queremos comenzar en la letra o de hola en vez de en la h, al usar '3.1' para iniciar en la linea tres pero desde el carácter numero 1 como inicio, leeremos desde allí, lo mismo podemos setear el final.
Cambiando END por el numero de columna y carácter en este caso '5.2' para que termine en la linea 5, carácter numero 2. (no incluye al ultimo en este caso al 2)
Otra opción es usar insert() para agregar texto, el primer argumento sera el texto a insertar y el segundo donde lo ubicaremos, es este caso agregaremos la string 'manola', a partir de '2.3,' el tema es que allí no hay texto tipeado era una linea vacía y para poder escribir desde el carácter 3 debería haber algo escrito adelante, como esta vacía lo colocara al inicio al no saber que poner delante, si hubiera habido texto escrito lo reemplazaría veamos.
Intentaremos insertar en '2.3' la palabra 'jeje'
al apretar ENTER
Vemos que en este caso, si lo pudo insertar pues ya había texto escrito en esa posición, en el caso anterior estaba vacía la linea y había que llenarla desde el inicio. Bueno ya vimos lo mas básico para poder lanzarnos con un script mas complejo, este se llamara scrolledtext.py ya que usara Text y Scrollbars como es un ejemplo largo lo analizaremos paso a paso como siempre, si es necesario debuggeando.
Bueno allí esta el código veamos que hace paso a paso.
Vemos que luego de hacer la instancia de la ventana raíz llamada root, dentro de un try-except prueba si le hemos pasado argumentos para ubicar el nombre de un archivo de texto a mostrar, si no puede ubicar un archivo de texto valido para pasárselo como argumento de la función, setea una variable text con la string 'Words\ngo here' para indicar que esta vacío, probemos si este es el
funcionamiento principal. Al correrlo sin argumentos
En cambio si le pasamos el nombre de un archivo de texto que pueda ubicar con su path (o si esta en la misma carpeta del script, le pasamos el nombre solo)
Si pongo como Debug file, y le coloco argumentos en este caso el nombre de un archivo de texto que tengo en la misma carpeta que el script.
O sea que el script mostrara un archivo en el editor de texto, si puede encontrar el archivo de texto y leerlo, si no mostrará el texto 'Words\ngo here', vayamos a analizar la función.
En este caso no le paso argumentos por lo cual usara text=.'Words\ngo here' Entro en la función con f7.
Bueno la clase hereda de Frame ya sabemos que Frame es el mejor contenedor y mas flexible de varios widgets, ya lo sabemos jeje, bueno luego de llamar al método constructor de Frame y hacer el pack obligatorio al mismo, llama a la función makewidgets() , en forma muy similar al ejemplo scrolledlist.py de la parte anterior. De la parte anterior:
El actual vemos que es similar.
Se debe hacer una instancia en Scrollbar y otra en el widget al cual le queremos adosar la barra de scroll, en este caso sera en Text. Así que ya tenemos sbar que es una instancia de Scrollbar y text que es una instancia en Text, ahora habrá que enlazar ambas con las instrucciones que aprendimos en la parte anterior, que enlazan el command del scrollbar con el método yview del Text y el yscrollcommand del text con el sbar.set, para que al mover uno, el otro se acomode y este sincronizado.
Bueno luego se realizan los packeos correspondientes y se asigna la instancia text a self.text para poder utilizarla fuera de makewidgets().
Vemos que una vez que termina con el método self.makewidgets() llama a self.settext() al cual le pasa como argumentos el texto o el nombre del archivo a mostrar. Entremos en dicho método.
Vemos que tiene valores por default para text='' y file=None en nuestro caso como lo corrimos sin argumentos text sera 'Words\ngo here' y file=None
Como en mi caso file=None pues no le pase un nombre de archivo como argumento, no entrara en el if file de haber existido entraría, leería el contenido del archivo y le asignaría a text lo que lee del archivo. No debemos confundir self.text con text, dentro de este método self.text es la instancia de Text y text es el texto que queremos escribir en ella, no ha sido afortunado ponerles nombres tan parecidos, pero bueno sigamos.
Luego le aplico a self.text los métodos delete() que borrara teniendo como argumentos donde queremos que comience a borrar y donde queremos que termine, en este caso borrara todo ya que desde '1.0' que es el inicio al END que es el final, dejara todo vacío. Luego mediante insert() agregara el texto que o bien leyó del archivo y esta en text o bien es el texto como en mi caso 'Words\ngo here'.
Luego utiliza el método mark_set() que es para marcar una locación con un nombre si allí en vez de INSERT que es un caso especial le hubiera puesto ('jose', '4.5') estoy marcando esa posición '4.5' con el nombre 'jose' así que para insertar ya no necesito usar el numero puedo hacerlo usando la marca 'jose' text.insert ('jose', 'manola') Insertara 'manola' en la posición marcada como 'jose' o sea la 4.5 INSERT es una etiqueta que se refiere a la posición del cursor en ese momento. Cuando usamos mark_set con INSERT en el caso del ejemplo. (INSERT, '1.0') significa que INSERT que es la posición del cursor actual se cambiara al inicio.
Se utiliza focus() para que no haya que hacer click en la ventana para poder escribir y quede el cursor titilando en la misma listo para tipear.
Vemos que ya sale de la clase y vuelve al nivel superior, allí define una función show que sera el callback de bind(), el cual capturara si apretamos la tecla escape y llamara a dicha función show. Esta capturará el texto de la pantalla con st.gettext() ya que st es una instancia de nuestra clase, así que podemos llamara a cualquier método de la misma, usando dicha instancia, en este caso llama al método gettext()
Que lee el texto desde el inicio hasta END menos un carácter.
Es un código permitido restarle caracteres a END en este caso -1c significa un carácter menos, es para quitar el carácter /n del final y imprime, esto es todo veamos si funciona desde la consola.
Allí no aparece el scrollbar porque el texto no es lo suficientemente largo, si achicamos la ventana.
Allí vemos que aparece la barra de scroll y cuando apretamos ESCAPE debería imprimirse el texto veamos.
Vemos que funciona perfectamente y quita el carácter \n final como habíamos visto. Existe la posibilidad de capturar fácilmente texto que ha sido resaltado con el mouse mediante.
text = self.text.get(SEL_FIRST, SEL_LAST) text = self.text.get('sel.first', 'sel.last')
Las constantes predefinidas SEL_FIRST, SEL_LAST se refieren al inicio y final de texto resaltado con el mouse, de esta forma podemos probar desde una consola fácilmente que capturamos el texto resaltado.
Tambien podemos crear nuestros propios TAGS sin necesidad de usar los de sistema Crea un tag para todo el texto que hay en el widget self.text.tag_add('alltext', '1.0', END) Usando el tag especial SEL, resalta el texto entre index1 y index2 desde el script self.text.tag_add(SEL, index1, index2)
. También tenemos el método search() para buscar la primera ocurrencia de una string dentro del texto.
text.search('ffffff', '1.0', END)
Vemos que me devuelve la linea 2 carácter 16 que es la primera ocurrencia de dicha string. Si hacemos un TAG desde alli al final del texto. text.tag_add('mytext', '2.16', END) Y se puede remover al tag dejando el texto con tag_remove y tag_delete borrara el contenido de dicho tag completamente. Bueno ahora usando los conceptos que vimos anteriormente haremos un editor de texto mas avanzado que se llamara simpleedit.py.
Bueno vemos que el código es ya mas complejo y largo lo estudiaremos.
Antes que nada para que funcione debe poder importar Quitter y Scrolledtext, ya que los utilizara, así que o bien ubiquenlos en la misma carpeta o en la de módulos de Python.
Vemos que si esta corriendo como principal llamara a la clase SimpleEditor la cual es un widget, a la cual le pasa como argumento el nombre del archivo de texto a mostrar que lo toma de los argumentos que debemos pasarle al script.
Vemos que dicha clase es un widget pues hereda de ScrolledText que era un widget independiente creado por nosotros, y dentro del método constructor de SimpleEditor, primero hace una instancia en Frame a la cual packea, y le agrega botones con el texto Save, Cut, Paste y Find, ademas de un botón Quitter para salir y los packea, luego llama al método constructor de ScrolledText, pasandole los argumentos necesarios como el nombre del archivo a mostrar ya que text al no pasárselo usara el valor por default text=''
Con esto ya habremos creado el mismo editor de texto del ejemplo anterior recordemos que el método constructor de ScrolledText ya llamaba a makewidgets() y a settext() de forma que esta creado en forma completa, lo único que no tendrá este, es la función show que se creaba como función callback de bind() solo si el script era corrido como principal, como no es este el caso, dicha funcionalidad no existirá.
Vemos que solo queda estudiar que ocurre al apretara cada botón para eso corramos antes el script y veamoslo en funcionamiento.
Vemos que es similar al ejemplo anterior tiene la scrollbar vertical, y tiene los botones que le agregamos arriba, veamos que ocurre apretemos el botón SAVE.
Vemos que se abre el dialogo de guardar veamos el código de la función callback de este botón Save.
Vemos que usa el dialogo que importo asksavefilename() que abre el buscador para que escribamos o busquemos el nombre del archivo a guardar, una vez que lo tipeamos usando el método gettext() leerá todo el texto y lo escribirá en una archivo usando el método open, y como argumentos el nombre que elegimos en el dialogo y el parámetro 'w' para que sea para escribir, y luego con write(alltext) le escribirá el texto que esta visible en el editor. O sea que el botón SAVE en resumidas cuentas guarda el texto en un archivo. Luego existe el botón CUT que es para cortar texto, para ello debemos marcar una sección de texto con el mouse.
Ahora si apretamos el botón CUT la corta, podemos probar que funciono pegándola al final con el botón PASTE.
Veamos las funciones callback de los botones CUT y PASTE.
Vemos que lee usando get() en los tags SEL_FIRST y SEL_LAST , el texto resaltado, luego lo borra con delete(), luego limpia el clipboard y le apendea el texto resaltado que corto usando.
Podríamos haber guardado el texto cortado en una variable de Python y luego pegarlo fácilmente pero al usar los métodos de clipboard podemos pegarlo en otros programas fuera del nuestro, lo cual es muy útil. La forma de manejar el clipboard en Tkinter es la siguiente: Para limpiar el clipboard self.clipboard_clear( ) Para guardar texto self.clipboard_append(text)
Para recuperar el texto del clipboard text = self.selection_get(selection='CLIPBOARD') Ahora si, vemos la función callback del botón Paste veremos que hace uso de esta ultima. Intenta leer el texto del clipboard y si hay lo inserta a partir de la posición del cursor (usa INSERT
que marca la posición del cursor actual como inicio de donde pegar) si no pueda va al except y ejecutara pass o sea nada, jeje. Nos queda el botón FIND al apretarlo nos muestra una caja de dialogo para que ingresemos el texto a buscar.
Veamos el código de la función callback de este ultimo botón.
Vemos que usa el dialogo askstring para pedirnos el nombre de la string a buscar, y usando el método search() la buscara a partir de la posición del cursor hasta el final. Luego vemos que usa pastit = where + ('+%dc' % len(target)) para hallar la posición final, usando el len(target) o sea el largo de la string buscada y por lo tanto mediante format string le agrega a where que es la posición donde encontró que se inicia la ocurrencia, la string '+ xc' o sea si el largo de la string buscada es 2 por ejemplo sera '+2c' de la misma forma que le restábamos a END -1c, ahora desde el inicio le sumamos el largo para hallar el final. self.text.tag_add(SEL, where, pastit) Luego usando tag_add y SEL que selecciona la string que encontró. self.text.mark_set(INSERT, pastit) aquí pone el cursor al final del texto seleccionado. self.text.see(INSERT) Y lo aquí hace visible y luego con focus lo pone en foco para evitar los clicks y que se pueda tipear.
Aquí tenemos un simple ejemplo en la consola.
Luego hacemos search() buscamos la string 'pepe' desde el inicio.
Vemos que la encontró en '3.7' o sea fila 3 carácter numero 7.
Allí le agregamos el 4c que es el largo de la string 'pepe'
Allí lo resaltamos usando tag_add y SEL desde el inicio al final de la string buscada, vemos que el cursor aun esta al inicio o donde estaba.
Allí con mark_set movimos el curso al final de la palabra buscada.
Y con see debería ponerse visible aunque no se aprecian cambios pero bueno quizás en consola eso no se vea.
Aquí probé el script comentando y descomentando la linea del see y no veo diferencia, y bueno lo veremos mas adelante, si eso tiene alguna otra influencia hasta aquí llegamos hoy.
Hasta la parte 49
49-CANVAS WIDGET PARTE 1 A medida que vamos avanzando en los ultimo widgets nos damos cuenta que estos son un poco mas complejos que un simple botón, o un Label, dado que tienen mas posibilidades, también tendrán obviamente mas métodos para poder hacer uso de ellas, pero bueno, la cuestión es ir conociéndolos y usándolos poco a poco, sin asustarse, para familiarizarnos con ellos, de esta forma cuando uno lo use en el futuro podrá volver a consultar en google o releyendo estos tutes, porque nadie se acuerda todos los métodos de memoria, pero recordará rápidamente lo ya leído. Como siempre para familiarizarnos podemos usar la consola de esta forma podemos ver que efecto producen cada uno de los comandos que tipeamos.
Allí tenemos la creación de un Canvas vacío, por supuesto habrá que importar Tkinter como siempre y luego realizar una instancia en Canvas, pasandole como argumentos el ancho y el alto, y el color de fondo en este caso blanco. >>> canvas=Canvas (width=300, height=300,bg='white')
De cualquier forma, como la mayoría de los widgets se puede realizar una instancia sin pasarle ningún argumento y luego configurarlo mediante config.
Allí vemos que lo configure para que sea de 500 por 500 y de fondo negro en este caso, las opciones del método config funcionan como en todos los widgets. También se puede reconfigurar si no nos gusto el negro y lo queremos cambiar al inicial, pues una nueva configuración machacara la anterior.
El widgets Canvas sabemos que es como una base para poder dibujar imágenes, ahora pongamos como ejemplo que quisiéramos trazar una linea dentro de el, bueno no hay que ser muy imaginativo el método se llama create_line, jejeje, si estamos usando PyScripter vemos los nombres de los posibles métodos La única diferencia que en PyScripter en modo interactivo no podemos ser tan explícitos como en la consola de comandos o en un script real, debemos crear una ventana raíz y allí agregar nuestro canvas, si no no lo veremos, seria como crear un botón suelto sin tener una ventana base, en la consola o en un script real no hay problema, porque toma todo por default, pero en modo interactivo hay que colocarlo en algún lugar bien determinado.
Allí vemos que lo creamos en forma similar solo que creamos una ventana raíz instanciando en Tk, y se la pasamos como parent al instanciar el canvas, luego la configuración y el packeo son similares. Solo lo arme en el PyScripter para que podamos ver los métodos que posee mediante la ayuda que tiene que es muy importante, veamos.
Allí vemos que tenemos muchísimos métodos para experimentar, y dentro de ellos create_line que va a ser el primero que vamos a elegir.
Cuando ponemos el paréntesis nos muestra la ayuda, allí dice que creara una linea y que hay que pasarle las coordenadas de inicio y final de la misma, pues lo hacemos.
Allí trazamos una linea cuyo punto de inicio esta en 100,100 y cuyo punto final esta en 200,200, como sabemos al configurar el canvas le pusimos tamaño 300 por 300 así que estará centrada.
Vemos que con la opción fill, podemos elegir el color de la linea, el próximo paso será agregar un ovalo jeje.
Es divertido probar y hay muchísimas opciones.
Allí creamos un circulo, ya que el inicio es 0,0 y el otro extremo estará en 20,20 y de color amarillo, podremos borrarlo?
Vemos que no es muy difícil ya que al crearlo nos devuelve el ID del objeto, el cual deberíamos guardar para poder borrarlo, lo haremos bien ahora.
Vemos que creamos un ovalo amarillo y que guardamos su ID por si queremos borrarlo.
Vemos que los argumentos se interpretan, como si el ovalo estuviera inscripto en un rectángulo, cuya diagonal empezaría en 100,100 en su extremos superior izquierdo y terminaría en 200,200 en su extremo inferior derecho, por eso al cambiar este ultimo punto podemos hacer óvalos en vez de
solo circulos.
Aquí lo borramos y cambiaremos el punto inferior derecho de la diagonal para hacer un ovalo.
Quiere decir que realmente estamos armando un ovalo o circulo, haciendo un rectángulo cuya
diagonal, configuramos poniendo un punto inicial y final, y dentro de ese rectángulo, se inscribirá el ovalo. Por supuesto crear un rectángulo es similar, jeje, se configura la diagonal, en este caso el punto de la diagonal esta en 200,0 y el otro en 300,10, de forma que lo ubico fácilmente donde yo quiero.
Vemos que arc crea un cuarto de circulo, toma como posición inicial en este caso 0,0 y el final 200,200, como si fuera un circulo completo y solo muestra ese arco del mismo. Bueno pueden seguir probando , pero es hora de un script lo llamaremos canvas.py.
Vemos que agrega un gif dentro del canvas recordemos que dentro del script esta el path al gif, así
que verifiquemos si ese es un path correcto a un gif, si no dará error.
Aquí tampoco tenemos el problema de tener que especificar la ventana raíz como en el caso de PyScripter actuando en forma interactiva, si no lo hacemos sabemos que se crea por default, por supuesto si corremos este script en PyScripter funciona perfectamente, solo que al usarlo en forma interactiva no puede suponer o tomar la ventana por default Tk, así que en ese caso hay que crearla para que se vea, aquí no es necesario. Vemos que luego de instanciar en Canvas pasandole el tamaño que aquí es 300 por 300 y el fondo que es blanco, y packear, le agrega lineas como vimos en nuestro ejemplo interactivo, dentro de un for, crea mas lineas tomando i los valores en cada ciclo. Luego creara un ovalo, como vimos su diagonal comenzara en 10,10 y terminara en 200,200 y sera azul, crea también un arc que en nuestra imagen no se ve porque queda tapado por el gif, también crea un rectángulo, y luego haciendo una instancia en PhotoImage del gif que quiere mostrar, lo agrega con create_image, poniéndole donde se ubicara y la instancia de la imagen con image=photo, luego crea un Label, haciendo una instancia en dicha clase y le coloca el texto Spam, luego lo embebe con create_window y le agrega un text con create_text. Es bueno aclarar que estas lineas, óvalos, rectángulos, no son widgets, son objetos que como vimos tienen un numero identificatorio o ID. Por lo tanto resumiendo vemos que agregamos los siguientes objetos
id = canvas.create_line(fromX, fromY, toX, toY) id = canvas.create_oval(fromX, fromY, toX, toY) id = canvas.create_arc( fromX, fromY, toX, toY) id = canvas.create_rectangle(fromX, fromY, toX, toY)
# linea inicio, final # dos esquinas opuestas # dos esquinas opuestas # dos esquinas opuestas
id = canvas.create_image(250, 0, image=photo, anchor=NW) id = canvas.create_window(100, 100, window=widget) id = canvas.create_text(100, 280, text='Ham')
# embebemos una photo # embebemos un widget # escribimos texto
Esta claro que dos esquinas opuestas se refiere a una diagonal imaginaria que en el caso de un ovalo, seria la diagonal del rectángulo donde estaría inscripto. También canvas tiene un método llamado create_polygon que sirve para crear formas arbitrarias con puntos de inicio y final de las rectas del borde del mismo. Vimos que cada objeto tiene un identificador, el mismo se usa para diferentes operaciones ademas del borrado o delete como vimos hasta ahora. canvas.move(objectIdOrTag, offsetX, offsetY) Vemos que mueve el objeto del ID especificado un desplazamiento x.y que le pasamos. Si teníamos guardado el ID en la variable id4 seria canvas.move (id4, 100,0) en este caso moveríamos el objeto de id4, 100 en la dirección horizontal. canvas.delete(objectIdOrTag) canvas.tkraise(objectIdOrTag) canvas.lower(objectIdOrTag) canvas.itemconfig(objectIdOrTag, fill='red')
# borrar objeto que posea ese id # traer el objeto al frente # poner detrás de otros # llenar el objeto con dicho color
Bueno aquí terminamos la primera parte de canvas en la siguiente continuaremos con este widget tan interesante. Hasta la parte 50 Ricardo Narvaja
CANVAS PARTE 2 En la parte anterior vimos lo básico sobre el widget canvas, ahora veremos algunos puntos mas que quedan, para terminar con el. Los canvas se pueden agrupar e identificar por tags aquí vemos algunos ejemplos de como hacerlo. Para asociar un objeto con un tag solo debemos pasarle al crearlo, entre los parámetros de su configuración el argumento tag='xxxxxx' con lo cual ese objeto quedara marcado con dicho tag o como perteneciente al grupo xxxxxx. canvas.create_oval(x1, y1, x2, y2, fill='red', tag='bubbles') canvas.create_oval(x3, y3, x4, y4, fill='red', tag='bubbles') Aquí vemos la creación de dos objetos óvalos, en los cuales al pasarle dentro de su configuración tag='bubbles', automáticamente los hacemos a ambos pertenecer a dicho grupo con el nombre bubbles. Luego cualquier operación de mover, borrado, etc puede ser realizada a todo el grupo, por ejemplo para mover todos los objetos taggeados del grupo bubbles. canvas.move('bubbles', diffx, diffy) Si recordamos la definición general que vimos en la parte anterior de move. canvas.move(objectIdOrTag, offsetX, offsetY) Vemos que cualquier operación como por ejemplo en este caso move, puede realizarse sobre un objeto mediante su ID o sobre un TAG afectando a todo el grupo que pertenece al mismo. Una forma de añadir un objeto a un grupo es la siguiente: objectId = canvas.create_oval(x5, y5, x6, y6, fill='red')
#creo un objeto
canvas.addtag_withtag('bubbles', objectId)
#lo añado a un grupo existente
De esta forma creamos un nuevo objeto, y guardamos su ID, y luego mediante el método addtag_withtag, lo agregamos a un grupo, de cualquier forma creo que utilizar dos pasos cuando se puede en la configuración del mismo añadirlo a un grupo solo agregando: canvas.create_oval(x5, y5, x6, y6, fill='red', tag='bubbles') Es mucho mas breve y fácil definirlo allí, que usar el método addtag_withtag, pero bueno es mejor conocer todas las formas. Otro método para ubicar un objeto en un canvas es find. canvas.find_closest(X,Y) El cual pasandole las coordenadas X e Y de posición, nos devolverá el ID del objeto que se encuentra en ellas.(o el mas próximo)
Allí vemos la creación de un canvas sencillo desde la consola, y taggeamos el ovalo que creamos en el mismo con el tag bubbles. Podría moverlo, borrarlo etc por medio de su tag como vimos.
Si usara find pasandole las coordenadas donde se encuentra.
Vemos que find me devolverá el objeto mas cercano a las coordenadas que estoy pasando si hubiera dos objetos, siempre me devolvería el id del mas cercano. AGREGANDO SCROLL A UN CANVAS En el siguiente script veremos que agregar scroll a un canvas no difiere de lo hecho para otros widgets a los cuales se los puede relacionar con una barra de scroll, se llamara scrolledcanvas.py.
Vemos que no se aleja de los otros widgets que hemos hecho portables, tiene su clase que aquí se llama ScrolledCanvas y que hereda de Frame como ya sabemos, luego de llamar al método constructor de Frame, lo packeará y creara una instancia en Canvas, a la cual mediante el método config, le seteará el color, el tipo de borde SUNKEN, el ancho y el alto y un par de configuraciones especiales para un canvas que ya veremos.
Como hemos hecho en los widgets que necesitaban scroll, creamos una instancia en la clase ScrollBar y usando los métodos cruzados yview y sbar.set se relacionan para que cuando se mueva uno se ajuste el otro automáticamente, vemos que es similar a los otros widgets con scroll, el sistema es el mismo. Luego vemos que dentro de un for crea 10 objetos de texto, con la string 'spam' mas el numero de
ciclo que esta loopeando y lo llenara de color beige. Luego usara bind, para crear una función callback, para que cuando hagamos doble click se la llame esta se llamará onDoubleClick y esta definida justo abajo.
Vemos que dentro de la función callback imprimirá la información de la posición de donde clickeamos, veamoslo correr luego aclararemos lo que falta.
Allí esta nuestro canvas por supuesto si lo estiramos podremos ver mas objetos.
Y por supuesto hacer scroll con la barra que colocamos para ello.
Veo que al hacer doble click me imprime la info de las coordenadas donde clickee.
Bueno veamos lo que faltaba, nosotros podemos definir dos tamaños, el tamaño total que tendrá el canvas y la zona que se mostrara de el inicialmente. La zona del canvas que se muestra al inicio es solo una porción del canvas total, el cual podemos navegar con la barra de scroll, mediante width y height seteamos que porcion del canvas inicialmente se vera.
Allí agrandamos en forma vertical el alto de la porción inicialmente visible. Por otro lado tenemos el seteo del tamaño total del canvas, esta debe ser por supuesto mayor que la zona inicialmente visible para que la barra de scroll tenga sentido, si ambas son iguales, se vera todo al inicio y no seria necesario una barra de scroll. Por lo tanto scrollregion setea el tamaño total del canvas, en este caso sera un rectángulo cuya diagonal ira desde 0, 0 hasta 300,1000 (o sea de 300 por 1000 en total ) y de toda esa zona, solo inicialmente veremos una parte de 300 por 200 seteado por widht and height. Otro tema interesante es que la posición que obtenemos al doble clickear print event.x, event.y imprime el lugar donde se hizo doble click en la ventana no importando la barra de scroll siempre sera relativo a la ventana visible, pero print self.canvas.canvasx(event.x), self.canvas.canvasy(event.y) es la posición de nuestro punto de clickeo dentro del canvas total, solo coinciden ambos cuando la barra de scroll esta arriba si la bajamos vemos que ambos difieren considerablemente.
Si clickeo en la p de spam0, veo que la posición de las coordenadas x e y dentro de la zona visible coinciden con la posición dentro del canvas total, pero si muevo la barra totalmente hacia abajo y clickeo en la p de spam8.
Veo que la primera linea nos muestra siempre la posición dentro de la zona visible, que no es muy
diferente a la primera linea anterior, pero la segunda nos muestra las coordenadas x e y dentro del rectángulo total de nuestro canvas por lo que difiere completamente de la segunda linea anterior, al haber movido la barra de scroll. Creo que con esto agotamos el tema Canvas casi ya estamos llegando al fin de nuestro viaje por el conocimiento de todos los widgets y por suerte ya falta poco para empezar a usarlos en conjunto.
Hasta la parte 51 Ricardo Narvaja
GRID Hemos visto muchas clases de widgets y vimos como siempre usábamos pack, para llamar al manejador de geometría y ubicaba el widget en una zona definida por nosotros, en la parte vacía de la ventana que quedaba en ese momento, pero hay otra forma mas precisa de hacer esto y es usando grid, con lo cual usaremos columnas y filas en la ventana donde lo vamos a ubicar. Al momento de escribir el libro donde nos basamos, grid y pack eran completamente excluyentes de forma tal que si en una ventana se aplica un método, no se puede usar el otro, a no ser que hagamos ciertos manejos que veremos mas adelante en esta misma parte. Veamos un ejemplo usando grid, primero lo haremos fácilmente en la consola, la cual nos da esa idea interactiva, que a todos nos gusta.
Allí vemos que colocamos un Label con el texto 'pepe' le dijimos el tamaño que queremos que tenga,. Pero en vez de packearlo, le aplicamos el método grid el cual le podemos pasar la columna y fila donde queremos ubicarlo, como por ahora hay una solo Label, no hay problema, ahora agregaremos un segundo abajo de este o sea sera row=1, column=0 (fila 1 columna 0)
Allí lo agregamos ahora, pondremos uno al lado de este ultimo en la columna 1 fila 1.
Vemos que lo va ubicando como si fuera una tabla.
Vemos que si agrando el tamaño, se acomodara con un nuevo ancho de columna.
Allí agrego una mas pequeña, jeje, bueno el script que hace esto es este solo quería mostrar un poco la idea antes, se llama grid.py.
Vemos que crea una lista con nombres de colores y dentro de un for coloca un Label y un Entry en la misma fila pero en diferentes columnas contiguas, veamoslo en acción.
Vemos que el label tiene el nombre del color y un ancho de 25 y se ubica en la primera fila primera columna, mientras que su Entry asociado tiene como color de fondo (bc) el mismo y tiene como ancho 50 y esta en la fila 0 columna 1, y así se construyen dentro del for, pares de Label y Entry ubicados de esta forma y identificados por el texto del Label que sera el color de fondo del Entry. Ahora en el ejemplo siguiente haremos una comparación haciendo lo mismo usando pack y grid, es de notar que se aplicaran en ventanas Top Level independientes ya que no pueden coexistir en la misma, este script se llamara grid2.
Aquí tenemos definidas dos funciones una llamada gridbox que creara una ventana Top Level usando grid como manager y la otra llamada packbox usara pack para lo mismo. Vemos que al llamar a estas funciones se realiza dentro del mismo argumento, la instancia en Top Level.
Y esa instancia de TopLevel sera la que use como parent o sea que allí ubicara los Label y Entry cada uno en su instancia de Top Level que creo independiente del otro. Vemos que la función que usa grid es similar al ejemplo anterior dentro de un for colocara los Label y los Entry y los ubicara en la misma fila pero en columnas contiguas. La que usa pack, creara un Frame en cada ciclo del for, para poder ubicar ambos el Label y el Entry, y tiene que realizar tres llamadas a pack, una para el Frame que le diremos que lo ubique TOP, y dentro de el ya que lo pasamos como parent en la creación del Label que ira a la izquierda LEFT y el Entry a la derecha RIGHT. También en la ventanita principal crea un Botón para salir del script. Corramoslo, pero ya vemos que grid es mas practico y sencillo para ubicar cosas en formatos tipo tabla.
Vemos que el resultado es similar, pero con grid no fue necesario usar Frame, y se acomodan directamente sin tener tanto problema. Como dijimos no podemos combinar el uso de grid y pack en una misma ventana, la única excepción seria metiendo cada una en un Frame, para que estén como widgets independientes y no tengan problemas entre si, el script siguiente hará eso,.
Vemos que aquí se crea un Label con el texto Grid y se instancia en Frame, y dicha instancia es pasada como parent a la función que importo del ejemplo anterior gridbox, lo mismo hará con la otra función packbox, ambos los ubicara en la misma ventana root, pero dentro de Frames diferentes, de forma de utilizarlos como widgets independientes así no se molestan entre si.
Vemos que así funciona, si quitamos la instancia en Frame y los agregaremos directamente a la ventana root.
Vemos que sin dar error, queda RUNNING y nunca llega a abrir, hasta se puede llegar a colgar Python, así que debe tenerse cuidado, cada Frame sabemos que es como una caja que contiene uno o mas widgets, y de esta forma se aíslan entre si, sin usar Frame no podemos utilizarlos en una misma ventana. En el siguiente ejemplo llamado grid3.py veremos como se hace para configurar un grid para que sea expansible, y la comparación con pack que ya es conocida por nosotros.
En el caso del que utiliza pack, ya sabemos que haciendo que el Frame sea expansible agregándole expand=YES, fill= BOTH nos aseguramos que sea expansible en ambos sentidos x e y. En el caso de grid es parecido pero se realiza utilizando lo siguiente:
Allí vemos que hay que configurar cada columna y linea nueva como expansible, eso se hace llamando a rowconfigure o columnconfigure con el indice de linea o columna, y pasandole weight diferente de cero, cualquier valor que no sea cero hará dicha fila o columna expansible. En este caso como dentro del loop se crean nuevas filas, se agrega una opción para que a cada fila creada se la transforme en expansible. root.rowconfigure(r, weight=1) y luego como hay solo dos columnas a cada una de ellas se la configura también como expansible. root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) Veamos correr el script
Así esta cuando lo arranco ahora lo expandiré.
Funciona, jeje se expande perfectamente en ambos sentidos.
Hay una opción mas que no comentamos que es NSEW, y significa los puntos cardinales para donde se puede expandir, puesto que al agrandar uno puede querer que se expanda solo en dirección vertical (NS o NORTE SUR) o horizontalmente (EW o ESTE OESTE) o cualquier combinación de puntos cardinales que uno quiera, de esta forma puede hacer que una fila por ejemplo solo se expanda en forma vertical y mantenga su ancho, en este caso al poner NSEW le permitimos expandirse en todas direcciones. El método span que se usa tanto para filas y columnas columspan o rowspan lo que hace es agregar una columna sin precisar la ubicación o sea seria equivalente al TOP de cuando usamos pack, la ubica en la fila o columna en la parte superior respetando las columnas y filas ya puestas o sea adicionándose a continuación.
El siguiente ejemplo es una tabla sencilla usando grid
Creo que ni hace falta explicarlo jeje corramoslo.
El siguiente script es parecido solo que usa Entries
Crea Entries y hace una tabla con los mismos, a cada uno le coloca el texto de la posición i.,j donde esta ubicado mediante insert como vimos en la parte de entries, y luego en una lista vacía llamada rows, y en otra llamada cols, guarda apendenado todas las instancias de Entry para luego en un botón que tiene el texto FETCH se llame a su función callback onPress(), la cual imprimirá el contenido de todos los entries en un doble for, usando get() en cada instancia de Entry para obtener el texto que tiene cada uno, veamoslo correr.
Al apretar el botón debería imprimir todos los contenidos de los entries incluso si cambiamos alguno.
Si tipeo algo en un Entry sera capturado al apretar el botón nuevamente
Vemos que también como el botón lo ubicamos usando grid (ya que no podemos usar pack) y no especificamos nada lo coloco en la próxima casilla vacía de la tabla, si le decimos en que fila y columna ponerlo.
Creo que con esto estamos claros de como funciona grid, si necesitamos hacer tablas o ubicar cosas en una forma diagramada de columnas y filas, usar grid es mucho mas fácil que usar pack, si no para el resto se sigue usando pack como hasta ahora. Hasta la parte siguiente Ricardo Narvaja
ALGUNAS HERRAMIENTAS DE TKINTER-AFTER-PAUSAS Ahora que ya terminamos de ver todos los widgets y antes de ver algunos ejemplos, nos quedaría ver algunas herramientas adicionales de Tkinter que sirven en programación en casos reales, veremos algunas herramientas interesantes y algún ejemplo de como usarlas. La primera herramienta interesante es la que Tkinter nos provee para realizar una acción luego de un determinado tiempo, muchas veces una GUI necesita esperar cierto tiempo para ejecutar una acción como por ejemplo, titilar un botón después de tantos segundos, y con esta herramienta podemos tener una función callback que puede ser llamada por tiempo, realmente esta función no pausa el flujo normal del programa el cual sigue ejecutándose, solo que la función callback se disparara luego de que pase el tiempo que configuramos en ella. Aquí vemos el método after que puede ser aplicado a cualquier tipo de widget, sus argumentos son el tiempo en que tardará en dispararse su función callback y el segundo argumento será el nombre de dicha función, tiene un tercer argumento que puede usarse para pasarle argumentos a la misma. widget.after( milliseconds, function, *args) Si no se le especifica una función, se comportara en ese caso como una pausa del programa, idéntica a time.sleep, el programa se pausará y luego del tiempo especificado continuará corriendo, un argumento de 5000, pausará el programa por 5 segundos. widget.after( milliseconds) Una variante que nos provee Tkinter es after_idle que nos da la posibilidad de que se ejecute una función cuando la GUI no esta realizando ninguna tarea, ni tiene eventos pendientes que procesar, es útil para tareas de indexado, que normalmente se realizan cuando el usuario no esta usando el programa y este quedo activo. widget.after_idle( function, *args) Como todos estos eventos devuelven un ID se pueden también cancelar usando el mismo. widget.after_cancel( id) Hay varias opciones mas de pausa son las siguientes: widget.wait_variable(var)
widget.wait_window(win)
widget.wait_visibility(win) La primera esperara hasta que una variable Tkinter cambie de valor, la segunda hasta que una ventana de sea destruida y la tercera hasta que una ventana sea visible. Veremos un ejemplo usando el método after se llamará alarm.py.
Vemos que el ejemplo tiene una clase llamada Alarm que hereda de Frame y en su método constructor luego del llamado al método constructor original de Frame para inicializarlo y el consabido packeo, define algunas variables, como por ejemplo la cantidad de milisegundos que se puede pasar como argumento, aunque tiene un valor por default de 1000 milisegundos. Luego agrega un botón instanciando en la clase Button, este botón tendrá como texto la frase 'Stop the beeps' y su función callback será self.quit, por lo cual al apretarlo se cerrara el programa. Luego del packeo del botón y algunas configuraciones del mismo realizadas mediante config como el color de fondo y el de la fuente y un borde para que no quede pegado al margen. Luego para poder usar la instancia stopper del botón dentro de toda la clase se asigna a self.stopper y finalmente el programa llama al método self.repeater() el cual esta definido dentro de la clase y es un atributo de ella.
Allí vemos que dentro del método se llamara a self.bell() que sonará una campana, luego le aplicará a la instancia del botón stopper el método flash() que lo hará titilar y allí terminaría todo, ya no habría mas posibilidades de ejecutar nada, pues el botón solo tiene como función callback la que
cierra el programa, y el método repeater() ya habría sido ejecutado, pero la siguiente linea es la que usa el método after, que es el que hará la magia, jeje. self.after(self.msecs, self.repeater) Pone como evento a ejecutarse luego de 1000 milisegundos, a la misma función repeater, la cual es función callback aquí de after, y con ello luego de que transcurra el tiempo se volverá a ejecutar una vez mas, pero al volver a ejecutarse, al final vuelve a ponerse nuevamente como función callback a ejecutarse luego de un tiempo y así se repetirá hasta que pulsemos el botón y cerremos el programa. Es de notar que usando el método after para colocar un evento a realizarse en un lapso de tiempo, hace que este se coloque una sola vez, no se repetirá por si sola, el efecto de repetición se obtiene al estar dentro de la misma función que será su callback, haciéndolo recursivo. Probemos ejecutarlo a ver si funciona:
Vemos que cada tanto suena la campana y flashea el botón repetitivamente, al apretar el botón stopper el programa termina como habíamos analizado, también se puede llamar como es una clase desde otro script, importándola y luego llamándola pasandole el tiempo que queremos que tarde en repetirse el ciclo, ya que es un argumento valido. Si esta clase estuviera usada como widget en una GUI mas compleja, la misma nunca interrumpiría su trabajo normal, seguiría trabajando y cuando el tiempo de que el evento que esta esperando llega, se dispararía sin interrumpir el trabajo de la misma. Acá veremos una variante del script de alarma.
Vemos que este importa alarm o sea el modulo del ejemplo anterior y creara una nueva clase Alarm que hereda de la clase del anterior que en este caso se llama alarm.Alarm ya que fue importada con import para poder diferenciarla de la de este script que se llama igual. Quiere decir que como esta clase ya hereda la instanciacion en Frame, el packeo, aquí no tenemos necesidad de hacerlo solo como heredamos de alarm.Alarm debemos satisfacer su clase constructora, lo hacemos aquí:
Vemos que dentro del método constructor de nuestra clase llamamos al de la superclase alarm.Alarm, de esta forma la inicializamos en forma correcta, la misma en su método constructor instancia en Frame y realiza toda la construcción de la GUI inclusive la instanciacion del botón stopper y su configuración. También nuestro script inicializa una variable llamada self.shown que comienza estando en cero
En nuestra clase Alarm customizamos la función repeater de forma que para nuestra clase, lo que hará la misma sera primero sonar la campana,
Luego chequea el estado de la variable self.shown si es verdadera, le quita el packeado con self.stopper.pack_forget() que hace que desaparezca el packeado del botón y por supuesto desaparecerá el mismo, y si es falsa irá al else donde se packeará y se pondrá visible nuevamente. Luego vemos que el estado de self.shown se cambia con self.shown=not self.shown invirtiendose para que al chequear nuevamente, entre alternativamente una vez a packear y la vez siguiente a quitar el packeo para hacer invisible y visible alternativamente. Luego al igual que en el ejemplo anterior se coloca como evento la función callback self.repeater aquí self.after(self.msecs, self.repeater) Lo que hará que se repita incesantemente, hasta que apretemos el botón stopper, probemos a ver como funciona.
Vemos que el botón aparece y desaparece, por supuesto sonando la molesta campana jeje. En este ejemplo llamado alarm-withdraw.py la que titila es la ventana completamente
También importa alarm y igual que el anterior redefine repeater comprobando si la ventana esta visible con master.state()='normal' ya que el método state retorna o cambia el estado de la ventana que puede ser (normal, iconic, zoomed [full screen], or withdrawn) Si esta normal dentro del if la oculta con self.master.withdraw() if self.master.state( ) == 'normal': self.master.withdraw( )
# esta la ventana visible?
Al else lo llama solo si la ventana no esta en estado normal, y en ese caso la vuelve a activar usando self.master.deiconify( ) self.master.lift( )
# else la redibujo # y la coloco sobre las otras
Allí lo corro y la ventana aparece y desaparece en forma alternativa según el tiempo que le pase como argumento y que usara el método after para setear la función callback repeater. Bueno creo que es suficiente por hoy ya aprendimos a usar el metodo after y algunas posibilidades de pausa en nuestras GUI. Hasta la parte 53 Ricardo Narvaja
53-MOVIENDO Y ANIMANDO DENTRO DE UNA GUI En esta parte 53 veremos como mover partes de un canvas para que parezca una animación, mediante diferentes técnicas, el primer ejemplo es sobre la primera de ellas y se llama canvasDraw.py.
Aquí tenemos el primer ejemplo, si lo corremos vemos una ventana vacía, pero esta preparado para realizar ciertos eventos según lo que hagamos con el mouse. Para borrar todo lo que se creo en el canvas, el script captura los doble click del botón izquierdo y con ello llama al método delete de canvas que borra todos los objetos que hay en la pantalla mediante tags. Clickeando el botón izquierdo y manteniendo apretado, podemos crear un rectángulo o un ovalo
como vemos en la imagen.
Por ultimo cuando clikeamos con el botón derecho automáticamente se mueve el ultimo objeto creado a la zona donde clickeamos, veamos como realiza todo esto el script. Primero realiza una instancia en nuestra clase llamada CanvasEventsDemo.
Al instanciar en nuestra clase, se llama al método constructor de la misma o __init__
Aquí se crea una instancia en Canvas con el ancho y alto de 300, y color de fondo beige, y luego se packea. A continuación se utiliza el método bind para capturar los clickeos del mouse, vemos allí cuatro instrucciones, cada una tiene su función callback.
. Allí vemos la instrucción sombreada en amarillo que captura el click del botón izquierdo y llama al método onStart(), la que esta sombreada en celeste captura el movimiento arrastrando o drag y llama al método onGrow(), la que esta sombreada en verde captura el doble click izquierdo y llama a onClear() y la que esta sombreada en rosado llama al método onMove() al clickear el botón derecho. Aquí vemos las constantes que se crean, que se usarán dentro de los métodos, ademas vemos la asignación de la instancia recién creada canvas a self.canvas para poder ser utilizada dentro de toda la clase y la creación dentro de una lista de dos elementos llamada self.kinds donde guarda los nombres de los métodos que crean el ovalo y el rectángulo, sin llamarlos (no realiza el () ), solo guarda los nombres de los mismos en una lista. >>> self.kinds [, ]
Bueno ya sabemos como es llamada cada función callback mediante bind y que deberían hacer, miremos un poco como hace cada una su tarea.
La función onStart(), en la primera instrucción y mediante indice toma de la lista el nombre del método para crear el ovalo y lo asigna a self.shape. >>> self.shape En la siguiente linea los invierte o sea que self.kind queda ahora con su primer campo con el método del rectángulo y el segundo el del ovalo, para alternar en la creación de uno o otro. >>> self.kinds [, ] Luego en la siguiente linea asignamos event a self.start, recordemos que una función callback de bind recibe siempre como argumento la instancia event, de la cual se pueden extraer los datos del evento que acabamos de capturar con bind, su posición etc.
Luego de almacenar la instancia del evento inicial en self.start, asigna a self.draw= None y continua corriendo luego de terminar el método onStart, por supuesto como nosotros comenzamos a arrastrar sin soltar el botón del mouse ahora es llamado mediante bind el siguiente método onGrow().
Vemos que usando widget puede recuperar la instancia del canvas original, ya que sirve para recuperar la instancia del widget donde se genero el evento, podemos comprobarlo fácilmente en el DebugProbe. >>> self.canvas==canvas True Es lo mismo en self.canvas tenia guardada la instancia original del canvas, y ahora la recupera gracias a event, lo cual no era necesario pues podía utilizar directamente self.canvas, vemos que ambas son iguales al respondernos True la verificación de igualdad.
Luego si self.drawn es diferente de None, entra allí al if a borrar el objeto, pero como por ahora es None, pues no ha sido terminado de crear continua en la siguiente linea, vemos cual es el truco, el de borrar y crear un objeto nuevo, para dar la sensación de movimiento.
>Luego configura la forma utilizando self.shape que tenia guardado el método para crear un ovalo o rectángulo según el caso, como es la primera vez será un ovalo, usando como punto de inicio las coordenadas que obtiene de self.start de cuando hicimos click la primera vez, y de event consigue la coordenadas del evento actual que al haber sido arrastrado por el mouse ya son diferentes, de esa forma se crea un ovalo o rectángulo, de acuerdo a esas medidas y se guarda el ID del objeto. Ahora es llamado el método de crear un rectángulo o ovalo, vemos que self.shape es llamado pasandole la configuración necesaria, llamando a la función con ().
De esta forma vemos que la próxima vez que entre si seguimos arrastrando, se borrara el objeto anterior ya que self.drawn como guarda el ID sera diferente de None y creara un objeto nuevo con las coordenadas iniciales que siempre son constantes y las actuales del evento que a medida que vamos moviéndonos va creciendo mas y mas. Los otros dos métodos son mas sencillos, onClear() borra todos los objetos creados usando el tag 'All' que directamente agrupa a todos los objetos existentes.
Vemos que lo que realiza al capturar el click del botón derecho es muy parecido, en canvas tendrá la instancia del canvas original, y calcula la diferencia entre la posición actual donde clickee y la posición inicial y lo guarda en diffx y diffy, y utiliza move para moverlo ya que self.drawn tiene el ID, el cual se pasa como primer argumento, para mover el ultimo objeto creado.
Ahora veremos otro ejemplo mas avanzado que utilizará el script anterior se llama canvasDraw_tags.py.
Aquí tenemos el ejemplo veamos un poco que hace y como funciona, primero ejecutemoslo, recordemos que debe poder encontrar al anterior ya que lo importa, el menos deben estar en la misma carpeta.
Si creamos un ovalo
y apretamos la o comienza a moverse el ovalo y si es un rectángulo se comienza a mover con la r.
Vemos que este importa la clase del ejemplo anterior para usarla como superclase y crea una nueva con el mismo nombre, la del ejemplo anterior aquí al ser importada se llama canvasDraw.CanvasEventsDemo y la nueva se llama CanvasEventsDemo solamente. Siempre que creamos una clase que hereda a otra, si la nueva tiene un método constructor, debe llamarse en el, al método constructor de la superclase, para poder satisfacerla y que se cree en forma correcta.
O sea que ya al llamar al método constructor de la superclase, tenemos en funcionamiento, lo que vimos en el ejemplo anterior, el bind a los diferentes clicks del mouse y los métodos preparados para recibirlos como funciones callback.
Luego agrega mediante create_text, el texto para aclarar que apretando las teclas o y r se pueden mover los objetos creados, y crea dos nuevas instrucciones para capturar usando bind, segun se aprete la tecla r o la o, cada uno de ellos con su función callback correspondiente, al apretar la tecla o se llama a self.onMoveOvals y la tecla r llama a self.onMoveRectangles.
También vemos que machaca self.kinds en este caso en vez de guardar los nombres de canvas.create_oval y canvas.create_rectangle como hacia el anterior, guarda dos los nombres de dos métodos propios que define allí.
Recordamos que solo guarda los nombres de los métodos, estos no son llamados desde aquí pues no es pasado argumento alguno ni es usada la función con (). Por supuesto estos métodos están definidos y cuando la superclase vaya a capturar los eventos se encontrara que deberá usar estos en vez de los originales.
Aquí vemos el ejemplo anterior, al clickear con el botón izquierdo, tomaba los nombres de los métodos a utilizar para crear los óvalos y rectángulos de la lista self.kind que como la machaque, ahora usará los nuevos definidos en la clase nueva. Cuando aquí llame a crealos
realmente self.shape tendrá el nombre de uno de los métodos nuevos y le pasara las coordenadas para crear los rectángulos y los óvalos a ellos.
Aquí están y recibirán las coordenadas para crear en cada caso el ovalo o el rectángulo llamando a la create_oval o create_rectangle de canvas, guardando el ID del objeto creado y configurándolo, para que se pinte de azul si es ovalo y rojo si es rectángulo, ademas de crear un tag, de forma que todos los óvalos creados tendrán el tag 'ovals' y los rectángulos tendrán el tag 'rectangles'. Después lo único que nos queda es analizar los métodos que se disparan al apretar la tecla r y la tecla o, que son los siguientes,
Vemos que ambos cuando son llamados usan el tag, para llamar a un método moveInSquares, que hará un for que moverá los objetos tagueados y repetirá el canvas para que se redibuje cinco veces, le agrega a esto una pausa mediante time.sleep que hace que no sea tan brusco el movimiento, pero no molesta ni pausa la GUI. El siguiente ejemplo importa el anterior y le realiza pequeñas modificaciones se llama canvasDraw_tags_after.py
Vemos que en este caso no tiene un método constructor propio solo agrega o modifica algunos métodos, o sea que al instanciar sera similar al caso anterior solo que cambiamos el método moveInSquares() La diferencia es que vemos que aquí utiliza after para colocar eventos que se ejecutaran luego de cierto tiempo como vimos en la parte anterior, al llamarse recursivamente, se vuelve a repetir la función callback de after, pero esta limitada porque el if que chequea el valor de moremoves., que al usar indice slice se va achicando y solo puede repetirse cinco veces. >>> allmoves = [(+20, 0), (0, +20), (-20, 0), (0, -20)] * 5 >>> print allmoves [(20, 0), (0, 20), (-20, 0), (0, -20), (20, 0), (0, 20), (-20, 0), (0, -20), (20, 0), (0, 20), (-20, 0), (0, -20), (20, 0), (0, 20), (-20, 0), (0, -20), (20, 0), (0, 20), (-20, 0), (0, -20)]
Como allmoves se asigna a moremoves al llamar a la función moveEm(), se asignará a diffx=20 y diffy=0 la primera vez y se quitarán dichos valores de moremoves para que la vez siguiente tome el siguiente par, así hasta que se acaben y moremoves quede vacío y terminará de moverse ya que no se repetirá la instrucción after, al no entrar en el if.
De esta forma los objetos giran 5 veces antes de parar, por supuesto podemos moverlos eso es parte del primer script, al clickear con el botón derecho moveremos el ultimo creado a otra posición. El ultimo ejemplo es usando threads se llamará canvasDraw_tags_thread.py
Vemos que esencialmente es lo mismo importa canvasDraw_tags y modifica el método moveInSquares creando un nuevo thread el cual llama a la función moveEm() que dentro de un for repite el movimiento cinco veces de los objetos tagueados, y ademas agrega un time.sleep para regular la repetición que no sea muy brusca, como la función se ejecuta en un thread separado, no afecta la GUI principal. Vemos ademas que en el ejemplo que usa after como en el ejemplo que usa threads podemos mover los óvalos y los cuadrados a la vez ya que son threads diferentes y eventos que disparan la función callback luego de cierto tiempo sin bloquear la GUI, mientras que en el que usa tags, si movemos los óvalos se detienen los rectángulos, pues es una sola función que se maneja con tags, o mueve los óvalos o mueve los rectángulos, no puede hacer ambas cosas a la vez.
Bueno seguiremos en la parte 54 Hasta la próxima
ETAPA 3: PARTE 1-INTERNET PROGRAMMING Llegado a este punto se me planteo una disyuntiva, la idea es estudiar y explicar todo el libro de PROGRAMMING PYTHON DE MARK LUTZ y eso se hará, pero como vemos en el indice del mismo, ya vimos dos capítulos enteros dedicados al tema GUI los que están en amarillo que son lo básico, y los que están en celeste son ejemplos de programas avanzados que seria lo que correspondería venir según el orden, a continuación.
Creo que hacer cuatro capítulos seguidos dedicados al tema GUI es un poco denso, por lo cual saltearemos esos dos capítulos, para ir a la etapa 3 de programación de Internet, sockets, mails, ftps, que creo que es muy interesante también, y al final de todo haremos los dos capítulos que quedaron de GUI, o sea no estamos quitando nada solo alterando un poco el orden para no aburrirnos y ir aprendiendo de todo un poco.
PYTHON E INTERNET No es necesario aclarar la importancia que en las comunicaciones tiene Internet, hoy día una de las mayores fuerzas de comunicación entre personas y de transmisión de información que se expandió la ultima década, justamente Python fue creado también hace una década, y ha ido creciendo paralelamente e incorporando las tools necesarias para manejarse en este sofisticado mundo. En cualquier computadora con una conexión a Internet que tengamos instalado Python, nos permitirá tareas tan diferentes como escribir scripts para leer y enviar mails, leer paginas web de lugares remotos, transferir archivos por FTP, programar interactivamente sitios WEB, parsear archivos XML y HTML y muchísimas mas, simplemente usando los módulos que trae incorporado Python como herramientas standard. En efecto, compañías de todo el mundo como: Google, Yahoo!, Walt Disney, Hewlett-Packard, JPL, y muchas otras, utilizan Python en sus webs para potenciar las mismas, el famoso GOOGLE SEARCH utiliza extensivamente código Python, así mismo BIT TORRENT utiliza Python para el manejo de transferencias de archivos PEER to PEER y ha sido utilizado por millones de personas. Muchas personas utilizan ZOPE para manejar sitios webs el cual esta escrito y es customizable a través de Python, Otros sitios utilizan PLONE el cual esta construido con ZOPE y deja el contenido a manejar por sus usuarios. Otros usan Python para escribir para escribir aplicaciones de web Java, utilizando Jython, un sistema que compila Python como Java bytecode, exporta librerías Java para usar en Python scripts y permite a Python usarse como web applets que corren en un browser. Existen miles de aplicaciones conocidas mas que aprovechan las posibilidades de Python en Internet que detallarlas a todas me llevaría una parte completa, lo que si sabemos que a medida que Internet creció, Python también lo ha hecho acompañando este crecimiento y proveyendo nuevas herramientas para los nuevos desafíos del uso de Internet de cada día. CONECTADOS A INTERNET A menos que el que lee este libro haya vivido en una cueva los últimos diez años, la mayoría estamos familiarizados con el uso de Internet y sabemos al menos como usuarios de que se trata cuando hablamos de ella. Técnicamente Internet consiste en muchas capas de abstracción y maquinas para poder enviar bits a través de cables o el medio por el cual se transmita a través del mundo, para poder recibirlos en nuestro web browser que los capturará y los renderizará en la forma de texto, gráficos audio, video. En este curso nos abocaremos primeramente a la programación de las interfaces de Internet, esto incluye también múltiples capas: sockets que son interfaces programables para las conexiones de bajo nivel entre maquinas y protocolos standard los cuales agregan estructuras conocidas al trabajo con sockets. En palabras simples sockets son como las salidas de bajo nivel que son programables, y los protocolos de alto nivel como FTP, paginas webs, email, etc ocurren sobre sockets. Los sockets también son llamados puntos finales de comunicación o endpoints, porque ellos son como puertas a través los cuales los programas envían y reciben bytes durante una conversación. Ademas de poder usarse como mecanismo de comunicación para la red, también los sockets pueden ser usados como un mecanismo de comunicación entre programas de la misma computadora, tomando la forma de un mecanismo IPC o INTER PROCESS COMUNICATION. A diferencia de algunas otras IPC, los sockets son de comunicación bidireccional, los programas pueden tanto enviar como recibir datos a través de ellos.
IDENTIFICADORES DE MAQUINA Supongamos un momento que uno quisiera tener una comunicación telefónica con alguien que se encuentra en alguna parte del mundo, en el mundo real necesitaríamos su numero de teléfono para poder llamar a dicha persona, o al menos un directorio telefónico donde poder buscar el numero de teléfono de dicha persona por el nombre. Lo mismo ocurre en Internet antes de poder establecer una conversación con otra computadora en el ciberespacio primero deberemos conocer el numero o nombre de esa otra computadora. NOMBRE DE LA MAQUINA El nombre de una maquina toma la forma de una string de números separados por puntos, llamados IP address o dirección IP (ej=178.93.218.100) o en una forma mas legible conocida como domain name o nombre de dominio.(ej=starship.python.net) Estos nombres de dominio son automáticamente mapeados en su dirección IP, por algo llamado domain web server, un programa en la red que trabaja con el mismo propósito que la guiá de teléfonos de la conversación telefónica que intenta localizar a un numero telefónico por el nombre, en este caso intenta localizar un IP numérico, por el nombre de dominio. Como caso especial el nombre de maquina localhost y su equivalente IP 127.0.0.1, siempre significan la misma maquina local, esto nos permite referirnos a servers que corren localmente en nuestra propia maquina, usando dicha IP o dominio. NUMERO DE PUERTO Un numero de puerto es simplemente un identificador aceptado para una conversación, dado que las computadoras en la red pueden soportar una gran variedad de servicios, los números de puertos son utilizados para identificar o nombrar una conversación particular en la maquina. Para que dos maquinas hablen sobre la red, o se comuniquen ambas deben asociar sockets, usando los IP correctos y el mismo puerto para que la comunicación se efectivice, si no seria como una carta enviada a un edificio sin aclarar a que departamento hay que llevarla, no podría encontrar el destinatario, entre todos los departamentos existentes. LOS PROTOCOLOS A pesar de que los sockets forman la base de las comunicaciones en Internet, mucha de la actividad que ocurre en la red, esta programada con protocolos, los cuales son modelos de alto nivel que corren encima de sockets, los protocolos de Internet definen una forma estructurada, para hablar con sockets, y estandarizan los formatos de mensajes y los números de puerto. FORMATO DE MENSAJE: provee una estructura para los bytes intercambiados sobre sockets en una conversación, NUMERO DE PUERTOS: son identificadores reservados para utilizar los sockets sobre los cuales los mensajes son intercambiados.
REGLAS DE NUMEROS DE PUERTOS Técnicamente los números de puertos pueden ser cualquier numero entero entre 0 y 65535, para hacer mas fácil a los programas localizar los protocolos standard, los números de puertos de 0 a 1023 son reservados y preasignados a los protocolos standard de alto nivel. En la tabla siguiente veremos los números de puerto reservados para los protocolos mas comunes.
CLIENTES Y SERVERS Para los programadores de sockets que existan los protocolos standard, significa que los puertos desde el 0 al 1023 están fuera de limites en los scripts a menos que utilicemos los protocolos de alto nivel. Esto tiene bastante sentido común, un programa Telnet se puede conectar y iniciar un dialogo con cualquier maquina que este escuchando Telnet, conectándose al puerto 23 de la misma, si no se preasignaran números de puerto standard, cada maquina escucharía telnet en diferentes puertos y la comunicación sería mas dificultosa. De la misma forma los sitios web escuchan requests en el puerto 80 por standard, si no fuera así, habría que tipear el puerto para poder visitar cada site, al ser todos diferentes. Al definir de esta forma puertos standard para servicios, la red naturalmente esta utilizando una arquitectura cliente - server, en un lado de la conversación, las maquinas que soportan protocolos standard corren un set de programas que esperan por conexiones en los puertos reservados, en el otro lado del dialogo, otras maquinas contactan estos programas para usar los servicios que ellas exportan. Nosotros llamaremos a el programa que esta corriendo en forma perpetua escuchando, un SERVER, y al programa que trata de conectarse al mismo un CLIENTE. Como ejemplo el protocolo HTTP que vemos en la tabla, usado en la web, permite a clientes y servers hablar sobre sockets en el puerto 80.
Server Una maquina que hostea sitios web, usualmente corre un programa web server que constantemente escucha (listen) requests de conexiones entrantes, en un socket conectado al puerto 80. Normalmente, el server en si mismo no hace nada mas que vigilar requests en sus ports constantemente, manejar los requests es delegado a otros procesos o threads dependientes.
Clientes Programas que desean hablar a ese web server especifican el nombre de maquina (IP o dominio) y el port 80 para iniciar una conexión. Para conectarse a web servers, los clientes son web browsers como Firefox, Internet Explorer, o Netscape, pero también un script puede abrir una conexión client-side en puerto 80 para visitar paginas web. ESTRUCTURAS DE PROTOCOLOS Funcionalmente, los protocolos pueden facilitar una tarea familiar como leer un email o postear un mensaje en un newsgroup, pero en el fondo siempre consisten en mensajes de bytes enviados sobre sockets. La estructura de estos mensajes de bytes varían de protocolo en protocolo y realmente es como el mecanismo oculto dentro de las librerías de Python que engloban la tarea. Por ejemplo el protocolo FTP conversa sobre dos sockets, uno para control y otra para transferir datos entre server y cliente. Los clientes FTP abren sockets al server envían peticiones y envian y reciben archivos sobre otro socket conectado al puerto de datos. LIBRERIAS DE PYTHON PARA INTERNET De cualquier manera esto que suena horriblemente complejo es manejado internamente por las librerías de Python las cuales hacen el trabajo sucio , simplificandonos la tarea de tener que manejar el mecanismo interno, es como manejar un auto sabemos que al torcer el volante doblará, pero el mecanismo interno del auto que realizará el trabajo de transmitir a las ruedas este movimiento, sepamos o no sepamos internamente como se transmite, se realizará igual. Por ejemplo la librería de Python ftplib maneja todo el trabajo de sockets sobre el protocolo FTP, los scripts que importan ftplib tienen acceso a una interfase de mucho mas alto nivel, para manejar FTPs y pueden ignorar todo el trabajo interno de la librería. En efecto en Python cada protocolo soportado es representado por un modulo standard con un nombre de la forma xxxlib.py donde xxx es reemplazado por el nombre del protocolo en minúsculas. De esta forma FTP es soportado por el modulo ftplib.py, aun mas el objeto top level de esta interfase es usualmente el nombre del protocolo, así por ejemplo para empezar una sesión FTP en un script de Python uno escribe primero import ftplib y pasa los parámetros apropiados en una llamada a ftplib.FTP(), en telnet escribe primero import telnetlib y luego telnetlib.Telnet() , etc. En Python tenemos también módulos para trabajar con respuestas de web servers (urllib), para parsear y manejar datos una vez que han sido transferidos sobre sockets o protocolos (htmllib) y mas módulos que están mencionados en la tabla siguiente.
Nosotros veremos muchos de estos módulos en las partes siguientes, vaya esta como introducción al tema Internet, para irnos iniciando lentamente en el tema y en sus definiciones. Hasta la parte siguiente: Ricardo Narvaja
PROGRAMANDO SOCKETS Ahora que ya vimos en la parte anterior que papel juegan los sockets, vamos a explorar las herramientas que Python provee para programar sockets en scripts. La interfase básica en Python para manejar sockets es el modulo llamado con el mismo nombre o sea socket, el cual es solo un wrapper (o capa de interfase) construido sobre las correspondientes librerías de C para sockets, por lo tanto las funciones send y recv de C, se convierten en métodos del modulo socket de Python. LO BASICO DE SOCKETS A pesar de que en esta parte no iremos al uso profundo de sockets, las transferencias con sockets son fácil de codear en Python, para crear una comunicación entre dos maquinas, los programas hechos en Python importan el modulo socket y llaman a los métodos para establecer la conexión y enviar y recibir datos. Veremos el primer ejemplo donde crearemos un simple server, y luego un cliente que pueda conectarse al mismo, el server se llamara echo-server.py
Bueno este es un típico server que estará esperando en el port 50007 que algún cliente se conecte a dicho port, como pusimos la string vacía '' significa que trabajara local, o sea localhost, y puerto 50007, si en vez de las comillas hubiéramos puesto nuestra IP, cualquier cliente externo, con dicho IP y el port correcto, podría conectarse a nuestro server, por ahora haremos las pruebas solo como localhost.
Vemos que se crea un objeto socket llamado sockobj, y al crearlo se le pasan ciertas constantes en este caso (AF_INET, SOCK_STREAM), significa que la conexión sera TCP, luego el método listen tiene un argumento numérico en este caso 5 que son las conexiones pendientes que aceptara antes de comenzar a rechazar conexiones.( esto se utiliza para que el server no sea desbordado, una vez que se vacía a menos de 5 el queue de conexiones, aceptará nuevamente)
Y luego dentro de un while que siempre estará loopeando espera por un nuevo cliente el método accept, devuelve una tupla, en la misma el primer campo es un nuevo socket que se crea para dicha conexión con el cliente y el segundo campo la dirección desde donde se conecto el cliente. Ahora si usando ese nuevo socket, que se creo para ese nuevo cliente en especial, y dentro de otro while se le aplica el método recv() en este caso con argumento 1024, para recibir 1024 bytes del cliente y se guarda en la variable data, si ese cliente envía la señal de EOF (lo cual ya veremos mas adelante como lo hace), se sale de este while mediante break, y si realmente envió datos, se le responde usando el método send() donde se le agrega la string 'Echo=>' delante de data y se le devuelve los mismos datos recibidos solo que con esa string delante y luego se cierra el socket que fue creado para este cliente usando el método close(). Es de mencionar que el break nos hace salir del while interno, lo mismo si ya respondimos salimos de este while, si nos envió datos y le respondimos, pero el while principal, sigue funcionando y volverá a trabajar esperando mas clientes y usando accept(), para crear nuevos sockets con ellos, para enviar y recibir datos. Si colocamos el archivo para debuggear
Ponemos dos breakpoints como muestra la imagen y apretamos DEBUG.
Allí esta parado en el primer BP, al pasar con F6 dicha linea.
Allí vemos en el DEBUG PROBE que creo el objeto sockobj. Si en una consola tipeamos netstat -ano podemos ver todas las conexiones de nuestra maquina.
Vemos que no hay conexiones aun en el port 50007, sigamos pasemos la linea siguiente con f6 y vemos que no hay aun en la lista ningún registro nuevo de actividad, pasemos el listen con f6. Si tenemos el firewall de Windows activado o cualquier otro vemos que sale el cartel de que hay una nueva conexión, la desbloqueamos.
Allí vemos la nueva conexión listening o esperando en mi caso corresponde al proceso de PID 1596 , puedo fijarme en la lista de procesos a que proceso corresponde.
Agregándole para que se muestre la columna con los PID de los procesos vemos que 1596 en mi maquina corresponde a Python.
También rápidamente tipeando tasklist podemos verificar esto.
Ahora si le doy a DEBUG en el WING., veo que el proceso queda corriendo y no para en el segundo breakpoint ya que allí solo llega cuando un cliente se conecta a este server, así que estará esperando alguna conexión, en el accept() y no pasará de alli como dijimos si no hay un cliente que se conecte así que mas vale que preparemos un cliente para conectarnos a este server que nos esta esperando, jaja.
Antes de hacer el script cliente, trataremos de conectarnos desde la consola de Python tipeando algunas de las instrucciones que tendrá el cliente, para realizarlo en forma interactiva y entender bien como funcionaria. Ahora que tenemos el server esperando en el port 50007, puerto localhost o 127.0.0.1, podemos intentar conectarnos a este server desde una consola de Python así.
Lo primero es importar socket. from socket import * luego de eso al igual que en el server creamos un objeto socket para conectarnos a TCP
Ahora utilizamos el método connect() y al tipear la instrucción el server sale del estado de esperar y para en el breakpoint.
Le quitamos el breakpoint y damos a DEBUG para que continúe
Allí me imprime el IP y puerto del cliente. Ahora tenemos que enviarle data
Allí le envié el texto 'HOLA MUNDO' con send() y a ver que me responde el server con recv()
Vemos que como habíamos planificado me devuelve mi misma data con la string 'ECHO=>' delante.
Otra data que le envío y la consabida respuesta, cuando envío close() se cerrara la conexión con el
cliente (le envío el EOF) si quiero seguir enviando data ya no lo aceptará.
Bueno todo este trabajo lo realiza un script cliente que se conecta al anterior y se llama echoclient.py.
Vemos que realiza dentro de un script lo mismo que hicimos en la consola de Python a mano, lo único que en este caso importa sys para poder pasar el IP del server y los mensajes como argumentos. Luego abre el socket TCP y usa connect() pasandole en este caso localhost y 50007, como el mensaje pueden ser varias lineas, dentro de un for va enviando linea a linea y recibe e imprime la respuesta de cada una. Veamoslos a ambos funcionar, pongamos a ejecutar el server primero fuera de Wing.(detengamos el que estaba corriendo en WING sino el otro no podrá usar el port).
Al abrir una consola y ejecutar el server, en otra tipeamos netstat -ano y vemos que esta ahí listening en 50007, ahora desde otra consola corramos el cliente.
Vemos que al no pasarle argumentos manda la string por default y recibimos la respuesta del server con su string ECHO=> delante, ahora tratemos de conectarnos pasandole argumentos.
Ahí los tenemos trabajando en conjunto, pasandole varios argumentos, a cada uno el server contesta perfectamente. La instrucción sockobj = socket(AF_INET, SOCK_STREAM) usa un par de constantes asignadas como argumentos lo que en realidad significa CREAME UN SOCKET TCP/IP, realmente AF_INET significa el protocolo IP y SOCK_STREAM la transferencia TCP, si uno quisiera crear un socket UDP habría que usar en el segundo lugar SOCK_DGRAM y para sockets en UNIX en la maquina local se utiliza en primer lugar AF_UNIX. Bueno hasta la parte 3 Ricardo Narvaja
PROBANDO SOCKETS EN FORMA REMOTA Realmente cuando en una misma maquina usamos localhost en vez del IP de una maquina remota, se simula la conexión entre dos maquinas diferentes en una sola, de esta forma la parte anterior es similar a lo que se haría en forma remota, pero a veces es lindo ver lo que ocurre en diferentes maquinas. No todos poseen dos maquina con diferentes IP para poder probar esto, pero bueno, a veces con un poco de ingenio podemos lograr algo parecido. En mi caso tengo mi maquina real que será el HOST que es mi UBUNTU (podría ser cualquier otro sistema WINDOWS o MAC) y en esta maquina tengo un VMWARE que es un emulador y en el dentro tengo instalado un Windows XP virtual que sera el GUEST, que es donde normalmente hago los tutes, en ambas maquina tengo PYTHON. Cualquier combinación de HOST y GUEST sirve. Pueden ser ambos WINDOWS, o ambos LINUX, con trastear un poco tendremos IP diferentes para probar los scripts. El problema es que normalmente para poder navegar en el GUEST usamos en la configuración del VMWARE la opción NAT en la placa de red y de esa forma ambas maquinas comparten el mismo IP y no hay posibilidad de conexión entre ambas. Para poder lograr tener diferentes IP en cada una (aunque en mi caso si quito NAT el XP GUEST no navega) pero a pesar de eso obtiene su IP diferente y tiene PING con la maquina HOST, hay que cambiar en las opciones de RED del VMWARE a BRIDGED.(es una posibilidad seguro habrá otras, pero seguro es cualquier otra opción que no sea NAT)
Quizas solo con esto en algunos casos sirva es de notar que aunque no se pueda navegar en el GUEST, si podemos hacer un PING al IP del HOST y me da respuesta, entonces ya tengo la posibilidad de probar los scripts en forma remota. En mi caso, me fije todos los datos de la conexión en el HOST con ifconfig que es el equivalente a
ipconfig de Windows (tapo los IP porque son privados jeje)
Y allí obtuve los datos de mi IP, también el dato de mascara que es la mascara de red, lo siguiente es tipear route y allí abajo saldrá la puerta de enlace al lado de donde dice default, y los DNS están en la configuración de red, los copie todos a la configuración de red del GUEST en Windows y le puse un IP manual apenas diferente al de la otra maquina, que al hacer ping con la maquina principal tenia respuesta y viceversa. El que tiene HOST Windows solo tipeando ipconfig tiene los datos.
El que no quiere hacer esto puede siempre usar la misma maquina siempre reemplazando la IP remota con localhost y no hay problema, si logran tener dos IP diferentes, mejor.
Al final el HOST tiene IP: XX.XXX.58.165 y el GUEST tiene el IP: XX.XXX.58.164
Tanto en LINUX como en WINDOWS estos datos se consiguen fácilmente así que cambiando a BRIDGED podemos tener IP diferentes, posiblemente tendremos que deshabilitar el firewall de Windows, para poder conectarnos, pero la prueba del PING es concluyente si hago PING del host al guest.
Como vemos tenemos respuesta, aunque no puedo navegar en el GUEST como cuando uso NAT al menos podre usarlo para probar los scripts remotos, cuando termine lo volveré a NAT así podrá navegar nuevamente. La maquina GUEST la usare como server, haremos varias pruebas en esta y las siguientes partes, todo lo que yo diga de hacer en ella, si solo usan una maquina lo hace en la misma maquina tanto lo correspondiente al server como al cliente y donde dice IP colocan siempre localhost. Pondremos un FTP server en la maquina GUEST para poder copiar archivos fácilmente allí, pueden bajarse el FILEZILLA SERVER o cualquier otro que les guste, la configuración es muy sencilla, hay que agregar un usuario y un password.
Y el directorio raíz donde queremos que se guarden los archivos que enviamos.
Así que con eso el visitante navegara por esa carpeta SHARE y podrá guardar archivos allí dado que le di permiso de escritura, lo ponemos en marcha, veamos con netstat -ano si esta funcionando, como es un server, y en este caso usa el port 21, estará listening esperando conexiones de los clientes.
Por supuesto con tasklist hallo el PID corresponde al filezilla server.
En la maquina HOST tengo los dos scripts que usamos la parte anterior si quiero correr mi server en el GUEST deberé copiar el archivo echo-server.py al mismo.
Eso lo puedo hacer desde el terminal de mi HOST que es Linux o si el host fuera Windows es similar usando CMD para arrancar el consola. Conviene cambiar al directorio donde tengo los archivos a copiar en mi caso usando cd. ricnar456@ricnar456-desktop:~$ cd Escritorio ricnar456@ricnar456-desktop:~/Escritorio$ cd 02-PROGRAMANDO\ SOCKETS/ Y luego allí tipeo ftp y el IP del GUEST. ricnar456@ricnar456-desktop:~$ ftp XX.XXX.58.164 Connected to XX:XXX..58.164. 220-FileZilla Server version 0.9.24 beta 220-written by Tim Kosse (
[email protected]) 220 Please visit http://sourceforge.net/projects/filezilla/ Name (XX.XXX.58.164:ricnar456): ricnar456 331 Password required for ricnar456 Password: 230 Logged on Remote system type is UNIX. ftp> Allí ya me conecte, tipeando el user y password que coloque en el filezilla y luego tipeando dir puedo ver los archivos de la carpeta raíz que era SHARE ftp> dir 200 Port command successful 150 Opening data channel for directory list. -rw-r--r-- 1 ftp ftp 2015344 Apr 03 2008 44-VIENDO Y PROCESANDO IMAGENES CON PIL.rar -rw-r--r-- 1 ftp ftp 2001 May 29 09:02 eBP Released Info.txt drwxr-xr-x 1 ftp ftp 0 Apr 02 2008 ETAPA 2 -rwxr-xr-x 1 ftp ftp 2464595 Jun 15 22:47 FileZilla_Server-0_9_25.exe -rw-r--r-- 1 ftp ftp 147 Apr 01 2008 ftp viejo -rw-r--r-- 1 ftp ftp 8016 May 29 06:51 fwdrecursopythondia3material.zip -rw-r--r-- 1 ftp ftp 1301410 May 29 09:02 Learning Python, 2nd Edition.chm -rw-r--r-- 1 ftp ftp 10147205 Apr 01 2008 Programming_Python_3rd_Edition.rar -rw-r--r-- 1 ftp ftp 59413 Jun 15 23:20 screenshot1.png -rw-r--r-- 1 ftp ftp 33829 Jun 15 23:51 screenshot2.png
226 Transfer OK ftp> Luego tipeo bi para que cambie a binario ftp> bi 200 Type set to I y luego subo el archivo con put ftp> put echo-server.py local: echo-server.py remote: echo-server.py 200 Port command successful 150 Opening data channel for file transfer. 226 Transfer OK 1524 bytes sent in 0.00 secs (11360.9 kB/s) ftp> Bueno con eso el archivo echo-server.py fue subido a la carpeta raíz.
Ahora para ejecutarlo en forma remota tenemos que hacer algunas cositas, primero activar el servicio TELNET en la maquina GUEST, eso en MI PC, click derecho ADMINISTRAR y luego en SERVICIOS.
El servicio TELNET viene deshabilitado por default, así que lo habilito y arranco en forma manual, para poder conectarnos a TELNET la maquina debe tener un nombre de usuario y un password si no lo tiene, o sea si arranca directamente nuestra PC sin pedir password cuando la iniciamos, podemos agregarle un password aquí.
Si no tiene password lo podemos configurar allí para que al ingresar lo pida y al tratar de conectarnos a telnet nos lo pedirá.
Recordemos que telnet usaba el puerto 23
Así que si lo hicimos correr correctamente, estará listening en el puerto 23
Allí esta ahora tratemos de conectarnos desde el HOST en el terminal o consola, tipeando telnet y el IP del GUEST. ricnar456@ricnar456-desktop:~$ telnet XX.XXX.58.164 Trying XX.XXX..58.164... Connected to XX.XXX.58.164. Escape character is '^]'. Welcome to Microsoft Telnet Service login: ricnar password: *=============================================================== Bienvenido a Telnet Server de Microsoft. *=============================================================== C:\Documents and Settings\ricnar> Bueno ya estamos conectados debemos ir al directorio donde estaba el archivo echo-server.py C:\Documents and Settings\ricnar>cd .. C:\Documents and Settings>cd .. C:\ Tenemos que entrar a SHARE C:\>cd SHARE C:\>dir Directorio de C:\SHARE 16/06/2008 16/06/2008 03/04/2008 29/05/2008 16/06/2008 02/04/2008 15/06/2008 01/04/2008 29/05/2008 29/05/2008 01/04/2008
00:08 . 00:08 .. 06:05 2.015.344 44-VIENDO Y PROCESANDO IMAGENES CON PIL.rar 09:02 2.001 eBP Released Info.txt 00:00 1.524 echo-server.py 06:32 ETAPA 2 22:47 2.464.595 FileZilla_Server-0_9_25.exe 22:32 147 ftp viejo 06:51 8.016 fwdrecursopythondia3material.zip 09:02 1.301.410 Learning Python, 2nd Edition.chm 22:36 10.147.205 Programming_Python_3rd_Edition.rar
15/06/2008 23:20 59.413 screenshot1.png 15/06/2008 23:51 33.829 screenshot2.png 16/06/2008 00:08 9.217 screenshot3.png 11 archivos 16.042.701 bytes 3 dirs 5.005.963.264 bytes libres Allí lo tenemos ahora habrá que ejecutarlo para que arranque C:\SHARE>python echo-server.py Con eso lo ejecutamos y queda el server funcionando veamos si en el GUEST esta el server esperando conexiones.
Vemos que arranco, pudimos arrancarlo en forma remota y esta esperando conexiones. Ahora en la maquina HOST que la usaremos de cliente correremos el echo-client.py pero ahora tenemos que pasarle entre los argumentos el IP de la maquina del server para que pueda conectarse si no usaría localhost que esta por default. $ python echo-client.py XX.XXX.58.164 Client received: 'Echo=>Hello network world' $ python echo-client.py XX.XXX.58.164 pepe Client received: 'Echo=>pepe' $ python echo-client.py XX:XXX..58.164 pepe salame Client received: 'Echo=>pepe' Client received: 'Echo=>salame' $ python echo-client.py XX.XXX.58.164 pepe salame salamin Client received: 'Echo=>pepe' Client received: 'Echo=>salame' Client received: 'Echo=>salamin' Vemos que funciona exactamente igual que en la parte anterior que emulamos la maquina remota con localhost usando solo una que funcionaba tanto como cliente como server a la vez. Otra cosa que se debe tener en cuenta cuando uno va a poner en funcionamiento un server, es que el puerto no este ocupado ya sea por otro programa que casualmente usa el mismo puerto, o por una instancia anterior de nuestro server, que nos olvidamos cerrar, siempre podemos constatar si el puerto esta usado mirando con netstat -ano, y viendo si hay algún programa ocupando el puerto y luego con tasklist podemos ver por el PID que programa es y cerrarlo, así podemos correr nuestro server.
Es importante comprender que en este caso el server y el cliente tienen acordada la conexión a través del puerto 50007, que no es un puerto de los reservados, podría utilizarse un server que use estos últimos pero siempre es mas complicado, pues en las maquinas habrá programas que normalmente usan esos ports , como browsers, clientes de email, etc que no dejaran usar el puerto a menos que los cerremos, y entonces es mas molesto.
Podemos conectarnos a un puerto reservado usando sockets, en este caso debemos hacerlo en la maquina HOST o alguna que tenga conexión a INTERNET ya que en mi caso el GUEST no tiene, así que el host pruebo tipeando.
Vemos que nos conectamos a un POP server standard que responde cuando nos conectamos enviando un mensaje con algunos datos, en este caso +OK mas ciertos datos del POP server. También podemos conectarnos a un FTP SERVER desde un socket
En este caso como le pedí solo 40 caracteres, tuve que preguntar dos veces para tener toda la respuesta podría haber pedido 100 caracteres directamente
Ahora quedo mas elegante tenemos en la respuesta el nombre del programa que usa, también que versión, el nombre adonde pertenece el ftp, y el IP, un poco de todo jeje.
Aquí tenemos un socket conectándose a un web server,y obtenemos respuestas también. Si uno conoce como interpretar las respuestas de los servers, podría armarse un cliente que se conectara mediante sockets, pero por suerte no es necesario ya que para conectarnos a protocolos
conocidos, Python nos provee de módulos (poplib, ftplib, httplib, urllib, etc) de mas alto nivel que nos evitan tener que lidiar con las respuestas en forma cruda, y tener que conocer profundamente los protocolos y estos módulos usan internamente los sockets sin tener que lidiar nosotros con ellosEn la próxima parte nos dedicaremos a estudiar los módulos que trae Python para facilitarnos la tarea en el manejo de servers y protocolos conocidos. Hasta la parte 4 Ricardo Narvaja
SERVER MANEJANDO MULTIPLES CLIENTES A LA VEZ El modelo de server y cliente que vimos en las partes anteriores es la base para ilustrar lo fundamental de las conexiones por socket, pero adolece aun de un problema que es el momento en que muchos clientes tratan de conectarse a la vez al server, si esto ocurre el server se ve desbordado y fallará y los clientes comenzarán a ser rechazados por el mismo. Para ello hay varias soluciones una de las propuestas en el libro que nos basamos es un server que use fork, para cada vez que entre un nuevo cliente se cree un proceso, que será una copia del original, que lo atienda y así uno por cada nuevo cliente, el problema es que fork, solo funciona en plataformas Unix y Linux, por lo cual no veremos esa solución aquí, si no que iremos a una solución mas general que pueda funcionar en todos los sistemas, que es usando threads. Aquí tenemos una versión de nuestro server que maneja los clientes mediante threads se llama thread-server.py.
Vemos que es muy similar al original, la única diferencia aquí es que luego de crear el objeto socket y llamar a bind(), para poner en marcha el server en el port que le indicamos, llama a la función dispatcher(), la cual tiene el mismo while que el original y dentro del mismo esta el accept() que recibirá las conexiones de los clientes.
Dicha función dispatcher() una vez que un cliente se conecta y de imprimir el IP del mismo, y la hora que que se conecto usando now() que es una función que crea para retornar la fecha , crea un nuevo thread, pasandole el nuevo socket connection que se creo con dicho cliente como argumento a la función handleClient(), que correrá en un nuevo thread.
Aquí vemos la función handleClient()
Cada ves que un cliente se conecte será creado un nuevo thread que correrá esta misma función, vemos que tiene un time.sleep(5) para simular que el thread estará ocupado un rato procesando la conexión con dicho cliente, y por supuesto dentro de esta función estará el segundo while que recibirá la data con recv() y le agregará 'Echo =>' , y luego de repetir la misma data que nos envió el cliente, la hora mediante format string y se la reenvía al cliente con send(). Como resultado de esta forma de funcionar, cada thread se ocupa del trabajo mas pesado de manejar el intercambio de datos con cada cliente, mientras el loop principal, vuelve enseguida a estar disponible para aceptar nuevos clientes sin sobrecargarse realizando otros trabajos. Ahora haremos varias pruebas primero simularemos la conexión entre dos maquinas, usando localhost, para los que no tienen la posibilidad de dos maquinas con diferente IP, y luego haremos la conexión entre dos maquinas con distinto IP.
USANDO LOCALHOST (UNA SOLA MAQUINA) Ponemos en marcha el server
Ahí pusimos en marcha el server, ahora en la misma maquina en otra consola arrancaremos el cliente que es el mismo echo-client.py. Recordamos que si no aclarábamos el IP, usaba localhost por default si no enviábamos mas strings, así que no hay problema, recibimos el mensaje por default
Ahora enviemos varias strings.
Allí tenemos la respuesta en este caso tenemos que aclarar localhost porque el primer argumento es el IP y machaca el valor por default, si no lo pasamos dará error, por supuesto los que pueden hacer solo el ejemplo con una sola maquina, saben que reemplazando localhost por el IP de la maquina remota funcionará igual.
Podemos aumentar mucho el timer del server para que tarde muchísimo en responder, así tenemos tiempo de lanzar varios clientes y verificar si se crean varios threads.
Ahí le pusimos 60 segundos para que tarde en responder a cada cliente. Ahora cerramos el que teníamos corriendo y lo lanzamos nuevamente y abrimos dos consolas y preparamos para lanzar un cliente en cada una, ponemos el administrador de tareas que muestre la columna de cantidad de threads o subprocesos como los llaman ahí y vemos que cuando lanzamos los dos clientes el proceso de Python que corresponde al server, pasa de 1 thread a 3 threads
Si lo dejamos un rato al pasar los 60 segundos del timer el server contesta y los threads que abrió para responder a cada cliente se van cerrando y vuelve a 1 solo thread principal como al inicio.
CON DOS MAQUINAS (EN FORMA REMOTA) Ahora que ya probamos que se crea un thread por cada cliente que se conecta volvemos el time.sleep a 5 como era originalmente y colocamos la conexión de nuestra maquina virtual en bridged para tener un IP diferente y probar en forma remota.
Allí verifico que hay ping entre ambas maquinas, como antes el GUEST con el server tendrá el IP XX.XXX.58.164 y el HOST sera el cliente con el IP XX.XXX.58.165 en mis maquinas, ustedes acomoden las suyas a su gusto si no pueden tener dos maquinas con dos IP bien, y si no ya probaron el punto usando localhost. Pongamos otras vez el servicio TELNET en marcha en la maquina GUEST.
Y nos queda arrancar el FTP SERVER del filezilla para copiar el archivo. Ahora vamos a la maquina HOST desde donde haremos todo el trabajo como la vez anterior y abramos una consola de Windows o terminal si es Linux. Primero enviaremos el archivo por ftp
Allí subimos el archivo a la maquina GUEST, ahora lo ejecutaremos por telnet para poner en marcha el server en forma remota.
Y ahí navegue desde mi consola telnet remota hasta el directorio SHARE y arranque el server que estará esperando en el port 50007 por supuesto.
Ahora arrancare otro terminal en el host para conectar un cliente al server.
Y vemos que el server trabajo perfectamente en forma remota no necesitamos probar que creo varios threads, pero si quieren hacerlo, pónganle el time.sleep del server lo suficientemente grande, y arranquen varios clientes y que se conecten, luego verifican en en administrador de tareas que el Python que corresponde al server tiene 3 o mas threads según los clientes que hayamos conectado. CLASE STANDARD SOCKETSERVER
EN
PYTHON
PARA
ATENDER
VARIOS
CLIENTES-
Existe una clase que viene incluida en Python que permite realizar el trabajo anterior en una forma sencilla, dicha clase tiene atributos diferentes que permiten poner en marcha diferentes tipos de server, por ahora veremos como realizar el ejemplo anterior usando esta clase la cual se profundizará en otros usos mas adelante en las siguientes partes, el script se llamará classserver.py.
Vemos que el código es muy sencillo si lo comparamos con el thread server vemos que es mucho mas breve.
Allí vemos la comparación de ambos, en el thread server había que crear el objeto socket y pasarle los argumentos necesarios (AF_INET, SOCK_STREAM) para que sea TCP en cambio en el que usa SocketServer, vemos que eligiendo ThreadingTCPServer ya crea todo para que sea un server TCP. server = SocketServer.ThreadingTCPServer(myaddr, MyClientHandler)
Vemos que hay otros tipos de server posibles que eligiendo el atributo correcto los creará de la misma forma. El tema es crear una clase en este caso llamada MyClientHandler que herede de SocketServer.BaseRequestHandler, dicha clase deberá tener un método handle que correrá con los diferentes threads para intercambiar información con cada cliente.
En este caso self.request es el socket que se crea para conectarse a cada cliente y como en el caso anterior que se llamaba connection, aquí también se le aplica los métodos recv() y send() para recibir y enviar la respuesta al cliente. PROBANDOLO EN UNA SOLA PC
Ahí esta funcionando en una sola PC. PROBANDOLO EN DOS MAQUINAS Cierro solo el cliente y dejo el server corriendo, así no tengo que copiar por FTP el archivo y arrancarlo por telnet, ya sabemos como se hace de cualquier forma, así que ya tengo el server esperando, desde el HOST abro una consola o terminal.
Vemos que el server que tenia impresas las conexiones desde localhost, como no lo cerré, y lo llame desde otra maquina, ahora me muestra a continuación las dos conexiones con el IP correspondiente a la máquina remota desde donde se le conectó, vemos que las respuestas son similares sea el llamado desde localhost o desde un IP remoto, por eso es que yo pongo ambos casos como información pero pueden realizar todo estos ejemplos solamente usando localhost que será similar. Hasta la parte siguiente. Ricardo Narvaja
USANDO SOCKETS PARA CREAR UN SIMPLE SERVIDOR DE ARCHIVOS Lo que haremos ya que estamos en el tema sockets, será crear un servidor para bajar archivos que use sockets, para llevar la idea de server y cliente a un punto un poco mas real que solo hacer eco de la petición que enviá el cliente. El siguiente script que se llamará getfile.py, correrá en ambas máquinas server y cliente, solo diferenciándose el modo en que lo hará por un argumento que se pasa en la linea de comandos, de esta forma al arrancar decidimos cual de las dos maquinas será server y tendrá los archivos para bajarse y cual será el cliente que podrá bajárselos, en cualquier momento puede invertirse el funcionamiento arrancando nuevamente los scripts con los argumentos en forma inversa y el server pasar a ser cliente y viceversa. Es un poco largo el código ya que tiene el cliente y el server en el mismo script, así que lo partí en dos imágenes, igual lo explicaremos paso a paso como siempre.
Vemos que allí hay una aclaración de como se usa para pasarle los argumentos por linea de comandos, en la maquina que uno quiere usar como server, habría que arrancarlo tipeando en un terminal lo que vemos en la imagen siguiente, vamos a correrlo primero para ver como funciona y luego lo explicaremos, si lo hacemos en una sola maquina usando localhost, abrimos un terminal o consola y tipeamos.
Y con eso tendríamos el server escuchando, en otra consola lo podemos verificar.
Allí esta esperando conexiones. Ya que el server usa como directorio raíz el mismo donde corre y sus subcarpetas, cree una carpeta en el escritorio llamada testdir y dentro un archivo de texto llamado texto.txt.
Ahora en otra consola arranquemos el cliente.
Y vemos que se baja el archivo y lo guarda en el escritorio pues el cliente esta corriendo allí, si tenemos dos maquinas copiamos el script a la otra maquina, y lo corremos solo que en el caso de localhost no hubo que aclarar IP porque al no ponerlo, usa localhost por default pero ahora habrá que aclararlo.
Allí tenemos el comando completo, aclarando el IP de la maquina server el port y el archivo que queremos el cual transfiere y lo baja a la maquina del cliente, bueno ahora vayamos paso a paso viendo como funciona el script en ambos modos, cerremos todas las consolas para que se cierren todos los procesos que estaban corriendo y veamos en el WING.
Lo primero que hace lógicamente es importar todos los módulos que necesitará y define la función now() que devuelve la fecha actual y la hora, podemos ir debuggeando y probando para ir viendo como funciona, Coloco el script como Main Debug File.
Por ahora lo arrancaremos primero como server así que luego de poner un breakpoint y darle a DEBUG en la ventana que aparece le ponemos los argumentos para que corra como server.
Allí paro en el breakpoint.
En el Debug Probe como ya esta definida la función now(), podemos testearla para ver como funciona.
Jeje, funciona perfectamente.
Luego define las variables que usará, blksz será el tamaño del envío y recepción de datos que en los ejemplos anteriores siempre fue 1024, aquí lo setea en una variable, y el host y puerto por default que serán localhost y 50001, también define una variable helptext que tiene la ayuda de como arrancar el script en modo server y cliente.
Luego de definir todas las funciones, empieza a ejecutarse aquí, chequea primero que este corriendo como principal, y si es así, va a ejecutar la función parsecommandline(), cuyo resultado lo guarda en args, vayamos a dicha función, podemos poner un breakpoint dentro de ella y apretar DEBUG.
Allí estamos, vemos que crea un diccionario vacío llamado dict, y en args guardará los argumentos que tipeamos, quitando de la lista mediante indice slice el primer argumento que es el nombre del script que no lo necesita y deja el resto. >>> sys.argv ['C:\\Documents and Settings\\ricnar\\Escritorio\\getfile.py', '-mode', 'server']
Lo que hará en esta función, es armar un diccionario con los argumentos para poder directamente poder acceder por las keys del mismo por ejemplo, preguntando dict [-mode] y que el valor relacionado me conteste si esta en modo server o cliente, lo mismo con cualquier otro argumento, usando como keys los posibles argumentos -file, -host o -port que en cualquier caso pueda acceder fácilmente a su valor relacionado.
Allí en un while mientras el largo de args sea mayor o igual a 2, entra y arma el diccionario poniendo args[0] como key y el argumento siguiente arg[1] como valor relacionado, en nuestro caso son solo dos argumentos por lo cual no se repetirá el while, pero si hubiera mas, usando slice, quita los dos argumentos ya usados de args, y vuelvo a repetir el ciclo armando una segunda key y
valor relacionado con el siguiente par y así hasta que se vacíe args. En nuestro caso al llegar al return.
Vemos que quedo como pensábamos un diccionario con una sola key, que es '-mode' y su valor relacionado que es 'server'.
Luego de volver, va a ejecutar la función main() pasandole el diccionario que ahora esta en args, ya que se asigno al retornar de la función anterior.
Recordamos el método get() para diccionarios, devuelve el valor relacionado de una key si la hay y si no existe dicha key, usa el valor por default que es el segundo argumento Vemos que usa get() pidiendo el valor relacionado de '-mode' y nos devuelve 'server' ya que dicha key existe, mas adelante usa get() para preguntar por el '-host' y como no existe dicha key en mi
diccionario, usa el valor por default, defaultHost, si existiera dicha key, usaría el valor relacionado que sería el IP que hubiéramos pasado como argumento.
Así que de esa forma obtiene el host y el port el cual para a numero entero usando int().
Luego vemos que chequea el modo si es server llama a la función server() pasandole como argumentos el host y port y si no esta especificado si es server, entonces asume que será cliente y obtiene el nombre del archivo que desea bajarse y llama a la función client() pasandole el host, el port y el nombre del archivo, y como ultima opción si no halla el nombre del archivo, te imprime helptext para decirte como debes pasar los argumentos pues algo falló.
Vemos que a partir de aquí el server es similar al thread-server que habíamos visto, y cada vez que un cliente se conecte llamará a la función serverthread() que manejará el nuevo socket.
Vemos que la función wrappea el socket para manejarlo como si fuera un archivo de lectura, ya que si llegamos aquí es porque un cliente ya se conectó y le paso el nombre del archivo que desea bajar, vemos que si pongo un breakpoint en esta función y aprieto DEBUG el server queda corriendo, pues esta esperando que algún cliente se conecte y le pida algún archivo.
Para poder seguir debuggeando aquí, abrimos un terminal o consola y arrancamos un cliente aquí en la misma maquina para pedirle un archivo al server.
Apenas tipeamos eso, para en el breakpoint, al crearse un nuevo thread para atender al cliente que se conecto.
Vemos que al usar makefile(), convierte al socket en un file object por lo cual se pueden usar los métodos de archivos en el.
Y al leer una linea con readline obtenemos el nombre del archivo pedido, quitando le con slice el \n final.
Luego dentro de un try-except vemos que intenta abrir el archivo pedido en modo lectura y binario, y leer 1024 bytes del mismo con read(), luego usando send() le envía al cliente esos primeros 1024 bytes y se repite hasta que el archivo esta vacío, si no se encuentra el archivo va al else donde imprime un error, luego de enviar al cliente todo el contenido del archivo se cierra el socket.
Vemos que en el caso de que corra como cliente lo que hace es abrir un socket TCP usar connect() y enviar mediante send() el nombre del archivo mas un retorno de linea. Luego usa split() para quitarle el path y deja el nombre del archivo solo, y crea un archivo como binario y de escritura, y luego dentro de un while usando recv(), va a recibir de a 1024 bytes y los va escribiendo en el archivo con write(), luego cierra el archivo y el socket, imprimiendo que obtuvo el archivo. Obviamente este es un primer ejemplo y no tiene el server ningún tipo de restricción ni seguridad, cualquier cliente que mande un path con un archivo que exista, lo obtendrá sin restricciones, sin haber un chequeo de si esta autorizado o no para ello, mas adelante veremos servers con restricción de acceso y permisos diferentes, que se acercan mas a un uso realista del tema. El hecho que wrapee el socket en un objeto tipo archivo y use readline() no es para nada necesario, pues puede leer usando recv(), el nombre del archivo perfectamente, simplemente el autor lo usa, para mostrar una forma cómoda de utilizar métodos de archivos en sockets, que son muy útiles (por ejemplo podríamos aplicar load y dump de pickle que vimos anteriormente, o muchísimos métodos de archivos mas). AGREGAR UNA GUI Como ya tenemos una idea del capitulo anterior sobre Tkinter podemos agregarle una mini GUI al cliente para que sea mas sencillo pedir el archivo, se supone que el server ya estará corriendo, en ese caso no es tan importante una GUI pues solo se arranca una vez y no hay que elegir archivos los argumentos son siempre los mismos el script se llamará getfilegui-1.py. Esta punto no agrega nada a lo que estamos viendo de Internet, es solo para completar el que quiere si no le interesa el tema de la GUI puede pasar a la parte 6 que es la siguiente.
Vemos que crea una instancia en Tk, la cual en forma implícita la usa como parent para instanciar un Frame el cual estará en box. Luego packea el Frame como corresponde, crea dos nuevos Frames usando como parent el anterior o sea hijos del principal, el cual uno lo packea a la derecha y otro a la izquierda, estas instancias de Frame se llaman lcol y rcol. Luego crea una lista con los nombres que le dará a los labels que creará y dentro de un for que recorre esa lista la variable label tomará cada uno de los campos de la misma, y se realizará una instancia en Label, usando como parent la instancia de Frame icol o sea que se colocará a la izquierda pues estaba packeada allí, y tendrá el texto 'Server' y al lado a su derecha de instanciará un entry usando como parent el Frame rcol, o sea que irá a la derecha siempre pues así fue packeado. A su ves en un diccionario vacío llamado content, va guardando como key el nombre del label y como valor asociado la instancia del entry correspondiente que esta a su derecha.
Y así dentro del for se crearán tres Labels con su entry a la derecha, y el diccionario al salir del for tendrá, los labels como keys y sus valores asociados serán las instancias de los entries de su derecha.
Luego configura el titulo usando master.title y usando master.bind capturara si apretamos la tecla return cuando ocurra eso se llamará a la función callback que se llama onReturnKey().
Esta función armará el pedido por linea de comandos, usando get() en las instancias, obtendrá el contenido de cada entry y mediante format string, armará una string para usar como linea de comandos tal como si lo hubiéramos hecho directamente en la consola.
Luego usando os.system() se le pasa la string a ejecutar y usando showinfo que importo de
tkMessageBox, nos avisa que la bajada esta completa. Podemos probarlo en una misma maquina, ponemos el server a correr.
Y ahora arrancamos la GUI directamente.
Completamos los datos sin apretar ENTER.
Ahora si que esta todo listo apretamos ENTER
Allí esta el archivo texto.txt pedido, se pueden pedir varios archivos uno atrás de otro, sin tener que volver a tipear el IP y puerto. Este es un simple ejemplo y se podría mejorar muchísimo, pues si uno pide un archivo que no existe, no hay verificación de ello, y trata de bajarlo e igual dice download complete, a medida que transcurran las partes iremos avanzando y veremos mejores ejemplos que abarcan todos estos puntos que aun nos quedan pendientes. Hasta la parte siguiente Ricardo Narvaja
FTP : TRANSFIRIENDO ARCHIVOS EN INTERNET En la parte anterior hemos visto como funciona el tema sockets a bajo nivel, ahora comenzaremos a usar las librerías de Python que por supuesto en el trasfondo también usan sockets, pero están armadas usando protocolos de alto nivel de forma de que el trabajo de sockets generalmente es invisible al usuario y se realiza como dirían los actores tras bambalinas jeje. Los protocolos corren encima de sockets, pero las librerías tratan de facilitar el trabajo de programar los sockets manualmente como vimos en la parte anterior. Uno de los protocolos mas usados en Internet es el protocolo FTP, que es un modelo de conversación de alto nivel, que esta basado en el intercambio de strings y archivos sobre sockets. Usando FTP nosotros podemos realizar la misma tarea que el script getfile.py de la parte anterior, pero en forma mucho mas simple, y tendríamos mayores posibilidades como la de subir archivos al server, navegar por directorios, realizar una autenticación de quien se conecta al mismo, etc. Vamos a un ejemplo realista en un script llamado getone.py para bajar un archivo de un server FTP.
Allí vemos la conexión mas simple, por supuesto con este script, aun no podemos navegar solo loguearnos y bajar un archivo que sabemos que esta allí, en este caso uso un FTP server público de la universidad de Vigo, allí al inicio están los datos del mismo, la variable sitename tendrá el nombre del host o IP donde entraremos, la variable filename, tendrá el nombre del archivo que bajaremos, también se puede pedir otro archivo tipeandolo como argumento y machacará el nombre de archivo que está puesto por default, la variable dirname tiene el directorio donde esta el archivo a bajar, en esta caso como es en el directorio mail se usa un punto.
Usaremos user=anonymous y como password hay que poner un mail que puede ser inventado en mi caso uso
[email protected] y me deja entrar jeje, estos datos se guardarán en una variable llamada userinfo, ya aclararemos mas adelante porque nos pide el password usando getpass en vez de raw_input y que diferencia hay entre ambos. Si ejecutamos el script en una consola, bajará el archivo benvido.msg que tiene el mensaje de bienvenida que si lo arrastro y suelto en un bloc de notas podre leerlo.
Para conectarse a un FTP lo que hay que hace el script es realizar una instancia en ftplib.FTP, pasandole como argumento la string que representa el host o IP del FTP al que queremos ingresar eso el script lo hace aquí:
Vemos que solo por haber sido importado con el método from no necesitamos usar ftplib.FTP, en el caso de que la hubiéramos importado con import ftplib, allí sería necesario poner el nombre del modulo. Vemos que realiza como dijimos una instancia en dicha clase FTP
Vemos que se podría haber pasado los parámetros de user y password directamente en la instanciacion de la misma, pues tiene esa posibilidad, de cualquier forma, en el script se lo hace en varios pasos para no complicar. En nuestro caso usa primero el método login al cual le pasa el user y password para loguearse, vemos que colocando el cursor encima del método WING nos muestra la ayuda del mismo.
Como vemos se le pasa el user y password necesarios para loguearse, luego esta el método cwd para cambiar de directorio si fuera necesario.
Luego setea si el FTP es pasivo o no, en nuestro caso nonpassive esta puesto a Falso, así que sera pasivo.
Como no pasivo es falso, no entra en el if a setear que sea activo, y por lo tanto será pasivo, el método que usaría para setearlo activo es set_pasv que acepta los argumentos True o False.
Al no entrar y cambiar nada se usa por default el modo pasivo, luego como ya vimos va a bajar el archivo, lo cual se hace usando el método retrbinary.
Este método tiene tres argumentos el primero es el comando standard para bajar archivos en el protocolo FTP que es RETR mas el nombre del archivo a bajar.
El segundo argumento es una función callback que se ejecutará cuando se baje cada bloque, en este caso usará la función localfile.write para ir escribiendo bloque a bloque bajado en el archivo que estaba abierto localmente en nuestra maquina para escritura.
Y el tercero es el tamaño del buffer donde guardará cada bloque que bajara y al terminar de bajar cada bloque se llama a la función anterior, en este caso se pone como tamaño máximo un buffer de 1024 bytes o sea que cada vez que se llene el buffer con 1024 bytes, se llama a la función write que lo escribe al archivo abierto localmente, así se repetirá tantas veces como sea necesario hasta que se termine de bajar todo el archivo. Vemos que en vez de usar raw_input para pedirnos que tipeemos nuestro password en la consola, usa una función que importa de un modulo llamado getpass que viene incluido en Python, y es similar a raw_input, solo que no muestra en la consola lo que vamos tipeando, para seguridad de que no se vea el password en la misma, allí en la aclaración le llama ECHO o ECO a la propiedad de repetir lo tipeado en la consola, para que se vea como hace raw_input, en este caso getpass, no
muestra lo que vamos tipeando o sea no hace ECO, por eso se usa en el caso de passwords, porque si hay alguien mirando lo que hacemos verá nuestra clave y eso no esta bueno, jeje.
Luego que se baja todo el archivo se cierra la conexión usando el método quit() y se cierra el archivo local con close().
Y esto es todo, el trabajo de los sockets no se ve, y esta oculto tras los métodos de ftplib que simplifican la tarea. Antes de continuar la parte siguiente con mas FTP y mejores scripts, seria interesante que arranquen su FTP SERVER que hicimos con el FILEZILLA y si tienen una sola maquina se conecten a ella con este script usando localhost como nombre de host, y si tienen dos maquinas con IPs diferentes, intenten bajarse un archivo que tengan, pasandole el IP como host, para practicar cambiando los valores y bajando diferentes archivos, es muy sencillo modificar el script anterior para ello, solo tienen que editar el host cambiarlo por localhost o el IP de la otra maquina, luego el nombre del archivo a bajar y el directorio donde se encuentra y luego el user que tiene su server para loguearse y al correr el script tipear el password correcto, para poder loguearse al mismo, así que tienen tarea para practicar. En la parte siguiente seguiremos con el protocolo FTP y como mejorar nuestro script básico, con mejores opciones. Hasta la parte siguiente Ricardo Narvaja
USANDO URLLIB PARA BAJAR ARCHIVOS En la parte anterior vimos la forma de realizar un script sencillo usando FTPLIB, que obviamente como vimos en un modulo para poder conectarse a un server FTP. Existe otro modulo que es mas amplio llamado URLLIB, que permite conectarse y bajar archivos entre otras cosas, pero no tiene la limitación que solo puede conectarse a servers FTP, sino que puede conectarse a cualquier dirección URL valida de Internet, la misma puede comenzar con FTP://, puede también ser una WEB que comience con HTTP://, y varias posibilidades mas. Por lo tanto este script siguiente realiza en principio el mismo trabajo que el de la parte anterior solo que usando el modulo mas general URLLIB para conectarnos a un FTP, en vez del mas especifico FTPLIB, se llamará getone-urllib.py.
Vemos que es sencillo su funcionamiento, tenemos luego de las importaciones de os, getpass y lógicamente de urllib que utilizará, la asignación del nombre del archivo a bajar a la variable filename y la obtención del password como vimos en la parte anterior usando getpass en vez de raw_input para que no se vea en la consola lo que tipeamos. Luego arma la URL en el caso de FTPs con password se puede acceder a ellos usando la siguiente convención. La URL tendrá al inicio ftp://, luego vendrá el user y password separados por dos puntos, luego la letra arroba (@) y la dirección ftp, por supuesto si queremos bajar algo deberemos agregarle la barra (/) y el nombre del archivo a bajar a continuación, en este caso la URL sería: ftp://anonymous:
[email protected]/filename
El problema es que el password se tipea en el momento según el FTP donde queremos entrar y el nombre de archivo a bajar puede cambiar también por lo tanto en el script se arma esta misma URL, usando format string, para completarle los datos.
Vemos que al final de la URL se agrega luego de un punto y coma type=i, eso lo aclararemos mas adelante cuando estudiemos bien las estructuras de las URLs. Bueno una vez armada la URL con el user y password correcto, el nombre del ftp, y el archivo a bajar se llama al método urllib.urlopen al cual se le pasa como argumento la URL y devuelve un objeto como si hubiéramos abierto un archivo, pero en este caso estará leyendo de un archivo remoto al cual se le puede aplicar read() para leer su contenido.
Allí lo lee usando read(), vemos que justo antes había abierto un archivo local.
Así que en dicho archivo usando write() guardara lo leído del archivo remoto, y luego cerrara con close(), tanto el archivo local, como el remoto.
Aquí vemos el script en funcionamiento, por supuesto nos pide el password el cual no muestra en la consola, y luego baja el archivo si existe, mostrando la URL que armamos para ello.
Por supuesto este script es mas genérico y sirve para cualquier caso de bajadas de archivos, solo modificando para que arme una URL tipo http:// y la dirección del archivo a bajar lo bajará de cualquier dirección de Internet tal cual lo baja un navegador, al cual le tipeemos la misma URL en su barra de navegación. Vemos que en el script nos aclara que existe otra forma breve usando el método urlretrieve() urllib.urlretrieve(remoteaddr, filename)
O sea con dicho método no debemos preocuparnos por abrir un archivo local ni de escribir nada, el mismo método realiza todo el trabajo y baja el archivo si le pasamos como argumentos la misma URL y el nombre del archivo, allí vemos en la imagen como comentamos las ultimas cinco lineas del script que hacían todo ese trabajo a mano y igual funciona perfectamente.
O sea este ultimo método es muy útil si queremos bajar y guardar un archivo rápidamente, pero no es tan útil como el primero si necesitamos procesar los datos que leemos del archivo remoto inmediatamente en vez de guardarlos. Por supuesto URLLIB tiene muchísimos mas métodos y usos que veremos mas adelante en profundidad, por ahora solo usamos un único aspecto, que es el de bajar archivos fácilmente sea de ftps o webs. Como en la parte anterior me gustaría que practicaran bajando archivos usando este script, de su propio FTP SERVER, probando con diferentes directorios, si tienen una sola maquina usando localhost y si tienen dos maquinas, poniendo el IP correspondiente para conectarse a la misma, dentro de la URL. Aquí ven el ejemplo para bajar de mi propio server, usando localhost y bajando un archivo llamado screenshot1.png que esta en el directorio raíz del mismo.
Allí vemos que al armar la URL, puse el usuario que en mi caso es ricnar456, la dirección del mismo que es localhost (si es remoto deberán poner la IP)
Y vemos que funciona perfectamente practiquen con su propio FTP server cambiando nombres de archivos, y si tienen dos maquinas haciéndolo en forma remota. Ahora que ya vimos tanto FTPLIB y URLLIB para bajar archivos en la próxima parte seguiremos con la mejora de scripts para FTPs, y hacer que no solo bajen archivos si no que puedan subir archivos a los FTPs que lo permitan y mas variantes con scripts mas elaborados sobre el tema. Hasta la parte siguiente: Ricardo Narvaja
MAS SOBRE FTP Lo que haremos serán dos scripts llamados getfile.py y putfile.py para bajar y subir archivos de un FTP un poco mejores usando FTPLIB.
Veamos como funciona, antes que nada digamos que es casi el mismo código que usamos antes solo un poco mejor programado para hacerlo reusable, se puede importar de otros módulos y llamar a la función getfile() pasandole los parámetros que se quiera para bajar un archivo, ya veremos eso pero antes veamos como funciona.
Vemos que en este caso importa ftplib usando la forma from xxxx import yyy por lo cual no será necesario escribir ftplib.FTP, solo FTP y ademas importa de os.path la función exists, para verificar que el archivo que se va a bajar no existe ya localmente.
Vemos que define una función llamada getfile() a la cual se le pasan todos los argumentos necesarios para bajar el archivo, eso quiere decir que si este modulo es importado por otro, podrá reusarse dicha función, pasandole los datos necesarios para conectar al FTP y bajar el archivo, en este caso como lo corremos como principal, la función es llamada con estos datos.
Vemos que user será una tupla que guardara el nombre de usuario y el password que obtendrá con getpass, le pasaremos también como argumento a la función el nombre de archivo a bajar, el directorio donde se encuentra y el host del ftp a conectarnos.
Vemos que ademas de esos 4 parámetros hay dos que tienen valores por default, que son verbose=True y refetch=False, como viene por default significa que al estar verbose=True, imprimirá información de lo que está haciendo, si fuera verbose=false no lo haría y en el caso de refetch=False significa que si el un archivo con el mismo nombre es detectado que ya existe en nuestra maquina en el mismo directorio donde debe guardarse el que se va a bajar, no se reintentará bajarlo, si fuera refetch=True, lo bajaría nuevamente a pesar de ya existir localmente.
Si el archivo ya existe y no ponemos refetch como True, directamente sale un cartel diciendo que ya lo tenemos o 'already fetched', si no va al else a bajarlo, luego si verbose es True imprime 'Downloading' y el nombre del archivo, si no no imprime nada y baja directamente en forma silenciosa.
Vemos que la parte que conecta al FTP es similar al script anterior, se instancia en FTP, pasandole como argumento el host, luego se usa login para pasarle la tupla user y password, luego cwd para cambiar de directorio si fuera necesario, y luego retrbinary, pasandole 'RETR', mas el archivo a bajar, y luego la función callback que se llamara que es write, que escribirá los bloques de tamaño 1024 en el archivo local que fue abierto con open justo al inicio de esta rutina. Vemos que todo esto lo realiza dentro de un try que no tiene except, pues no es obligatorio, y cuanto termina va a finally donde cierra el archivo local y si estamos en modo verbose imprime 'Download done.'
Vemos que si voy al directorio donde esta el script o si lo guarde en la carpeta de módulos de Python, puedo importarlo, inclusive desde otro script, en este caso lo realice en forma interactiva, usando como FTP SERVER el que cree en FILEZILLA, y luego de importar el modulo getfile uso la función getfile(), pasandole los parámetros en el orden correspondiente, primero la string con el nombre del archivo a bajar, luego la del host, luego el punto para mostrar que es el directorio raíz donde se encuentra el archivo, y al final la tupla de user y password para ingresar al FTP. Vemos en la imagen que lo baja perfectamente y si quiero reintentar bajarlo, me dice que ya esta bajado y no lo hace.
A no ser que le pongamos el parámetro refetch=True para que no use el valor por default que era False.
Allí vemos que en ese caso lo vuelve a bajar y si ponemos verbose=False no imprime información de lo que hace.
Ahora haremos un script para subir archivos a un FTP que tenga permiso de subida obviamente si no no podremos hacerlo se llamará putfile.py.
Vemos que es muy similar al anterior, en este caso tiene una función llamada putfile() a la cual se le puede pasar los parámetros necesarios para conectar a un FTP y subir un archivo, en este caso como el FTP de VIGO no deja subir archivos lo hago en mi propio FTP SERVER del FILEZILLA usando localhost. Vemos que el nombre del archivo a subir se pasa por argumentos.
Y luego tenemos el site o host, en este caso localhost, el directorio donde lo subirá, y el user y password en una tupla que usa getpass para obtener el password.
Vemos que en este caso solo tiene la opción verbose que viene seteada por default como True y dentro de la función abre un archivo local como lectura binaria, y conecta al FTP usando ftplib.FTP ya que ftplib fue importado con import ftplib. Luego el consabido método login para loguearse pasandole la tupla de user y password y cwd para setear el directorio donde escribiríamos y luego el método storbinary el cual sirve para subir archivos y al igual que retrbinary tiene tres argumentos, el primero 'STOR' mas el nombre del archivo a subir, luego el objeto archivo que esta abierto para leer, y el final el tamaño del buffer que guardara los bloques que subirá. Probemoslo a ver si funciona:
Allí subí un archivo llamado conexión.png que tenia en mi escritorio.
Allí vemos que lo subió al directorio raíz del FTP SERVER que en mi caso era C:\SHARE. Si no tengo ganas de editar los datos que trae por default para conectarme y quiero usarlo en otro server, lo importo desde otro script para intentar hacer la conexión entre dos maquinas con diferente IP para probar si puedo subir un archivo de una maquina a la otra que tiene el SERVER.
Ahí tengo tanto getfile.py como putfile.py en mi maquina HOST y en la maquina GUEST tengo el FILEZILLA SERVER Importe en Python el modulo putfile y llame a la función putfile() pasandole los argumentos necesarios como el nombre del archivo a subir, el IP del server, el directorio donde lo subirá y el user y password en una tupla y lo subió perfectamente, en la carpeta raíz del server vemos que el archivo esta allí.
Así que ya tenemos dos scripts que nos permiten subir y bajar archivos a un FTP SERVER, sin necesidad de editarlos para cambiar los datos, pasándoselos en forma interactiva a la función. Podemos hacer un par de scripts útiles mas que suban y bajen carpetas enteras de un FTP, lo cual puede servir si uno mantiene un sitio WEB, al cual periódicamente hay que actualizar subiendo y bajando carpetas completas que tenemos en nuestra maquina. El que baja carpetas enteras se llamará downloadflat.py
Aquí lo puse y le coloque los datos para que baje los archivos de la carpeta raíz del server, me conectare desde la misma maquina como localhost. Pondré el script dentro de una carpeta así baja todo allí.
Ahora lo corro
Vemos que el script fue bajando todos los archivos que encontró pero da error cuando encuentra una subcarpeta .
Veamos si lo podemos arreglar.
Ponemos la lectura del archivo remoto dentro de un try, colocamos un except para que borre el archivo local del mismo nombre al fallar la lectura, así no quedan archivos incompletos o erróneos,
pero así funcionará y bajará todos los archivos de la carpeta sin bajar las subcarpetas. C:\Documents and Settings\ricnar\Escritorio\TEMP>python downloadflat.py Password for ricnar456 on localhost: Clean local directory first? y connecting... deleting local 04-SERVER MANEJANDO MULTIPLES CLIENTES A LA VEZ.rar deleting local 05-USANDO SOCKETS PARA CREAR UN SIMPLE SERVIDOR DE ARCHIVOS.rar deleting local 44-VIENDO Y PROCESANDO IMAGENES CON PIL.rar deleting local ActiveState.Komodo 4.3 for Windows BEAN.rar deleting local conexion.png deleting local downloadflat.py deleting local eBP Released Info.txt deleting local echo-server.py deleting local ETAPA 2 deleting local file.png deleting local FileZilla_Server-0_9_25.exe deleting local ftp viejo deleting local fwdrecursopythondia3material.zip deleting local getfile.py deleting local Learning Python, 2nd Edition.chm deleting local Programming_Python_3rd_Edition.rar deleting local putfile.py deleting local screenshot1.png deleting local screenshot2.png deleting local screenshot3.png deleting local screenshot4.png deleting local screenshot5.png deleting local screenshot6.png deleting local screenshot7.png deleting local thread-server.py downloading 05-USANDO SOCKETS PARA CREAR UN SIMPLE SERVIDOR DE ARCHIVOS.rar to . \05-USANDO SOCKETS PARA CREAR UN SIMPLE SERVIDOR DE ARCHIVOS.rar as ? downloading conexion.png to .\conexion.png as image downloading eBP Released Info.txt to .\eBP Released Info.txt as text downloading echo-server.py to .\echo-server.py as text downloading file.png to .\file.png as image downloading FileZilla_Server-0_9_25.exe to .\FileZilla_Server-0_9_25.exe as appl ication downloading fwdrecursopythondia3material.zip to .\fwdrecursopythondia3material.z ip as application downloading getfile.py to .\getfile.py as text downloading Learning Python, 2nd Edition.chm to .\Learning Python, 2nd Edition.c hm as ? downloading PEPE to .\PEPE as ? downloading Programming_Python_3rd_Edition.rar to .\Programming_Python_3rd_Editi on.rar as ? downloading putfile.py to .\putfile.py as text downloading screenshot1.png to .\screenshot1.png as image downloading screenshot2.png to .\screenshot2.png as image
downloading screenshot3.png to .\screenshot3.png as image downloading screenshot4.png to .\screenshot4.png as image downloading screenshot5.png to .\screenshot5.png as image downloading screenshot6.png to .\screenshot6.png as image downloading screenshot7.png to .\screenshot7.png as image downloading thread-server.py to .\thread-server.py as text Done: 20 files downloaded. Vemos que me pregunta si quiero vaciar la carpeta donde esta el script, así que cuidado con eso si tipean Y estando en el escritorio por ejemplo, les puede borrar archivos importantes, así que mejor hacerlo en una carpeta separada. El problema que tiene con las subcarpetas se puede subsanar fácilmente, el tema esta en el uso del método nlst()
Dicho método creará una lista de nombres con los archivos y subcarpetas por igual, y el script por cada uno de esos, crea siempre un archivo nuevo en vez de una carpeta, así que deberíamos corregir eso.
Un primer paso seria que cuando da error porque es una carpeta luego de borrar el archivo, cree una subcarpeta con ese nombre, el script es muy sencillo y pueden ustedes mismos, entender como funciona.
Este es similar al anterior tratara de subir una carpeta entera al server, veamos si tenemos problemas con las subcarpetas si no le colocamos un try como al otro.
Vemos que tiene el mismo error, así que haciéndolo dentro de un try - except lo arreglaremos. Con esto funcionara el que quiere, puede borrar el archivo que quedara mal creado dentro del except eso se los dejo para ustedes.
Ahora funciona perfectamente subiendo los archivos de la carpeta que le indiquemos a la del server. Por supuesto se pueden hacer scripts que suban todos los archivos de una carpeta con sus subcarpetas recursivamente, pero por ahora no veremos eso, mas adelante lo haremos. Hasta la parte siguiente Ricardo Narvaja
PROCESANDO MAILS - POP Continuaremos con los protocolos de alto nivel usados en Internet para recibir y enviar mensajes de mail, POP y IMAP para leer mails de los servers y SMTP para enviar nuevos mensajes. Uno normalmente no necesita conocer demasiado en profundidad estos protocolos cuando se envían y reciben mensajes desde los programas específicos para ello como por ejemplo el Microsoft Outlook y sistemas webmail, los cuales generalmente hacen el trabajo sucio por uno, ocultando el trabajo de los sockets. Como cuando estudiamos FTP, el manejo de emails consiste en comandos preformateados y streams de bytes que se envían sobre sockets y puertos (generalmente puerto 110 para POP y 25 para SMTP), pero también como en el caso de FTP, Python tiene librerías standard para simplificar el proceso de todos los aspectos del procesamiento de mails. Los módulos en Python encargados para ello son: poplib y imaplib para recibir mails smtplib para enviar mails el modulo email para construir mails desde la respuesta cruda del server Antes de que existieran las soluciones de webmail de gran capacidad, para personas que accedían a un mail tipo POP3 desde diferentes lugares, se les hacia difícil manejarse, pues si bajaban los mails en alguna maquina, estos quedaban en ella y en otra maquina no podían accederse, la mayoría de los proveedores de Internet, estaban contentos y sugerían que uno use programas como MICROSOFT OUTLOOK de forma de tener siempre sus casilla vacías, y no tener que mantener mails viejos, pero esa solución era muy complicada para gente que viajaba o trabajaba en diferentes maquinas, los cuales la mayor parte de las veces usaban conexiones telnet para bajarse los mails y leerlos sin borrarlos de los servers, para poder acceder a ellos en diferentes lugares y maquinas. Luego de que la mayoría de los ISP quitara la posibilidad de hacer telnet para bajar mail, la siguiente posibilidad eran pequeños scripts de Python que permitieran leer los mails y contestarlos sin usar los farragosos y pesados programas que borraban los mails de los servers, en cualquier maquina que tuviera Python podía leerse y enviarse mails con pequeños scripts o desde la misma consola de Python y sin borrar los mismos de los servers, ademas que dichos scripts podían manejar mejor los adjuntos que en el caso de telnet y tenían mayores posibilidades. Lo primero que haremos sera un script que contendrá los datos de una cuenta de mail con sus servidores POP3 y SMTP, así como la dirección de mail para ser usado por los restantes scripts fácilmente se llamará mailconfig.py. Aquí tenemos la versión del libro la cual debemos adaptar a nuestros propios datos de mail, es obvio que la configuración de mail no puede tipearse cada vez que se lea o envíe uno, por lo cual debe estar guardada en algún lugar o archivo.
Lo dividí en dos partes ya que es muy largo, pero vemos que contiene todas las necesidades de configuración del mail, guardadas en un archivo.
Bueno creamos una cuenta para practicar con estos tutoriales, igual pueden usar su cuenta particular reemplazando los datos por los propios, pero bueno, es una forma de poder todos probar con datos similares.
Vemos que para que los mails se envíen por POP3 hay que poner esta tilde en la configuración en la web del mail.
Ahí están los datos de los servidores POP3 y SMTP de la cuenta para poder editar nuestro archivo. Allí mismo en la web nos avisaron que nos envían un mail de bienvenida, así que si hacemos nuestro script que funcione en forma correcta podremos leerlo con el.
Allí vemos como guarde los datos que me dio al registrarme en este script. Bueno ahora deberemos hacer el script que lea el mail usando POP3 se llamará popmail.py.
Ademas de poplib por supuesto importa mailconfig que acabamos de crear para leer la configuración de la cuenta a la cual se conectará.
Vemos que si lo usamos nos pide el password para POP3 y una vez que aceptamos se conecta y baja el mail que es un mail en formato crudo.
Normalmente los mails no son tan decorados como este mail de bienvenida, igual podemos copiar lo que esta entre los guiones en un archivo vacío y guardarlo con extensión html y lo veremos en el formato original, hasta que hagamos un lector que pueda mostrar directamente en html. Puedo hacer un txt y cambiarle la extensión a HTML y pegarle dentro todo el código.
Puedo hacerlo con el notepad o wordpad es lo mismo, lo guardo y ejecuto.
Bueno normalmente los mails no son tan sofisticados y se pueden leer directamente, enviemosle un mail a dicha cuenta.
Leamoslo ahora con nuestro script.
Vemos que aun esta el anterior, al apretar ENTER vuelve a aparecer y luego la segunda vez sale el nuevo, ya veremos mas adelante como mejorar la legibilidad del texto, pero ahora veamos como funciona el script. Luego de importar los datos del modulo mailconfig, para establecer la conexión con el server lo que se hace es instanciar en poplib.POP3, pasandole como argumento el mailserver POP3.
Y luego a dicha instancia se le aplican los métodos user() y pass(), para loguearse, recordamos que el password lo pide interactivamente con getpass(), si no queremos tipearlo, se podría guardar como el password de SMTP (que es el mismo en este caso) en el archivo de configuración y aquí ingresar ese dato en vez de solicitarlo con getpass().
Luego vemos que con el método getwelcome() obtiene el mensaje de bienvenida del mail server y lo imprimimos. Luego usando el método stat(), nos devuelve una tupla que es la cantidad de mensajes que hay en el server y la cantidad de bytes lo cual se imprime también y luego usa list(), que muestra lo remanente antes de recibir el mail.
Vemos que luego de aceptar con ENTER en el raw_input() se entra en un for que se repetirá por la cantidad de mensajes que haya, y con el método retr(), lee cada mensaje lo cual es devuelto en una tupla de tres elementos, de la cual solo importa el segundo que es el campo mensaje, y el cual se imprime linea a linea en un for. Una vez que se termina de leer todos los mensajes se sale del for y se cierra con quit(). Como todos los que hacemos este tutoriales practicamos con el mismo server, es posible que en algún momento, dos lectores de mis tutes se quieran conectar al mismo tiempo, lo cual dará error al segundo que entre, por lo tanto hay que tener paciencia y si no sacar un mail propio. En la siguiente parte veremos como el modulo email de Python tiene herramientas para parsear el mismo y mostrarlo no en forma cruda como lo estamos viendo, si no, en forma legible. Por supuesto si no tenemos problema en tipear, podemos consultar el mail desde la consola de Python sin usar ningún script, debemos recordar eso si los datos del server POP3.
Vemos que con pocas lineas de código podemos obtener el mail que queramos, y podemos pedir con retr() sabiendo que en este caso hay dos mails, si le ponemos como argumento 1 devolverá el primero y si ponemos 2 el segundo, sin necesidad de leer todos.
Lo que si hay que saber es que los POP3 tienen un tiempo o timeout cuando nos conectamos, así que debemos tipear mas o menos rápido, si no nos echará por tiempo. >>> from poplib import POP3 >>> conn=POP3 ('pop.correo.yahoo.es') >>> conn.user('
[email protected]') '+OK password required.' >>> conn.pass_('practica') '+OK maildrop ready, 2 messages (34190 octets) (3607)' >>> conn.stat() (2, 34190) >>> conn.list() ('+OK 2 messages (34190 octets)', ['1 31885', '2 2305'], 17) >>> conn.retr(1) Allí vemos solo siete lineas de código y tengo el mail para leer allí bajado. En la parte siguiente continuaremos con la lectura de mails y le agregaremos el modulo email para una lectura mas agradable a la vista y que no sea tan cruda. Hasta la parte siguiente Ricardo Narvaja
PROCESANDO MAILS – SMTP Antes de comenzar en la siguiente parte con el modulo email que nos permite leer en forma sencilla y armar mails, como en el caso de la parte anterior, que gracias a poplib podemos recibir mails, lo mismo que en este parte gracias a smtplib, podemos enviarlos, aunque aun no vimos la forma de leerlos y armarlos en forma legible que será la parte siguiente. En este parte nos concentraremos en la forma de conectarnos a un SMTP server, para poder enviar un mail, lo cual haremos con un script que veremos a continuación que se llama smtpmail.py.
El centro del mecanismo del script esta dado de la misma forma que para POP3 con una instancia en este caso a smtplib.SMTP
Luego de realizar esa instancia a la cual se le pasa el nombre del smtp server que teníamos guardado en el modulo mailconfig.
Vemos como al inicio importa ese modulo, por lo cual tiene que estar en un path correcto para ellos o entre los módulos de Python. Si todo fue bien y no nos pide user y pass, se utiliza el método sendmail, el cual tiene tres argumentos el remitente o From, el destinatario o To y el texto a enviar o Text. Vemos que todos estos datos nos lo pide el script al inicio para que los tipeemos, ademas del subject que tendremos que tipear.
Y con esos datos vemos que primero al texto le da un formato.
Obtiene la fecha actual y mediante format string incluye la misma y el destinatario, el remitente y el subject en una string que dira: 'From xxxxxx To yyyyyy: Date :zzzzzz Subject;nnnnnn' Luego vemos que nos pide que tipeemos el texto del mensaje el cual mediante sys.stdin.readline() que es equivalente a raw_input iremos tipeando y ira agregando linea a linea.
Luego lo que ya vimos con esos datos realizara la instancia y se conectará, si falla deberemos agregarle los métodos para loguearse, porque hay SMTP servers que los piden y otros que no, intentaremos, de cualquier forma la respuesta del SMTP server la imprimimos si llega a ser un error sabremos porque fue.
Bueno ya que dio error, le agregaremos la forma de conectarse con user y pass los agregados al script original son:
Vemos que importamos getpass para poder pedir el password en forma oculta, luego importamos de mailconfig el nombre del usuario que usamos para el POP que es el mismo en este caso que el de SMTP así que funcionará y al final agregamos el uso del método login al cual se le pasa el user y password para conectarse antes de enviar el mail.
Allí veo que envié un mail desde la cuenta creada, a otra cuenta propia mía y que no hubo errores, luego de tipear el texto para salir y para que conecte a enviarlo, se debe tipear CTRL mas Z y ENTER, eso se hace para que no se tenga que salir apretando solo ENTER y el mensaje pueda tener varias lineas.
Allí vemos que el mail que envié, llego a mi otro mail de gmail, en forma perfecta. También podemos enviar los mails desde una consola de Python en pocas lineas de la misma forma que lo hicimos en el caso del POP3.
Como siempre el problema es el timeout, así que lo escribí en el bloc de notas y luego copie y pegue todo junto, así no hubo drama, cuando termino y se pauso aprete ENTER para enviar y listo. Si me fijo en el mail recibido, lo coloco en la carpeta SPAM por no tener subject, así que se ve que el formato de la string que se envía es importante, así que lo haré así.
Ahí lo envié nuevamente, veo que el campo del texto le doy formato al hacerlo entre comillas triples, pongo el FROM, en la linea siguiente el TO, en la tercera el SUBJECT, luego dejo una linea vacía lo cual es obligatorio, y al final pongo el texto del mensaje y cierro las comillas triples y veo que a diferencia del mail anterior este se recibió en forma correcta.
También corroboramos que en el script se uso el mismo formato ya que vemos que entre dato y dato hay un salto de linea (\n) y luego del subject dos saltos de linea (\n\n), así que es norma realizarlo en este forma y debemos adaptarnos a ello.
Si uno lo va a tipear desde la consola conviene armarlo en el bloc de notas al menos y cuando esta todo correcto copiarlo y pegarlo para no tener timeouts.
En la parte siguiente veremos que el modulo email realiza todo el trabajo de darle el formato correcto a los mails tanto para lectura como para el envío, con lo cual no deberemos preocuparnos por estos detalles, aunque es bueno conocerlos. Hasta la parte 11 Ricardo Narvaja
MODULO EMAIL PARSEANDO Y COMPONIENDO MAILS Como hemos visto en las partes anteriores podemos mediante poplib y smtplib enviar y recibir mensajes, pero armar manualmente el formato correcto es todo un tema, sobre todo cuando queremos enviar mensajes complejos, por ejemplo con adjuntos, o que no sean de texto solamente, y ahí la cosa se complica, por suerte existe el modulo email que salio en las nuevas versiones de Python que facilita todas estas tareas simplificándolas enormemente. Veamos en forma interactiva algunos métodos muy usuales del modulo email, pueden hacerlo en la consola de Python pero yo lo haré en la consola interactiva de Wing, para ver que métodos aparecen y tener el autocompletado. Recordamos que en la parte anterior para enviar un mail tuvimos que darle formato a mano a una string a la cual le pusimos saltos de linea, luego del subject dos saltos de linea etc. Para enviar un mail ya formateado en forma interactiva tendría que tipear ya existiendo el mensaje preformateado. from smtplib import SMTP conn=SMTP('smtp.correo.yahoo.es') conn.login('
[email protected]','practica') conn.sendmail('
[email protected]','
[email protected]',mensaje) Por supuesto no funcionará aun porque no existe mensaje, ahora como podemos con el modulo mail, formatear un mail de texto en forma sencilla para enviar allí en el lugar de mensaje.
Importo email y la clase Message
Habrá que instanciar un objeto Message en ella, realizamos la instancia.
Luego de tener el objeto m, debemos configurar el mensaje pasandole el destinatario, el remitente y el texto, eso se hace fácilmente.
Allí vemos que configurar el mensaje con el remitente y destinatario es muy simple, solo asignando como a un diccionario, a las keys ['from'] y ['to'], los valores correspondientes, luego quedaría el texto del mensaje, eso se hace con el método set_payload.
Allí le pasamos el texto del mensaje, por lo tanto ya tenemos configurado todo, el tema es que m es un objeto instancia y necesitamos convertirlo en una string para usarlo para enviarlo por mail, usando str() lo convierto en texto.
Vemos que le agregó la fecha y lo formateo, por supuesto al imprimirlo se ve perfecto, pues al imprimir respeta el formato.
Ahí abajo copie del bloc de notas donde los tenia guardados, los comandos interactivos para enviar un mail que vimos en la parte anterior, para enviar este mensaje ya creado.
Por supuesto lo envía perfectamente.
Lo único que no configuramos fue el subject pero también es muy fácil lo agregamos.
Ahora lo envío
Veamos como llega
Este seria el caso mas general después veremos sofisticaciones en los scripts para hacerlo, pero es la base de enviar y construir un mail simple en modo texto. Para leer lo recibido hay un par de variantes si recibimos un objeto s del server igual a la string que enviamos con el formato correcto, veamos como decodearla.
Allí tenemos en s una string formateada para enviar por mail si recibiéramos la misma, la leeríamos asi:
.Vemos que importamos Parser y instanciamos un objeto en dicha clase y al mismo le aplicamos el método parsestr(), usando como argumento la string recibida. De esta forma podemos leer las características del mail fácilmente como cuando se creo.
El tema en nuestra cuenta de mail que creamos es que los datos los reenvía en HTML y son MULTIPARTES por lo cual el Parser común de mails de texto no servirá para el mismo y es un poco mas complejo para hacerlo en forma interactiva, así que sigamos y mas adelante veremos los scripts que realizan ese trabajo. OBJETOS MENSAJE Vemos que al instanciar en la clase Message creamos un objeto mensaje que es el corazón el trabajo del modulo email, cada uno de estos objetos consistes en estas tres partes principales de información. Tipo El tipo del mensaje encodeado como MIME en forma de tipo principal y subtipo, esto que parece muy complejo sería por ejemplo 'text/plain' si es completamente de texto, 'text/html' si tiene contenido HTML ; 'image/jpeg' significa una foto en formato JPEG, 'multipart/mixed' significa que tiene varias partes diferentes. Headers Es un diccionario, como vimos al asignar valores en la forma m['from'] , lo que estábamos haciendo era en el diccionario vacío configurar los valores del mail, que serán las keys del mismo, y su valor asociado será en este caso el remitente del mail, de esta forma la configuración se puede asignar o leer fácilmente mediante las keys del diccionario ('from', 'to', 'subject') Content El payload o la carga del mensaje puede ser una o varias string para mensajes simples o una lista de objetos mensaje adicionales para los mensajes multiparte, aquí puede también incluir los adjuntos o attachs. Es un tema realmente árido, por ejemplo un mail con imágenes atacheadas puede tener un top-level
objeto Message (tipo 'multipart/mixed') con tres objetos mas en su payload, uno para su texto escrito (tipo text/plain), seguido por dos de tipo imagen para las fotos (tipo 'image/jpeg'). Vemos que es un tema que tiene sus aristas pero iremos paso a paso tratando de comprenderlo. De la misma forma mensajes que tienen ambos la alternativa de mostrarse como texto o como contenido HTML tendrán dos objetos Message en su payload uno 'text/plain' y otro HTML 'text/html', ademas el objeto mensaje principal de tipo 'multipart/alternative' Como vimos cuando creamos nuestro simple mensaje, nosotros creamos un objeto Message y luego lo convertimos en string para enviarlo, pero realmente nuestro objeto tiene estas las tres partes que vimos TIPO, HEADERS y CONTENT.
Allí tenemos m que es un objeto mensaje.(no usamos la string recordemos) >>> m.get_content_type() 'text/plain' >>> Vemos que tenemos métodos para trabajar con sus partes allí hay uno para poder averiguar su content_type, como todos estos métodos tiene su contrapartida set para setearlo. >>> m.is_multipart() False Si le preguntamos si es multipart nos dice que no >>> m.keys() ['from', 'to'] >>> m.values() ['
[email protected]', '
[email protected]'] Vemos que a pesar de ser una instancia se maneja los headers como un diccionario, y por supuesto tiene sus keys y valores asociados que seteamos.
Otro de los métodos convenientes es walk() que nos permite dentro de un loop, saltear al siguiente objeto mensaje que existe en el payload, ya tendremos oportunidad de usarlo en los scripts. Para poder agregar un attach a un mensaje hemos visto que hay que manejarse con multipartes, el modulo email viene preparado para ello instanciando en MIMEMultipart en vez de Message
Y seteando los valores de la misma forma que lo hicimos en el mail simple.
Podemos ponerle un subject lógicamente
Ahora podemos adjuntar un texto que irá incluido en el mail usando MIMETEXT y instanciandolo allí, lo mismo si es un archivo adjunto.
Allí instanciamos un texto en MIMETEXT y ahora haremos otras instancia con un archivo de texto vamos pasito a pasito, creo un archivo de texto con algunas palabras dentro, en mi caso en c:\data.txt
Y allí hice la segunda instancia. Ahora hay que agregar en el header que hay un attach, .con el método add_header
y se usa el método attach para adosarle al mail top las dos instancias sub1 y sub2 de MIMEText top.attach(sub1) top.attach(sub2) Listo ya esta todo configurado perfectamente solo falta pasarlo a texto para enviarlo con smtplib tal cual lo hicimos con str() la vez pasada, esta vez usaremos el método. as_string() que hace lo mismo.
>>> text 'Content-Type: multipart/mixed; boundary="===============0788113023=="\nMIME-Version: 1.0\nfrom:
[email protected]\nto:
[email protected]\nsubject: SIEMPRE PRUEBA\n\n--===============0788113023==\nContent-Type: text/plain; charset = "usascii"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\n\nnice red uniforms...\n\n-===============0788113023==\nContent-Type: text/plain; charset="us-ascii"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-Disposition: attachment; filename="data.txt"\n\npepe\nes\nun\ngenio\n\n--===============0788113023==--' >>> Vemos que la cosa no es tan sencilla para armar a mano como hacíamos antes de usar el modulo email para armar los mismos, poníamos tres saltos de linea y uno doble después del subject y listo, en mails sencillos eso puede hacerse, pero aquí sin la ayuda del modulo email la cosa se complicaría mucho realmente. Y luego ya podemos enviarlo con las cuatro lineas que teníamos guardadas para enviar por smtplib.
Si le doy a VIEW puedo mirar el contenido del archivo que envié o bajarlo si le doy a DOWNLOAD, esto mismo se puede hacer con cualquier tipo de archivo, sin problema. Quise antes de entrar de lleno en el tema de los scripts que realizan estos trabajos primero hacer un paneo del funcionamiento en forma interactiva para irnos aclarando de a poco, de cualquier forma explicaremos los scripts de la parte siguiente en profundidad para que no queden dudas como siempre. Hasta la parte siguiente Ricardo Narvaja
PYMAIL : UN CLIENTE DE MAIL DE CONSOLA Vamos a armar un script que una todo lo que hemos visto de mail en uno solo, que podrá realizar cualquier tarea de las vistas en las partes anteriores, como es un poco largo lo pondré completo como siempre, luego lo probaremos y analizaremos por partes, se llamará pymail.py.
Y bueno es bastante largo porque hace de todo jeje, o hace bastantes cosas, primero que nada veamos si funciona y como lo hace, vemos que no tendremos que tipear los datos del mail, pues importa mailconfig que era un archivo que guardaba los datos de la cuenta de mail, así que debe
poder localizar este, ya sea en el mismo directorio o en un path de módulos. C:\Documents and Settings\ricnar\Escritorio>python pymail.py Password for pop.correo.yahoo.es? [Pymail email client] Connecting... +OK hello from popgate 2.39.2 on pop003.mail.ukl.yahoo.com ('+OK 1 message (2347 octets)', ['1 2347'], 8) There are 1 mail messages in 2347 bytes Retrieving: 1 1: 2295 bytes From=>"Ricardo Narvaja" Date=>Sat, 5 Jul 2008 12:49:24 -0300 Subject=>prueba [Pymail] Action? (i, l, d, s, m, q, ?) l -------------------------------------------------------------------------------[, ] -------------------------------------------------------------------------------Ahí vemos el script en funcionamiento, al arrancarlo nos pide el password para nuestro mail ya que para pop3 se tipeaba con eco para que no se vea, y luego de que conecta y nos dice que hay un mail para leer nos da las opciones para interactuar con el según las letras que tipeamos. [Pymail] Action? (i, l, d, s, m, q, ?) l Por supuesto si queremos saber cual es cada letra apretando ? nos muestra el menú de ayuda. Available commands: i - index display l n? - list all messages (or just message n) d n? - mark all messages for deletion (or just message n) s n? - save all messages to a file (or just message n) m - compose and send a new mail message q - quit pymail ? - display this help text Vemos que la i muestra el indice de mensajes, la l sin ningún numero imprimirá en la consola los mensajes, si le ponemos un numero mostrará solo el mensaje correspondiente, la d borrara, la s guardará a un archivo, la m compondrá y enviará un nuevo mensaje , la q quitara pymail y la ?, nos mostrara la ayuda. Si apretamos la l vemos que hay un pequeño problema cuando mostramos los mensajes multiparte que lo podemos solucionar fácilmente. [Pymail] Action? (i, l, d, s, m, q, ?) l -------------------------------------------------------------------------------[, ]
-------------------------------------------------------------------------------Vemos que cuando le pedimos la l o sea que muestre el mensaje, en nuestro caso como es multiparte, nos muestra las dos instancias o sea la de texto y la de html, pero no muestra su contenido, eso se cocina aquí.
Eso solo funcionará como habíamos visto si el mensaje es solo texto y msg tendrá la instancia del mensaje que devolvió parsestr() como vimos en las partes anteriores, pero en el caso de ser multiparte msg es una instancia de la misma forma, pero al usar getpayload() como tiene dos payloads nos devuelve una lista con dos instancias nuevas una para cada payload el de texto y el html >>> msg >>> msg.get_payload() [, ] Que es lo que imprime el programa, y vemos como salida en la consola, si hubiera sido un mail con un solo payload no habría habido problema, al hacer getpayload(), imprimiríamos su contenido directamente pero en nuestro caso no, lo arreglaremos.
Allí lo debuggeo y estoy parado en el mismo, veo que msg es una instancia de mensaje.
Veo que el largo de msg.getpayload() me dice que es 2 o sea tiene dos payloads. >>> msg.get_payload() [, ] >>> msg.get_payload()[0]
Como es una lista de dos instancias con el subindice 0 y 1 puedo elegir la instancia que quiero, y de ella hallar el payload y ahí veo que la primera es tipo texto y la segunda tipo html.
Allí vemos como lo repare, mire el largo y si es uno que directamente lo imprima y si es mayor que uno en un for, imprime el get_payload() de cada una de las instancias de mensaje, si lo probamos.
Vemos que nos muestra las dos instancias primero la de texto que se ve perfectamente ya que al imprimir queda formateada y la html en segundo lugar. Tratemos ahora de enviar un mail lo cual se hace con la opción m. Vemos que también debemos arreglarlo pues en nuestro caso no se conecta si no le pasamos el password como vimos en la parte anterior.
Ahí esta usando el método login habíamos solucionado el tema le pasamos el mismo user y pass del pop3 pues es similar.
También tuve que colocar la variable del password como global para que pueda usarse en todo el script, porque sino tenia que cambiar las definiciones de las funciones, porque para la función que conecta no existía esa variable, así que con eso ya estaría listo. Probemos nuevamente:
Vemos que cuando escribimos una linea con un puntito solo y apretamos ENTER ahi termina el mail y se intenta enviar el mensaje, y al menos no dio error veamos si llego a mi mail.
Vemos que llego perfecto. El script a pesar de ser largo, no deja de ser una recopilación de lo que vimos en las partes anteriores:
Vemos que importa la configuración del mail de mailconfig, y luego de la modificación para hacer global la variable mailpswd que le agregue yo, para no tener que volver a pedir nuevamente el password ni cambiar medio script pues en la definición de las funciones no estaba contemplado. Vemos que lo primero que hace es llamar a loadmessages() y crear una lista con los mensajes que existen en el server y la guarda en msgList, para poder procesarla y no tener que depender del timeout del server, una vez leídos los tenemos guardados para procesar.
Vemos que loadmessages() lo primero que hace es llamar a una función connect() que esta definida justo arriba, a la cual le pasa el nombre del server, el user y pass para conectarse, dicha función retorna la instancia de conexión en POP3.
Vemos que imprime la lista con list() y luego usa stat() como habíamos visto en la parte de pop3 que nos devuelve una tupla con la cantidad de mensajes y los bytes que ocupan, lo cual imprime en la siguiente linea.
Vemos que luego en una lista inicialmente vacía, con retr() obtiene los mensajes y va apendeando, solo la parte de mensaje a dicha lista, separados por un salto de linea.
Al final devuelve mediante el return esta lista de los mensajes msgList.
Luego que ya tiene la lista de mensajes, llama a la función interact(), pasandole esta lista de mensajes.
Vemos que interact() es una función que mediante raw_input verá que opción elegís y según eso te derivará a la función correspondiente, pero al inicio de todo, llama a la función showindex(), a la cual le pasa la lista de mensajes.
Vemos que es una función que tomará la lista de mensajes y la recorrerá usando parsestr() para hacer una instancia de cada uno, y a dicha instancia como vimos en las partes anteriores, usando ['from'], ['to'], etc, imprimirá los headers de los mensajes.
Esta función no retorna valores solo imprime los headers de los mensajes, al retornar a la función interact() recordamos que la misma nos daba a elegir que queríamos hacer con los mensajes.
Por ejemplo si elegíamos la l que era para mostrar los mensajes llamaba a la función showmessage()
Allí también creaba una instancia de Message mediante el uso de parsestr(), y como vimos modificamos el script para que si era multiparte lo detecte y como get_payload() nos devolvía una lista de instancias, chequeábamos el largo de la misma, si era uno, no había problema y el script iba a imprimir el mismo, pero si era mas de uno, hacíamos un for, donde mediante subindices se imprimían los payloads de cada una de esas instancias que hubiera. Creo que no es un script muy complicado y como tarea pueden tracear y mirar como funcionan las otras opciones, que no son mas que lo que ya se vio en las partes anteriores recopilado. Vemos que para borrar mensajes usa dele()
También guardar los mensajes en un archivo es trivial usa open() y write() como cualquier hijo de vecino jeje.
Creo que no es necesario hacer paso a paso todo el análisis del script pues lo mas importante ya se ha visto, solo recordando que en la parte de enviar el mensaje la otra modificación que ya mencionamos fue:
Para que el script pueda loguearse con user y password. Hasta la parte 13 Ricardo Narvaja
13-NNTP- ACCEDIENDO A LOS NEWSGROUPS En los capítulos anteriores nos hemos enfocado en usar Python para conectarnos a FTP, leer mails y en el uso ftplib, poplib, smtplib, email, mimetools, urllib aunque sea en su uso mas básico, por supuesto que estos módulos tienen muchísimos mas métodos y posibilidades que las que podemos ver aquí, pero conociendo su uso básico, la consulta de la información en google sobre los módulos de Python, enseguida nos complementará lo que sabemos. Ahora veremos otro modulo que se utiliza para interactuar con los newsgroups y se llama como no podía ser de otra forma nntplib jeje. Como en los casos anteriores NNTP corre encima de sockets y tiene su definición standard para enviar mensajes y recibirlos, y por supuesto el modulo nntplib nos evitará tener que conocer en profundidad el manejo de sockets facilitándonos la tarea. El script que lee news se llamará paradojicamente readnews.py Normalmente nuestro proovedor de Internet suele tener un servidor de news al igual que los de mail que nos provee mediante el cual podremos tener acceso a las NEWS de mucha mejor calidad que los accesos gratuitos, de cualquier manera probaremos con un servidor gratuito de NNTP, hay muchos en Internet, algunos dejan leer sin registrarse, otros permiten postear, según el caso, aquí puse 'nntp.aioe.org' y el grupo puse cualquiera porque desconozco los grupos que tiene, igual si no coincide en este caso, va a buscar el que mas coincide y si no irá al grupo general, si tuviéramos bien definido otro grupo lo colocaríamos en la configuración.
Vemos que la forma de conectarse no difiere mucho de la que usamos para FTPs y MAILS.
Vemos que importa de nntplib la clase NNTP y realiza una instancia en ella pasando el nombre del server donde se conectará. Esta parte la vemos aquí cuando lo probamos:
Vemos que conecto y encontró un grupo del tema que pusimos y imprimió que hay 2190 artículos para leer vemos que en el script imprimía esa info justo aquí.
Vemos que al inicio se pueden pasar argumentos y si no se los pasamos usa los valores por default. servername, groupname, showcount = sys.argv[1:]
Si no hay argumentos dará error e ira al except donde se asignarán los valores por default, quiere decir que este script se puede usar para leer de otro servidor de news, otro grupo y otra cantidad de post que la que esta asignada por default allí. En nuestro caso el valor por default de showcount es 10, para que muestre los últimos 10 posts que haya en el grupo.. Como entre los valores que me devuelve esta el numero del primero y ultimo post.
Esos valores los mande a imprimir así que en mi caso son 2190 post en total, el primero con el numero 123598 y el ultimo con el numero 125826.
Si alguno hace la resta vera que no da 2190, da un poco mas posiblemente haya algunos post que fueron eliminados por no estar de acuerdo con las políticas del newsgroup o bien no saben restar jeje.
Allí hará la resta de el numero del ultimo post menos la cantidad que quiero leer menos 1, en mi caso será 125826 – 10-1 o sea que el primer post que leeré será el 125817 como vemos allí en la consola.
Luego a la instancia de conexión se le aplica el método xhdr.
Vemos que se le puede pasar un rango XXXX- YYYY para que devuelva los headers de dichos post, en mi caso el rango sera desde fetchfrom a last. Si lo debuggeo en este punto en el DEBUG PROBE del WING veo >>> reply '221 subject matches follow (NOV)' >>> subjects [('125818', 'Re: MethodChain'), ('125819', 'Re: Question'), ('125820', 'Re: Question'), ('125821', 'Re: Not entirely serious: recursive lambda?'), ('125822', 'Genetic programming: pygene, pygp, AST, or (gasp) Lisp?'), ('125823', 'Re: matplotlib: Plotting a graph against time'), ('125824', 'Re: Genetic programming: pygene, pygp, AST, or (gasp) Lisp?'), ('125825', 'Re: MethodChain'), ('125826', 'Re: Problems with curses'), ('125827', 'Re: __del__ methods')] >>> Veo que subjects guarda una lista con los números de post y los subjects correspondientes.
Luego en un simple for que recorrerá toda la lista subject, la variable id y subj tomarán en cada ciclo los valores de cada campo, en el primer loop en mi caso será. [('125818', 'Re: MethodChain'), ....... o sea que id sera 125818 y subj será el subject correspondiente, por supuesto se siguen agregando post minuto a minuto, así que como me conecte varias veces mientras hacia el tutorial el primero a mostrar ya cambió, como cuenta a partir del ultimo y hay post nuevos, cambiará el primero a mostrar, pero siempre me pregunta si quiero leerlo o saltearlo.
Si aprieto 'y' mostrara el encabezado y luego me pregunta si quiero mostrar el cuerpo del post.
Este corresponde ya a otro mensaje pues volví a conectarme varias veces, si quiero leer el mensaje mas viejo deberé cambiar el valor de showcount a un numero mas grande.
Vemos que usando el método head pasandole como argumento el numero de mensaje a leer me devuelve los datos del encabezado del mismo.
>>> reply '221 125820 head' >>> num '125820' >>> tid '' >>> list ['Path: aioe.org!news.glorb.com!postnews.google.com!s50g2000hsb.googlegroups.com!not-formail', 'From: Paddy ', 'Newsgroups: comp.lang.python', 'Subject: Re: Question', 'Date: Sat, 19 Jul 2008 23:54:33 -0700 (PDT)', 'Organization: http://groups.google.com', 'Lines: 14', 'Message-ID: ', 'References: ', 'NNTP-Posting-Host: 77.100.79.81', 'Mime-Version: 1.0', 'Content-Type: text/plain; charset= ISO-8859-1', 'Content-Transfer-Encoding:
quoted-printable', 'X-Trace: posting.google.com 1216536874 10183 127.0.0.1 (20 Jul 2008 06:54:34 GMT)', 'X-Complaints-To:
[email protected]', 'NNTP-Posting-Date: Sun, 20 Jul 2008 06:54:34 +0000 (UTC)', 'Complaints-To:
[email protected]', 'Injection-Info: s50g2000hsb.googlegroups.com; posting-host = 77.100.79.81; ', '\tposting-account=p34dwkAAAC60rn_Tuk_HKyjfq-hlvCF', 'User-Agent: G2/1.0', 'X-HTTP-UserAgent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.1) ', '\tGecko/2008070208 Firefox/3.0.1,gzip(gfe),gzip(gfe)', 'Xref: aioe.org comp.lang.python:125820'] >>> Vemos que usa list, dentro de un for lo recorrerá linea a linea.
Y dentro de otro for recorrerá showhdrs que era una lista que definió al inicio con los headers que quería mostrar, los cuales resalte entre el contenido de list.
Así que lee el primer campo de list en mi caso será >>> line 'Path: aioe.org!news.glorb.com!postnews.google.com!s50g2000hsb.googlegroups.com!not-for-mail' >>> >>> prefix 'From' Lo que hace es ver el largo de la palabra que busca en este caso From, va a ver si esta en la primera linea, como sabe que si esta ubicada, estará al inicio de la misma, corta justo en el largo de la palabra que busca, en mi caso como From tiene 4 letras se fija si las primeras 4 letras de la linea son From aquí.
O sea que el largo de prefix será el largo de From o sea cuatro, de esta forma hace un slice de la linea usando [0:4] los primeros cuatro caracteres de la misma y comparando a ver si son From , si es verdad, imprime los primeros 80 caracteres de la linea, si no va a buscar la siguiente linea, asi
hasta que encuentra la que corresponde a From, la imprime y salta a buscar el segundo header, o sea 'Subject'.
Cuando termina de imprimir las lineas que corresponden a estos headers, me pregunta si quiero mostrar el cuerpo del post, para lo cual a la instancia le aplica el método body pasandole como argumento también el id o numero de post.
Ahora en list devolverá el cuerpo del post >>> list ['On 20 Jul., 04:43, Michael Tobis wrote:', '', '> Can a lambda call itself without giving itself a name?', '', "Sure, use a fixed point combinator. I've just added this recipe:", '', 'http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/576366', '', '> Google was not my friend on this one, and I suspect there is no', '> answer.', '', "Even the Great Google can't help if you don't use the right", 'keywords ;)'] >> Al imprimirlo linea a linea en el for quedara legible.
Modificando apenas el script nos permitiría postear (siempre y cuando este permitido)
Una vez que armamos una lista con los cinco headers correctos como los que leímos del post anterior, usando el método post podremos subirlo a las news, de cualquier manera no abundaremos en detalles sobre eso, pero damos la información para que se vea que es posible, en google abundan los ejemplos de código para postear en los servidores de news creo que no es necesario mas sobre el tema, ya lo conocemos en forma básica. Hasta la parte 14 Ricardo Narvaja
14-HTTP -ACCEDIENDO A WEB SITES Las librerías standard de Python incluyen soporte para HTTP, que es el standard para transferir información en la web, este es el protocolo que nuestros navegadores (ej Internet Explorer,Firefox, Opera, Safari, etc) usan para leer y mostrarnos paginas que se encuentran en servidores remotos, a medida que surfeamos en la web. Esencialmente es solo transferencia de bytes sobre el puerto 80. Para comprender realmente la transferencia HTTP necesitamos saber algunos tópicos que se cubren mas adelante en este curso ( p ej: invocaciones de scripts y los esquemas de direcciones de Internet, etc) así que esta parte 14 no será muy útil para usuarios que no tengan ese conocimiento, de cualquier manera se aconseja leerla, sabiendo que mas adelante se explicará en profundidad esos puntos, luego de lo cual podrán revisitar esta parte con ese conocimiento ya adquirido. El modulo que automatiza el protocolo HTTP y permite leer paginas webs como si fuera un web browser se denomina httplib, y como siempre la mayor complejidad esta oculta por el funcionamiento de este modulo. Veremos un script que leerá un archivo de un HTTP (web) server se llamará http-getfile.py.
Allí vemos el inicio, luego de la importación de sys y httplib que son los módulos necesarios, y la asignación de una variable showlines que en este caso es 6 y será la cantidad de lineas a mostrar del archivo, el script ve si hay argumentos, para asignar a servername y filename, si no los hay dará error e irá al except donde se asignarán los valores por default del nombre del server y el archivo a leer.
Como lo debugeé sin argumentos, al llegar al print los valores de ambas variables tienen el nombre del web server y el archivo a leer por default.
Vemos que la forma de conectarse al web server no difiere mucho de las libs anteriores que vimos, instanciando en la clase HTTP, pasandole el nombre del server, ya tenemos una instancia llamada server a la cual le podemos aplicar los métodos que se necesiten, vemos en la imagen que hay que usar el método putrequest(), en este caso pasandole GET y como segundo argumento el nombre del archivo que buscamos. Ya dijimos que esto se aclarará un poco mas adelante, pero por ahora aceptemos que hay que llamar a estos métodos pasandole dichos argumentos, luego se usa el método putheaders() al cual le pasamos 'Accept' y el tipo de archivo que es 'text/html' y luego se usa el método endheaders()
para terminar con el header. Con esto ya conformamos un request valido para realizar, y tendremos una respuesta de parte del web server eso lo leemos aquí.
Vemos que el método getreply() me devuelve tres valores, el primero es el que me importa el código de error, si es diferente de 200 significa que no encontró el archivo y según el numero de error sabremos que paso, en nuestro caso, fue 200 así que el request fue exitoso. Vemos que en el if se fija si el código de error es diferente de 200 como en nuestro caso es igual, va al else donde leerá el archivo. Usando el método getfile() nos devolverá un objeto archivo, del que le pedimos cuando usamos GET o sea index.html, y a ese archivo le podemos aplicar los métodos usuales como si hubiéramos abierto un archivo en nuestra propia maquina.
Por ejemplo el método readlines para leer todas las lineas del mismo.
Vemos que devuelve una lista con las lineas del archivo las cuales puedo imprimir, en este caso imprimiré las primeras 6 solamente.
Allí vemos como usa la variable showlines como indice slice para solo imprimir las seis primeras lineas.
Vemos como el script puede ser usado en una web y el archivo a mostrar puede tener un path.
En este caso vemos que no le pasamos un nombre de archivo sino un path solo y me da un error en el request, lo corregimos en el siguiente.
Vemos que al agregarle un path valido del web server, con una carpeta donde existe un archivo, el cual en este caso se llama también index.html y existe, muestra su contenido también sin error. Si usamos el script que hicimos webserver.py en partes anteriores y lo ponemos a correr, con nuestro script actual podríamos leer también archivos del mismo.
Primero correríamos el webserver en una consola diferente, recordemos que tenia una subcarpeta cgi-bin con un script de Python
Por supuesto no lo halla pues no existe el archivo languajes.py si lo cambiamos a cgi101.py que si existe.
Vemos las respuestas del webserver, al principio dio error, cuando le pedimos un archivo que no existía pero luego al pedirle uno existente dio OK.
Y allí muestra su contenido HTML que seria el mismo que veríamos en un navegador aunque aquí
ya esta con formato, titulo REPLY PAGE y texto WHO ARE YOU?
Vemos que es la misma respuesta que nos dio nuestro lector, leyó el formato HTML lo que termina mostrándose.
Bueno es suficiente por esta parte seguiremos en la siguiente Ricardo Narvaja
USANDO URLLIB EN HTTP Cuando tratamos FTP vimos que en vez de usar ftplib, podíamos usar el modulo urllib para bajar un archivo de un FTP, ftplib es mas especifico para ello y tiene mas posibilidades, mientras urllib es sencillo y un poco mas genérico, en el caso de conectarse a una web como en la de la parte anterior, también podemos usar urllib en vez de httplib, será mas sencillo, aunque quizás no tenga todas las posibilidades de la librería especifica, pero para tareas sencillas servirá. Recordamos que cuando lo usamos en FTP, urllib nos devolvía directamente un objeto tipo archivo usando urlopen, aquí es similar veamos el script llamado http-getfile-urllib1.py
Vemos que es un script muy sencillo, lo que hace es intentar dentro de un try leer argumentos, alli se puede indicar el nombre de la pagina web y del archivo que queremos leer igual que en el script que usaba httplib, si no hay argumentos fallara y ira al except donde usará por default la pagina 'starship.python.net' y el nombre de archivo sera el /index.html. Luego en la variable remoteaddr se armara la url de dicha web, mediante format string.
Allí la armo
Luego usando el método urlopen y pasandole como argumento la url, nos devolverá un objeto archivo al cual le podemos aplicar los métodos tradicionales de archivo aquí utiliza readlines .
Como remotefile es un objeto archivo, aplicándole readlines guardará las lineas del mismo en una lista.
Luego las imprimimos en un for como en el script de la parte anterior, linea a linea.
Y listo tenemos impreso las primeras seis lineas del contenido del archivo ya que la variable showlines corta usando indice slice la lista de lineas, podíamos haber abierto un archivo local y guardado este contenido con write y habríamos bajado el archivo completo, ya conocemos esas diferentes posibilidades que nos da manejar un objeto archivo. Vemos que urllib se da cuenta en este caso que la url comienza con las palabras http:// en vez de cuando la usamos en ftps que comenzaba con ftp:// y se adapta para conectarse a una web HTTP en vez de a un FTP server. Vemos que este script al igual que el de la parte anterior funciona en forma bastante similar en los mismos ejemplos que mostramos.
Vemos que en este caso aun sin especificarle el .html como en la parte anterior, igual el modulo se da cuenta y muestra el contenido del archivo index.html, mientras que en el que usa httplib daría error, pues los request tienen que estar exactos en ese caso, urllib como maneja urls es un poco mas flexible.
Aquí en la parte anterior, nos había dado error al solo apuntar a una carpeta y no especificar index.html, en este caso urllib, si apuntamos a una carpeta tratara de buscar en la misma un index.html y si lo halla lo mostrará sin dar error. Si arrancamos nuestro webserver.py en una consola aparte.
Ahora podemos igual que en la parte anterior
y el web server acuso el uso que le dimos
Vemos que aun sin especificarle el lenguaje o poniéndole otro el contenido es siempre correcto, urllib se maneja muy bien con urls y es bastante flexible. Recordamos que cuando usamos urllib en FTPs si usábamos directamente el método urlretrieve para que guarde directamente en un archivo local la data que bajamos, es un método rápido pero no es tan conveniente si queremos procesar de alguna forma dicha data, lo veremos en el ejemplo httpgetfile-urllib2.py
El script es muy parecido al anterior solo tiene un poco de trabajo extra para determinar el nombre del archivo local donde se guardara la info, vemos que en este caso puede tener mas argumentos, al inicio solo usa los dos primeros los cuales extrae mediante indice slice.
Vemos que es similar que el ejemplo anterior solo que en este caso esta preparado para aceptar un argumento mas, el primero sigue siendo el nombre de la web, el segundo el archivo a bajar, y el tercero sera el nombre del archivo local con que guardaremos la data, por ahora hasta aquí armo la url de la misma forma que en el ejemplo anterior, en la variable remoteaddr y ignorando el tercer argumento.
Esta parte es agregada para manejar el nombre del archivo local, si el largo de sys.argv es 4 o sea que tiene tres argumentos ademas del nombre del script, en ese caso a la variable localname se le asigna sys.argv[3] que es el tercer argumento que no se había usado el que especifica el nombre del archivo local donde guardaremos la info. Vemos que si ese argumento no se pasa, usa urlparse.
Que nos devuelve datos del url que le pasamos como argumento, en este caso en path guarda el path remoto al que accedemos con el nombre de archivo.
os.path.split(path) es una función que devuelve una tupla, donde el segundo campo es todo lo que esta a continuación del '\' ultimo, esto lo hacemos porque si es un path tipo \pepe\jose\index.html nos devolvería lo resaltado o sea no importa que tenga mil subcarpetas, siempre sera lo que esta a continuación de la barra final con lo cual obtenemos siempre el nombre de archivo.
Como devuelve una tupla y solo queremos el segundo campo le agregamos el subindice
De esta forma si no se especifica el nombre del archivo local sera similar al remoto que bajamos.
Luego solo con esa linea que usa urlretieve el archivo se bajara y guardara si la paso con f6.
Veo que el archivo se guardo, en las lineas siguientes directamente lee de este archivo para imprimir su contenido.
Abre el archivo local con open y luego le aplica readlines, en este caso al no tener un objeto archivo con la data bajada como en el ejemplo anterior, tenemos que crearlo mediante open, sobre el archivo ya bajado, y luego imprimirá las primeras seis lineas del mismo dentro de un for.
Es importante ver que en el primer ejemplo urlopen devuelve un objeto archivo de la info remota que baja.
Mientras que en este se crea un archivo real y se guarda, por lo cual si necesitamos manejar los datos deberemos abrirlo con open y crear un objeto archivo del mismo.
Si lo corro de linea de comandos
Veo que funciona en forma similar al anterior salvo que ahora guarda el archivo.
Vemos que si hacemos doble click en el archivo bajado se muestra perfectamente en un navegador
Por supuesto recordemos que en consola solo imprimíamos las primeras seis lineas del mismo, pero cuando lo bajamos se baja entero, por eso se muestra completo en el navegador.
Vemos que le agregue un argumento mas para que me guarde el archivo con ese nombre pepe.html.
Por supuesto su contenido es similar al otro solo lo guardo con otro nombre.
Vemos que al pasarle un path con varias subcarpetas funciona perfecto y halla el nombre del archivo que esta a continuación de la ultima barra y como no le pase un argumento con el nombre lo guarda con el nombre que hallo en forma correcta.
En el caso de nuestro web server como es un archivo py al que llamamos, que es un cgi, y muestra contenido web, se bajara el archivo html que se muestra cuando uno accede como web, el problema es que lo guarda como cgi101.py lo cual no es correcto, pues debe ser html, así que le pondremos el nombre nosotros y funcionara.
Vemos que ahora si funciona perfectamente cuando veamos el capitulo mas profundo de scripts cgi se aclarara mejor ese punto. Bueno hasta la parte siguiente Ricardo Narvaja
SERVER-SIDE CGI SCRIPTS Como dijimos en las partes anteriores, íbamos a profundizar en el estudio de CGI scripting, el cual implementa mucha de la interacción que vemos en la WEB y es una de las formas standard y ampliamente usada para programar sistemas basados en web y interactuar con ellos. Hay otras formas de agregar contenido interactivo a sitios web desde Python, por ejemplo soluciones client-side (del lado del cliente) como Jython applets y Active scripting y tecnologías server-side (del lado del server) que se basan en el modelo básico CGI por ejemplo paginas Python server, Zope, Webware, CherryPy y Django frameworks, estas alternativas las estudiaremos mas adelante. Por lo demás los scripts CGI server-side son usados para programar mucha de la actividad en la web, ellos son la primera y mas primitiva aproximación a implementar sitios web, aunque no ofrecen las herramientas que vienen incluidas en los mas importantes programas de construcción de webs, de cualquier manera CGI scripts, son la forma mas sencilla de implementar scripts en el server y una de las mejores formas de iniciarse en la programación de sitios web, especialmente para sitios sencillos que no requieren herramientas a nivel corporativo, CGI es suficiente y puede ser mejorado con librerías adicionales a medida que se necesita. UN SCRIPT CGI DETRAS DE LA CORTINA Formalmente hablando CGI scripts son programas que corren en una maquina server y adhieren al modelo CGI (Common Gateway Interface) de comunicaciones entre browser y server. CGI es por lo tanto un protocolo que los web servers usan para transferir datos de entrada y resultados, resultando en una interacción, la cual es un proceso para el que navega muy simple y familiar, detallemoslo: Submisión Cuando uno visita un sitio web para por ejemplo comprar un producto, registrarse en una pagina web, o en un foro, o simplemente enviar cierta información, uno generalmente llena en un formulario en el browser tipeando los datos solicitados, y luego envía los mismos, apretando un botón para enviar la información y queda esperando por una respuesta. Respuesta Si todo esta correcto con la conexión a Internet y se puede contactar la computadora que aloja el web server, la misma recibirá la información que enviamos, y te enviará una respuesta la cual generalmente será que te mostrará una nueva pagina o modificará la anterior, puede ser el agregado de un mensaje como 'Gracias por su compra' o un nuevo formulario para complementar la información anterior, o si no le pareció correcta la info que enviaste, te mostrará nuevamente el formulario original. Este sencillo modelo realiza la mayor parte del trabajo en la web, pero internamente es un poco mas complejo, en efecto es una arquitectura basada en una comunicación usando sockets entre cliente y server, donde la computadora que tiene el browser es el cliente y la computadora que contactas en la web es el server, examinemos este escenario nuevamente con los detalles que el usuario no ve.
Submisión Cuando uno llena el formulario en un web browser y aprieta el botón para enviar la información, atrás de la escena el browser envía la info a través de Internet a la maquina server que debe recibirla. La maquina server es una computadora remota que por supuesto existe en el ciberespacio y en la realidad, la forma de elegir cual computadora remota uno accederá es la dirección URL que uno escribe en la barra de navegación del browser, pero en el caso de un formulario que uno llena y aprieta un botón, no necesita tipear explicitamente la dirección en la barra del navegador, pues la pagina web esta preparada para cuando uno aprete el mismo, en el código HTML de la misma, haya un tag que maneje la entrada de esos datos y tenga un hyperlink que los envíe a la computadora correcta. Una vez que el server esta especificado, el browser envía la información como bytes en un socket, usando las técnicas que vimos en las partes anteriores, en la maquina server, un programa llamado HTTP server corre constantemente, esperando por conexiones de entrada y datos que son enviados por browsers y otros clientes, usualmente en el port 80. Procesamiento Cuando la información llega a la maquina server, el programa HTTP decide como manejar el request, si el mismo apunta a una simple pagina web, por ejemplo termina en .html, el web server buscara y si encuentra el archivo correcto lo abrirá en la maquina server y lo enviará al browser usando el socket. En el cliente el browser lee el HTML y muestra la pagina que podremos ver. Pero si en vez de apuntar a un archivo .html el browser llama a un programa ejecutable, por ejemplo un request que termina en .cgi o .py, el HTTP server arranca el programa en la maquina server para procesar el pedido y redirige los datos que le enviamos al programa que acaba de arrancar, usando por ejemplo stdin, input stream, environment variables y argumentos de linea de comandos entre algunas formas que hemos visto para poder ingresarlos. Por lo tanto el programa correrá en la maquina server, no en la del cliente, en este caso el manejo del request es responsabilidad del script CGI, por ejemplo puede guardar la info enviada en una base de datos, cobrar a una tarjeta de crédito si es una transacción etc. Respuesta Finalmente el script CGI imprime código HTML, para generar una nueva pagina de respuesta, y el mismo código es enviado por dicho script CGI a través de Internet de regreso al browser del cliente, para mostrar en el mismo una nueva pagina. El código HTML impreso por el script, puede ser enviado redireccionando la salida del mismo usando stdout, como hemos visto y como los datos enviados están formateados en código HTML, funciona como si hubiéramos leído un archivo .html directamente. En otras palabras los scripts CGI son como manejadores callbacks para requests generados por web browsers que requieren un programa que corra dinamicamente. Ellos corren en el server en respuestas a acciones en el browser, y envían la respuesta a través del socket. ESCRIBIENDO CGI SCRIPTS EN PYTHON A pesar de que suena complicado Python nos facilita la tarea como siempre ocultando la parte compleja y facilitando el trabajo al usuario como veremos. Como mencionamos antes en el contexto de los scripts CGI, stdin y stdout están redirigidos automáticamente a los sockets conectados al browser, de forma que si un script CGI imprime algo,
esto se enviara al socket, no hay que preocuparse por las redirecciones de la entrada y salida. Los datos de entrada enviados por el browser al server, directamente están redirigidos como un stream de bytes en la entrada stdin, lo mismo que las environment variables. Cuando el script CGI imprime código en formato HTML, y al estar el stdout redirigido pues todo lo impreso, se envía al socket de vuelta al browser. Las partes mas complejas de este esquema incluso parsear la información para enviarla desde el browser y formatear la respuesta para enviarla de nuevo desde el server al browser, es facilitada por librerías de Python, las cuales automatizan dichas tareas. Entrada Con el modulo de Python llamado cgi, los datos tipeados en un web browser se muestran como objetos similares a diccionarios en scripts CGI. Python parsea los datos y nos da los pares key:valor asociado por cada entrada tipeada, dentro de un diccionario, Salida El modulo cgi también tiene herramientas para automáticamente dar formato HTML a strings, para respetar el formato, reemplazando por ejemplo los símbolos < y > y & con códigos HTML de escape, también el modulo urllib provee otras tools para formatear texto insertado en URLs. De cualquier forma los scripts CGI están ligados íntimamente al código HTML el cual no es propósito de este curso enseñar en profundidad, por supuesto es necesario conocer lo básico en HTML, algunas cosas se enseñaran, pero para profundizar será necesario que el lector se sumerja en el aprendizaje de código HTML fuera de este curso. CORRIENDO EJEMPLOS Para correr los programas basados en CGI necesitamos tres partes de software: 1)Un cliente que puede ser un browser o un script para enviar los requests. 2)El web server que recibirá el request. 3)El script CGI que es ejecutado por el web server y procesara el request. Nosotros crearemos los scripts CGI, podemos usar browsers como FIREFOX o INTERNET EXPLORER (también scripts que usen urllib), la única pieza faltante es el web server intermedio. OPCIONES EN WEB SERVER Hay muchísimas posibilidades para correr web servers, por ejemplo el sistema open source Apache, provee un completo web server y su extensión mod_python corre scripts Python fácilmente, pero para mantener las cosas sencillas correremos un script de Python que será un simple web server, y en la misma maquina correremos el cliente usando localhost, ya sabemos que si fuera una maquina remota solo poniendo su IP o host y teniendo acceso a ella seria lo mismo. El web server que utilizaremos sera el script que veremos a continuación, que es casi similar al que habíamos visto anteriormente en la parte 10 de la etapa 2, pero un poco mejorado, ya que permite pasar como argumento el puerto y el directorio de trabajo.
Ademas en este caso agregaremos el subdirectorio cgi-bin al path de módulos, de esta forma se pueden importar módulos de Python de allí mismo sin problemas.
Vemos que salvo el hack intermedio que no esta explicado en el libro para que es, el resto es muy sencillo, agrega cgi-bin a los paths para buscar módulos en Python aquí.
Y luego se realiza una instancia en la clase HTTPServer, pasandole como argumento el port y el IP, en este modulo si ese usa una string vacía lo interpreta como localhost.
Por lo tanto el primer argumento será srvraddr que es una tupla con el IP o host que en este caso es la string vacía y el puerto que es el 80 por default el cual puede ser cambiado pasandole como argumento otro port.
El segundo argumento sera la clase que manejara los request, como manejará scripts CGI, usaremos CGIHTTPRequestHandler que es la clase que realiza ese trabajo completamente para cualquier request.
Luego a la instancia se le aplica el método serve_forever el cual manejará los request de los clientes uno a la vez.
Para correr el web server, vamos a la carpeta donde esta el script y y tipeamos python webserver.py.
Si desde un browser accedemos mediante localhost veremos los archivos que se encuentran en su directorio raíz, nada nuevo.
Si en el directorio raíz donde corre el script existiría un archivo de extensión html, este se podría mostrar si lo ejecutamos por su nombre, inclusive si fuera index.html se mostraría por default ese archivo, también correrá por default scripts CGI que se encuentren en el subdirectorio cgi-bin, que serán los que iremos estudiando.
Por supuesto si apunto al directorio raíz del webserver a otro lugar pasandole el argumento correspondiente al arrancar el webserver, los archivos mostrados serán otros, pues cambiará el directorio raíz del mismo.
Allí ven como cambio el directorio raíz a la carpeta curso y si lo llamo desde el browser, mostrara su contenido y podre navegar por el y sus subcarpetas.
Volvamos al caso por default sin argumentos y el webserver teniendo la carpeta raíz por default. http://examples.oreilly.com/python2/ En este link están los scripts y archivos que usa el libro que estamos usando de base, el de MARK LUTZ, en su versión original, si no tienen ganas de tipear mucho pueden bajarlos de allí, y la mayoría salvo pequeñas correcciones esta igual, en este caso usamos de esos archivos el PyInternetDemos.html que adjuntaré y lo colocamos en el directorio raíz del webserver.
Ahora al llamar desde un browser aparecerá este archivo que podemos ejecutar.
Vemos que pide algunos archivos mas para completar su web los agregaremos.
Estaba pidiendo otro gif /PyErrata/PythonPoweredSmall.gif asi que copié la carpeta entera PyErrata que esta entre los ejemplos y de esta forma halla el gif necesario y ya no da error. Por supuesto para que la pagina web funcione completa vemos que los links llevan a las otras carpetas que están en el archivo de ejemplos así que las agrego y puedo navegar por ellas perfectamente usando los links.
Ahora si vemos que no solo funciona toda la web con sus links sino que ahora también tiene un index.HTML que si llamamos a localhost solamente se mostrara por default.
Ahora que ya tenemos armado nuestro webserver y una pagina web de ejemplo que estudiaremos, empezaremos paso a paso desde el mas sencillo CGI que contiene la misma ya que tiene incluidos todos los ejemplos que estudiaremos de HTML y CGI en la parte 17 en adelante. Hasta la parte 17 Ricardo Narvaja
COMENZANDO CON LOS EJEMPLOS DE CGI Antes que nada varios lectores del curso me comentaron que en el ejemplo de la parte anterior yo dije que la carpeta cgi-bin era para ejecutar script pero cuando entran a la misma le da un extraño error 403, mientras que fuera de ella los scripts no se ejecutan, si no que se muestran como archivos de texto, y podemos ver su contenido, pero no ejecutarlos. Si queremos entrar a la carpeta cgi-bin nos sale este error.
Error response Error code 403. Message: CGI script is not a plain file ('/cgi-bin/'). Error code explanation: 403 = Request forbidden -- authorization will not help. O sea el tema es que la carpeta cgi-bin es un lugar peligroso, contiene scripts ejecutables, y si se puede listar su contenido se pueden ejecutar cosas que por ahí el dueño del server no quiere, por eso allí no tenemos autorización para listar o navegar dentro de la carpeta, ahora si armamos el link completo con el nombre del script a ejecutar, vemos que si funciona, o sea que dicha carpeta tiene permiso de ejecución pero no de listado, así que tenemos que saber lo que queremos ejecutar, lógicamente nos debe dar el dueño del server un link correcto al script que quiere que podamos ejecutar, es una medida de protección, pero bueno ya sabemos como ejecutar scripts allí, con el link completo.
NUESTRA PRIMERA PAGINA WEB Por supuesto como con el tema de las GUI, en este caso empezaremos por lo mas sencillo que existe y paso a paso iremos avanzando en ejemplos que irán creciendo en dificultad. Como mencionamos los scripts CGI están íntimamente ligados al código HTML, así que comencemos viendo una simple pagina HTML, el ejemplo muestra una simple pagina web que contiene código HTML, llamada tutor0.html. Escribimos el código en el bloc de notas y lo guardamos con extensión html.
Y lo guardamos con el nombre
y allí esta
Si abrimos el archivo en un web browser .
Por supuesto si colocamos el archivo en el directorio raiz del webserver dejándolo vacío solo con la carpeta cgi-bin también vacía y apuntamos a este archivo.
Arranco el webserver.py y navego hasta el archivo desde el browser.
Ahora voy al archivo que cree y veo mi primera pagina web
Realmente es una pagina web, pues aquí nosotros estamos usando localhost, pero si tenemos un IP o un host, el que navegue al mismo vera lo mismo que nosotros aquí, o sea que mi pagina estará en la web para todo el que conozca mi host o IP.
Allí veo como quien tiene mi IP puede acceder a mi web, en este caso, no sera una web para todos pues mi IP es solo de una red interna y solo se puede acceder a dicha IP en esa RED no tiene salida al exterior, pero usando la dirección IP publica, tendremos una web para todo el que quiera entrar a ella, aunque es muy simple por ahora, pero vamos paso a paso, tratemos de entender esta primero. LO BASICO DE HTML Prometimos no meternos mucho con el código HTML pero explicaremos lo mas básico para poder entender los ejemplos. Brevemente HTML es un lenguaje que utiliza tags para determinar la forma en que un texto será mostrado, dichos tags estan encerrados en los símbolos < > , si miramos nuestro archivo vemos
varios tags, allí vemos resaltado un TAG HTML encerrado en los dos símbolos HTML 101 A First HTML page Hello, HTML World! Algunos tags trabajan sueltos como por ejemplo que significa regla horizontal, otros tags trabajan de a pares abriendo y cerrando, el tag que cierra tiene una barra extra. HTML 101 A First HTML page Hello, HTML World! Vemos que el tag HTML identifica el inicio de un codigo en este lenguaje y trabaja de a pares y el de cerrar tiene una barra mas para indicar eso, que allí se cierra o termina el código HTML. El tag BODY marca que dentro de dichos tag se encierra todo el contenido del documento que se mostrará. Dentro de estos tags se encuentran todos los elementos visuales (para navegadores visuales) del documento. HTML 101 A First HTML page Hello, HTML World! Por ejemplo dentro de los tag BODY en nuestro ejemplo hay un texto grande o nivel uno el cual se encierra entre tags H1. texto con eso dicho texto sale en la forma que vemos en nuestra web HTML 101 A First HTML page Hello, HTML World! Debemos encerrarlo entre tags H1, como siempre el de cerrar debe tener una barra mas, vemos también en nuestro ejemplo que dentro de un tag TITLE, se encierra el titulo de la pagina, el cual esta fuera del encierro de los tags BODY, pues no es un elemento a mostrar en el cuerpo de la pagina.
HTML 101 A First HTML page Hello, HTML World! Y el texto común esta entre tags P HTML 101 A First HTML page Hello, HTML World! Hay también algunos que no utilizamos aun como text el texto se mostrara como un link en el cual al hacer click navegaremos en nuestro browser a la dirección address.
Allí vemos que le agregue un link a mi web al hacer un click en el mismo.
Como vemos HTML es un código descriptivo de paginas el browser tomará los tags y mostrará la pagina según la convención que se le atribuye a cada uno, pero no es un código en el que puedan tomarse decisiones con if, else, loops, para ello deberemos ejecutar scripts por ejemplo de Python, vemos que en nuestro ejemplo anterior no hay nada de Python y por eso la pagina es absolutamente una lacra jeje. La falta de poder programar ese tipo de código en HTML es a la vez una fuerza y una debilidad del lenguaje, el cual sirve bien para describir paginas y poco mas, para GUIs complejas debemos introducir CGI scripts (no es la única forma obviamente de agregar scripts en una pagina web existen otras tecnologías), para lograr agregar programación dinámica al código HTML que sino sería absolutamente estático. DIRECCIONES DE INTERNET
Una vez que uno escribe un archivo HTML se necesita hacer que los web browsers puedan llegar a el, si uno esta usando un web server en Python como el que vimos hasta ahora, el mismo permitirá a los browser acceder a las paginas HTML, como hemos visto. En mi maquina por ejemplo (en la de ustedes variará) el path donde esta corriendo el webserver es C:\Documents and Settings\ricnar\Escritorio\webdir allí dentro tengo el webserver.py y la carpeta cgi-bin y el archivo tutor0.html que creamos. Ahora desde un browser la dirección URL para acceder a esa carpeta, si uso el directorio raiz que viene por default en el webserver será: http://localhost/tutor0.html si colocara mi IP que por ejemplo fuera 198.67.55.44 o tuviera un host por ejemplo ricardonarvaja.com seria http://198.67.55.44/tutor0.html o http://ricardonarvaja.com/tutor0.html Si el archivo tutor0.html lo hubiera colocado en una subcarpeta dentro de webdir, por ejemplo llamada HTML entonces el link para acceder al archivo seria en esos casos. http://localhost/HTML/tutor0.html o http://198.67.55.44/HTML/tutor0.html o http://ricardonarvaja.com/HTML/tutor0.html Realmente la dirección URL que armaremos es una concatenación de múltiples partes: Nombre del Protocolo: http El protocolo con el que se inicia la url le dice al browser que se comunique con el HTTP server usando el protocolo HTTP, las urls usadas en browsers pueden tener otro nombre de protocolo y comenzar por ejemplo con ftp:// para referenciar a un archivo manejado por un protocolo y server FTP, puede ser también file:// para referirse a un archivo en la maquina local, telnet para iniciar una sesión cliente de telnet y así hay unos cuantos protocolos mas con que se puede iniciar una url. Nombre del Server y puerto:
localhost ricardonarvaja.com 198.67.55.44 (el IP que corresponda) Luego del protocolo viene el nombre de la maquina donde esta el server al cual queremos acceder, como vimos usualmente localhost (o su equivalente IP 127.0.0.1) significa que el server esta en la misma maquina que el cliente, si es una maquina remota llevará el IP de la misma o el HOST que le corresponda según el caso y que nos permita acceder a ella. Opcionalmente esta parte de la url puede llevar el puerto en el cual el server esta esperando conexiones ( por ejemplo ricardonarvaja.com :8000 o 127.0.0.1:80) normalmente se utiliza el puerto 80 y no se especifica, si fuera un puerto diferente al 80, habrá que especificarlo explicitamente usando el signo dos puntos y a continuación el numero de port. Path al Archivo: /HTML/tutor0.html Esta parte de la url esta compuesta por el path al archivo que queremos acceder, el HTTP server automáticamente traslada cuando arranca, los paths de mi maquina y los convierte en paths relativos al directorio raíz con el que arranque el mismo, por lo tanto el archivo que en mi maquina esta en: C:\Documents and Settings\ricnar\Escritorio\webdir\HTML\tutor0.html Como en mi maquina, webdir es el directorio raíz del webserver por lo tanto convertirá ese path a direcciones relativas a esa raíz y quedará \HTML\tutor0.html Lo cual agregaremos a la url que armamos hasta aquí. Parametros del Query: Opcionalmente las urls pueden ser seguidas por una entrada adicional para programas CGI, estas comienzan con un signo de interrogación ? , y están separadas por caracteres & . Por ejemplo una string de la forma ?name=bob&job=hacker en el final de una url pasa los parámetros name y job, al script y dichas variables name y job tienen asignados valores, que serán en este caso bob y hacker respectivamente, mas adelante veremos mas en profundidad esta parte de la url, aunque diremos que los parámetros también pueden estar separados por punto y coma ?name=bob;job=hacker es menos común pero es valido. Estos valores son llamados parámetros del query y son tratados como inputs por los scripts. De esta forma analicemos una url completa por ejemplo:
La parte amarilla es el protocolo que es en este caso http, a continuación tenemos la parte celeste donde va el host y port en este caso sería localhost, que significa que el server y cliente están en la misma maquina y el puerto :80 que como es el valor por default no sería necesario, significa que el
server escucha conexiones en ese puerto. A continuación en la parte rosada tenemos el path al archivo que me da la locación del mismo, tomando como referencia o base el directorio raíz del server, de esta forma en la raíz del mismo habrá una carpeta cgi-bin dentro de la cual habrá un archivo languages.py que es el que queremos acceder. Y la string de query ?language=All provee un parámetro de entrada para ejecutar el script languages.py. Existen mas variantes en las urls algunas de las cuales iremos usando en las partes siguientes, aunque el mundo de las urls es muy rico y no se podrá abarcar todas las alternativas existentes seguramente lo básico podremos verlo.
USANDO URL MINIMAS Cuando un browser accede a una dirección de Internet, y esta ejecutando un archivo HTML, recuerda el protocolo y nombre del server, port y el path de forma que en un archivo HTML si estos valores se omiten, usa los valores faltantes correspondientes a la ultima dirección de pagina visitada, por lo tanto en un archivo HTML el codigo. Será tratado exactamente igual a: Pues al ver en el primero, que la la url tiene omitido el protocolo y host y el path, usará por ejemplo http://www.server.com/dirpath, que es el componente faltante correspondiente a la ultima pagina que visitó o sea la locación de la pagina actual y la usará para armar la url. PERMISO DE ARCHIVOS HTML Un punto mas antes de comenzar con nuestro primer script es que para que un web server, sobretodo si no esta corriendo en una misma maquina que el cliente, debemos asegurarnos que los directorios y los archivos a los que apuntamos tengan permiso de lectura, sobre todo en sistemas basados en Unix. Usando el comando chmod podremos cambiar en dichos sistemas los permisos de los archivos y carpetas. Por ejemplo chmod 755 filename, hará que el archivo tenga permiso de lectura y sea ejecutable por cualquier usuario, y solo pueda modificarlo el dueño que tenga la clave de administrador o root del sistema. NUESTRO PRIMER SCRIPT CGI El archivo HTML que creamos como primer ejemplo el llamado tutor0.html, es solo un archivo HTML no un script, para ilustrar la naturaleza de los scripts CGI lo mas sencillo será empezar recodeando el mismo ejemplo pero como un script de Python CGI se llamará tutor0.py. Abrimos el WING y creamos un nuevo script y lo guardamos en la carpeta cgi-bin ya que alli el webserver los ejecutará.
Vemos que nuestro script no tiene mucho de CGI, son cuatro prints que si lo ejecutamos desde la linea de comandos imprime las strings que están a continuación.
El tema es que al ser ejecutado por el web server, automáticamente la salida standard es redireccionada al socket leído por el browser en la maquina cliente, en ese contexto todo lo que imprimimos es enviado por Internet al browser del cliente, por supuesto lo que imprimimos debe estar en un formato adecuado a lo que espera el browser sino dará error. En particular cuando la salida del script llega al browser, la primer linea impresa es interpretada como header, describiendo el texto que seguirá de que tipo es, el header puede tener mas de una linea, pero por convención debe haber una linea en blanco que separe el header del resto. En este script el header le dice al browser que interprete el resto de la transmisión como código HTML (text/html)
Vemos en el script que se le agrega un \n adicional al final del print para que deje una linea vacía mas antes de continuar imprimiendo la próxima, como dice la convención.
El resto del programa imprime linea a linea el mismo código de nuestro primer HTML, pues es código HTML standard que el browser esta esperando, cada linea imprimirá y enviara el código a mostrar. Realmente vemos que el código enviado solo difiere del de la pagina web original en que le faltan los tags y los cuales pueden enviarse pero como en este caso, los browsers muestran la pagina igual, aunque no se pongan, pues el resultado es el mismo.
Recordemos que no podemos ir navegando a la carpeta cgi-bin sino que tenemos que pasarle el link completo al archivo a ejecutar, si no no podremos hacerlo y obtendremos error. Vemos que si al script lo completamos con los tags HTML y BODY
Y lo vemos el resultado es exactamente el mismo.
Por lo tanto para no complicar los quitamos. INSTALANDO CGI SCRIPTS Si estamos corriendo el webserver que usamos en los ejemplos, la instalación de un script CGI no requiere mas que lo que hicimos, colocar el script en la carpeta cgi-bin y desde un browser apuntar a el con un link completo, no ir navegando, ahora como hacemos para colocar un script CGI en otro server que no sea este? Como los archivos HTML, los scripts CGI son solo simple archivos de texto que uno puede crear en una maquina local y subirlos al server mediante FTP, o escribir directamente en un editor de texto corriendo directamente en la maquina server, también puede usarse un servidor telnet para hacerlo remoto. De cualquiera de estas formas como los scripts CGI corren como programas ejecutables hay algunos puntos en su instalación que difieren de los archivos HTML, en particular ellos deben ser guardados y nombrados específicamente y deben ser configurados para ser ejecutados, o sea deben tener permiso para hacerlo según quien queremos que los ejecute. Veamos algunos puntos en mas detalle: Convención de directorios y nombres: Primero que nada los scripts CGI deben ser colocados en un directorio que el web server reconozca
como directorio de programas, y debe darsele un nombre que el server reconozca como CGI script. En nuestro webserver local los scripts deben colocarse para ser ejecutados en una carpeta especial llamada cgi-bin y deben tener extensión .py, en otros servers por ejemplo los scripts CGI deben ser ubicados en otra carpeta por ejemplo public_html y tener una extensión .cgi, algunos servers permiten diferentes extensiones, así que debe conocerse el web server para saber que extensión es la permitida por cada uno. Convenciones de ejecución: Dado que estos scripts deben ser ejecutados, deben tener el permiso necesario para que puedan serlo, en la mayoría de los servers como usan plataformas basadas en Unix un comando de shell como chmod 0755 filename como ya vimos será suficiente. Bajo ciertos servers los scripts CGI también necesitan una linea especial al inicio, que comienza con #! para identificar el interprete de Python que correrá el código, el texto a continuación de #! simplemente da el path al ejecutable de Python en la maquina server.
En maquinas que no usen sistemas basados en Unix dicha linea es ignorada y tomada como un comentario, pues por medio de las environment variables se configura la ubicación del ejecutable de Python y no se necesita. Configuración el module search path: Algunos HTTP servers necesitan que los scripts tengan agregado una especificación de donde se buscarán módulos para importar de Python, aunque esto es poco frecuente, de ser necesario usando como ya vimos sys.path.append (dirname) Agregaremos un nuevo directorio, donde el script buscara módulos para importar, es muy raro tener que hacer esto, pero bueno lo comentamos. Convenciones de fin de linea: Debemos tener cuidado pues si creamos un archivo en un editor de texto en Windows y lo ponemos en un server basado en Unix, podemos tener problemas por las diferentes convenciones de los caracteres de fin de linea, sabemos que en Unix son \n y en DOS \r\n , así que cuando se ejecute el script en un sistema diferente encontrara caracteres extraños de fin de linea y no funcionará, por ello deberemos editarlo convenientemente para que corra según el caso sin problemas.
Streams de salida no bufereadas: Bajo ciertos servers, cuando imprimimos en vez de enviarse directamente al socket, puede bufferearse la salida. Si uno tiene un CGI script largo para hacer que el usuario no tenga que esperar que se llene el buffer para poder ver algo, puede forzar manualmente el vaciado del buffer con sys.stdout.flush(), o correr los scripts de Python en modo unbuffered, eso se logra mediante el flag -u que se puede colocar en la primera linea para sistemas Unix #!/usr/bin/python -u O también para sistemas Windows seteando una variable de entorno en la maquina server llamada PYTHONUNBUFFERED dicha variable debe estar seteada a un valor diferente de cero. Seguiremos con mas ejemplos en la parte siguiente Hasta la parte 18 Ricardo Narvaja
18- MAS EJEMPLOS DE CGI Veamos una simple pagina web en código HTML a la que le agregaremos imágenes, el script se llamará tutor1.py .
Por supuesto lo guardamos en la carpeta cgi-bin de nuestro server. Vemos que en este caso en vez de anidar varios print uno abajo del otro para armar la string que enviamos usando \n, directamente armamos la string en una variable en la forma que se necesita para la convención, la encerramos en triple comillas para no tener que poner caracteres especiales de formato, lo que este dentro de las triples comillas, será la string resultante. O sea por ejemplo:
imprime dos pepe dejando lineas vacías en medio y si hago una variable en este caso llamada hola que contenga una string encerrada entre triple comillas no necesito usar \n, pues imprimirá en la forma que lo ubique dentro de las triple comillas, y el resultado es similar aunque mas cómodo de trabajar ya que podemos dejar la linea vacía que debe ir obligatoriamente debajo de Content type, sin usar retornos de linea.
Este script usa ademas dos tag que corresponden a una linea horizontal y de un tag que sirve para incluir una imagen en nuestra pagina.
Por supuesto debemos ubicar el gif de la imagen en la carpeta raíz y arreglar el path del script para que la halle.
Y ahora para que la halle allí debemos arreglar el script.
Sabemos que los paths son relativos al directorio raíz, así que si colocamos el archivo en el mismo, debemos poner la barra delante para que lo busque allí, si estuviera en una subcarpeta xxxx y dentro de ella otra yyyy, lógicamente seria. /xxxxx/yyyy/ppsmall.gif Si no le pongo barra delante y pongo el nombre del archivo solo, el modulo de Python lo buscará en la misma carpeta donde esta corriendo el script o sea dentro de cgi-bin, así que arranco el web server , recuerden que deben poner el path entero en el browser al script si no les dará error.
Allí vemos las dos lineas creadas con el tag HR y la imagen Nuestro próximo ejemplo avanzará un poco mas y se llamará tutor2.py, lo guardamos en la carpeta cgi-bin el código es este.
Vemos que este script ademas de imprimir código html como los anteriores, ejecuta código Python (print lo es también), pero aquí ya no solo para enviar a la salida redireccionada al socket sino para crear código en forma dinámica. Vemos que la pagina tiene dos prints que envían al socket código que esta entre triple comillas y en el medio se genera una tabla dinamicamente, veamos como es un tag de tablas primero para ver que es lo que se debe imprimir allí.
Eso es lo que debería imprimirse en un tag de tabla, primero el tag y luego encerrado entre tags que es el inicio de cada linea, dentro va lo que mostrará cada casilla entre tags al cerrar el tag se cierra la linea y se salta a la siguiente o sea todas las casillas de la primera linea están enceradas entre tags , al finalizar toda la tabla se cierra con un tag de cerrar
O sea esto debería generar una tabla de 4 x 5 asi.
Si corremos el script en el web server.
Vemos que funciona, podemos chequear en el mismo browser, el código HTML de la pagina, haciendo click derecho existe una opción para ver el código de la misma.
Vemos el código que genero nuestro script y como se unió la primera parte que se envió entre comillas, la tabla que se creo con un par de for anidados y imprimiendo los tags y los contenidos y la ultima parte con el print del fin de la pagina. Aquí vemos la relación la primera parte es esta, nuestro script envía lo que esta entre comillas directamente al socket y va al browser.
La segunda parte del script imprime dentro de un for que se repite 5 veces una por cada linea, primero imprime el tag de inicio de linea y entra en el segundo for donde primero imprimirá. print "%d.%d" % (i, j) Allí vemos que mediante format string incluye el numero de linea y columna encerrados entre tags , así se repite 4 veces y sale del for a la siguiente linea, así hará todo ese código que vemos es el que le llega al browser el cual lo interpreta como una tabla dado que es el código HTML para ello.
Una vez que termina la tabla entre triples comillas de nuevo manda al socket el tag final de la tabla una linea horizontal y se termina.
Bueno en la parte 19 seguiremos poco a poco complicando el tema de los scripts CGI con mas ejemplos. Hasta la parte 19 Ricardo Narvaja
19- MAS CGI- AGREGANDO INTERACCION CON EL USUARIO Los scripts CGI pueden generar código HTML al vuelo, como en los ejemplos anteriores, pero también son comúnmente usados para implementar interacción con un usuario que tipea en el web browser. Como describimos en las partes anteriores, las interacciones web encierran el trabajo de dos paginas web, cuando uno llena sus datos en la pagina web original y aprieta submit y recibe una nueva pagina web en respuesta, entre medio el CGI script procesa el input. PAGINA DE SUBMISION Esta descripción suena sencilla, pero el proceso de colectar entradas de un usuario en una pagina web, requiere el conocimiento de un tag especial de HTML , es el tag . Miremos y expliquemos una simple implementación para ver como funciona , primero definiremos una pagina para que el usuario llene como vemos en el siguiente ejemplo, que es código HTML, así que en un editor de texto tipeamos su código y lo guardamos como tutor3.html.
Allí esta ubicada en la raíz de mi webserver su código es:
Vemos que es un simple archivo en HTML y no un script CGI, aunque su contenido, podría ser impreso en un CGI script como vimos en los ejemplos de la parte anterior. Si arranco el webserver y accedo a la pagina.
Veamos un poco mas en detalle el código del form, el resto es conocido para nosotros. Action handler La opción action que tiene form nos da la posibilidad de configurar la URL del CGI script que será invocado cuando enviemos la data.
Allí vemos que el link nos envía a un script CGI llamado tutor3.py que lógicamente esta en la carpeta cgi-bin, pues fuera de ella no se podría ejecutar, haciendo una equivalencia media grosera, action es similar a la opción command en Tkinter que define una función callback que será llamada cuando se apriete un botón, o se ejecute una acción en una GUI, aquí al apretar SUBMIT se ejecutará dicho script. Campos de input Los controles de input están especificados como tags anidados, en este ejemplo los tags input tienen dos opciones, la opción type, acepta valores como text para un campo de texto y submit para el botón de enviar.
La opción name se utiliza para identificar el valor entrado, ya veremos que en el server una vez que la data llegue al script usara la string user como key para obtener los datos que se tipearon en la pagina web.
Métodos de envío get y post El tag form también incluye una opción method para especificar el estilo a ser usado, para enviar la data sobre un socket al server, aquí, nosotros usamos el estilo post, el cual contacta el server y entonces distribuye en un stream de datos de entrada en una transmisión separada en el socket. El otro método alternativo get, distribuye la información de entrada al server en la misma transmisión apendeando datos que debemos enviar al final de la URL que invoca el script, usualmente después de un carácter ?, ya veremos estos estilos en detalle mas adelante. De esta forma con get las entradas son tipicamente argumentos o environment variables en la linea de comandos usados para iniciar el script, con post ellos pueden ser leídos de la entrada standard y decodeados. Como get permite que un usuario agregue la url a un marcador o bookmark, puede invocarse nuevamente dicha url con los mismos parámetros ya guardados, en cambio post se usa generalmente para enviar datos que serán enviados una vez y cambiarán SCRIPT DE RESPUESTA Vimos que cuando tipeemos y apretemos submit se llamará al script tutor3.py así que debemos escribirlo y ubicarlo en la carpeta cgi-bin para que sea ejecutado allí.
Vemos que necesita importar el modulo standard cgi para poder recuperar la data que se tipeo en la web la cual se guarda en la variable form, esto es automático en Python, una llamada a la clase FiledStorage, automáticamente hace todo el trabajo de extraer la data del stream de entrada o de los argumentos ya sea que los datos se enviaron con el método post o con el método get agregados a la URL, o sea que el tipo de estilo con el que se enviaron los datos no cambia la forma de recibirlos de un script CGI hecho en Python. La variable form que recibe los datos será en objeto del tipo diccionario, donde las keys que se usaron en la pagina web para enviar los datos serán, las keys del mismo y el valor asociado, los datos que se tipearon asociados a dicha key, en nuestro ejemplo el diccionario form tiene una key llamada user, y para hallar su valor asociado, como en cualquier diccionario. form['user'] Por supuesto como todo objeto diccionario soporta los métodos aplicables a los mismos como el
método has_key para chequear si el mismo tiene o no una key con tal nombre. El script al inicio luego de importar cgi y obtener el diccionario de datos, imprime la primera linea de la nueva pagina que estamos creando que siempre es el Content Type, luego debe venir una linea vacía.
Vemos que deliberadamente se deja una linea vacía en el código siguiente:
La variable html arma la pagina web la cual no imprimimos todavía, dejando la primera linea vacía, y la misma tendrá un titulo dentro de tags TITLE y un saludo y vemos como tendrá un format string para colocar la respuesta.
Allí vemos que se ingresará una string por medio de forma string, la string que se mostrara se decide en el código siguiente:
Vemos que chequea con has_key si el diccionario form tiene la key user si no la tiene en un print que envía la pagina de vuelta al browser, hace el format string en un solo paso, le agrega la string WHO ARE YOU?
En cambio si encuentra la key hace el format string pero arma la string que tendrá al inicio la palabra HELLO y mediante un segundo format string le adosa el dato que tipeo el usuario de su nombre que obtiene del diccionario form.
Veamoslo en acción
Al apretar el botón se envían los datos
Si no hubiéramos tipeado nada y apretábamos el botón submit.
Allí vemos en la barra de URLs del browser la dirección a la cual la pagina HTML nos derivo que por supuesto ejecuta el script, si nosotros tipeamos esa dirección directamente en el browser obtendremos el mismo resultado de la imagen anterior, pues el script se ejecuta sin datos que ingresen por medio del form, entonces nos toma por desconocidos. Acabamos de ver que form tenia dos métodos para enviar los datos get y post, dijimos que get los enviaría adosados a la URL y que el script de Python los captaría igual, por lo tanto podemos nosotros enviarle en el link los datos, que enviaría get directamente si lo hacemos en ese formato, el script debería capturarlo como datos de entrada y mostrar el resultado correspondiente veamos.
http://localhost/cgi-bin/tutor3.py?user=Brian
Funciona , de esta forma podemos pasarle parámetros al script directamente, la única contra es que debemos conocer el nombre del parámetro que esta esperando, si pusiera http://localhost/cgi-bin/tutor3.py?age=Brian
Al no tener el script una variable age, funcionará, pero sin datos en la variable user que es la que usa el script. Por supuesto si le pasamos varios parámetros de entrada y alguno es user lo tomará siempre los parámetros van después de un signo de interrogación y separados por &.
Si en la pagina HTML original cambiamos el método post por get veremos que la URL enviada por la pagina es similar a la que estamos enviando manualmente.
Llamemos a dicha pagina y pongamos datos en el form.
Vemos que no solo el script ha interpretado la entrada de datos enviada con este método sino que ahora vemos la diferencia con post, aquí se ven los datos, los nombres de las variables, y la URL se ve similar a las que armamos manualmente, lo que no ocurría con post. Una vez que entendimos como se pueden enviar argumentos por medio de una URL a un script CGI, el modulo urllib se vuelve mas útil. Recordemos que este modulo nos permite lee la respuesta generada por cualquier URL, cuando la URL corresponde a una simple pagina HTML, nos baja y muestra su contenido, pero cuando la URL apunta a un script CGI, el efecto es que ejecuta el mismo, y obtiene la salida del mismo a través del socket, de la misma forma que lo hace el browser. Veamos unos ejemplos desde la consola interactiva de Python, teniendo el web server corriendo en otra consola.
Vemos que llamamos mediante la URL del script a ejecutar el mismo con el argumento user=Brian, y obtenemos la respuesta en reply si imprimimos la misma.
Vemos que es el código HTML de salida que envió el script al socket y acá lo recibimos, como prueba de que se ejecutó esta el nombre Brian que le pasamos como input, dicho código HTML es el que en el browser se vera.
Si en la consola interactiva no le pasamos datos de entrada la respuesta será.
Recordemos de cuando vimos el modulo urllib que urlopen nos devuelve un objeto archivo, conectado al stream de salida, por lo cual usando read() podemos leer la misma. Una opción mas avanzada del modulo urllib, es armar los parámetros de salida tanto para el método get como para post, para ello usa urlencode, para get que es el mas sencillo y se usa por default .
Así que una vez armados se pueden usar directamente como segundo argumento de urlopen.
Vemos que inclusive si son varios parámetros, los arma en la forma necesaria para que se puedan incluir en la URL, urlopen los acepta como un argumento. Bueno hasta la parte 20 Ricardo Narvaja
PARTE 20:GCI CADA VEZ MAS REALISTA Movámonos a escenarios mas realistas de CGI poco a poco, en la mayor parte de los casos reales, las paginas de entrada están compuestas de varios campos. Cuando hay mas de un campo normalmente toma la forma de una tabla para dar una apariencia bien estructurada. Acá tenemos la pagina HTML de entrada, como siempre con el NOTEPAD podemos tipear el siguiente código, lo llamamos tutor4.html.
Vemos al ejecutar el archivo que en realidad arma una tabla de dos columnas por tres lineas.
Si miramos el código vemos que es similar solo cambio form.
Vemos que el mismo mantiene el método POST de enviar los datos sin que se vean en la URL, y en action vemos la localización del script que ejecutará, estará lógicamente en la carpeta cgi-bin y se llamará tutor4.py. Vemos dentro de los tags table, tags conocidos como TR que inicia cada nueva linea, en este caso hay 3 ya que hay tres lineas, y los tags TD que encierran o inician en este caso el contenido de cada casilla, ya que se pueden poner solo al inicio también, hay un tag nuevo llamado TH que es similar a TD la única diferencia es que al usar en alguna casilla TH en vez de TD, se interpreta que dicha casilla es HEADER o principal y se usa una fuente tipo BOLD para resaltarla de las otras comunes.
Allí vemos la correspondencia entre cada tag y la casilla de la tabla, vemos que los tags TH hacen
una casilla con texto resaltado pues son HEADERS y los tags TD tendrán texto común, ademas la ultima casilla del botón, abarca las dos columnas (colspan=2). Vemos que hay dos casillas de entrada de datos en amarillo, una para tipear el nombre y otra para tipear la edad, y que la única diferencia entre ambas es la key, en la primera name=user y en la segunda name =age, hace que esas dos (user y age) sean las keys, para buscar en el diccionario de entrada en el script de Python y los valores asociados serán lo que tipeo el usuario en cada casilla. Vemos que en este caso, en vez de el botón tener el texto por default, se lo colocamos mediante value=Send, de forma de personalizarlo mas. El que quiere jugar un rato con el código HTML para practicar puede ver las diferencias en estos casos: Si cambio TH por TD
Veo que la casilla es similar solo con texto no resaltado.
Vemos que con colspan=1 el botón abarca solo una casilla.
Vemos como se alinea a la derecha, así podemos jugar con el código en infinidad de formas, ahora volvamos al original.
Veamos el código del script CGI hecho en Python se llamará tutor4.py.
Bueno primero veamos la respuesta poniendo el webserver y ejecutando el HTML y luego analicemos el código parte por parte.
Si apretamos el botón Send sin tipear nada la respuesta es:
Si ponemos los datos.
Vemos que la respuesta ademas de mostrar el nombre, la plataforma win32 en este caso, donde corre el server, muestra la edad que enviamos al cuadrado (squared) Vemos el código del script para hacer esto:
Vemos que como en el caso anterior llamando a cgi.FieldStorage(), obtenemos el diccionario con los datos que nos envía el usuario. Luego de imprimir la primera linea con el Content Type, en la variable html armamos la pagina en una string con triple comillas, igual aun no la enviamos, solo la vamos armando, tiene una linea vacía al inicio como corresponde, y luego el titulo entre tags TITLE, saludos o Greetings entre tags H1, luego un HR que es una linea horizontal, y luego por medio de tres format string se ingresará allí las strings de respuesta. Vemos que armara las tres strings en las variables line1, line2 y line3 y hará los format strings con ellas y al hacer print allí mismo los enviará.
En el caso de line1, vemos que chequea si existe la key user, si no existe le asignará la string 'Who are you?'
Si existe va al else y arma la string 'Hello , “ y a continuación por format string le agrega el valor asociado a la key user, el que tipeo como su nombre en la primera casilla. En el caso de la string que asignará a line2 directamente utilizando sys.platform obtiene que plataforma es la que corre el server, y mediante format string arma la string 'You are talking to a win32 server' por ejemplo si es un sistema Windows. Y en la ultima line3, solo si encuentra la key age, la trata de elevar al cuadrado y arma la string “Your age squared is” y pone el cuadrado de la edad a continuación, como lo realiza dentro de un try-except, si esa operación da algún error, da un mensaje de que no puede computar esa edad, por ejemplo.
En este caso le envíe como edad 3.99, y como le aplique int(), allí me da error y salta al except donde imprime que no puede realizar la operación al no ser un numero entero. Vemos que también al script podemos pasarle los datos en la URL respetando el formato que usa el método get de enviar datos.
También usando el signo mas podemos dejar un espacio.
Lo único que nos quedo pendiente es la linea del script
Normalmente los errores de Python son escritos a sys.stderr de la misma forma que cuando hacemos print la salida se escribe en sys.stdout, al asignar sys.stderr a sys.stdout lo que hacemos es que si hay un error de Python después de esa linea se envíe al browser también, ya probaremos esto bien en los próximos scripts que haremos. Si queremos debuggear los scripts CGI tenemos el problema que estos son arrancados con argumentos que se envían por socket por lo tanto si lo arrancamos sueltos no funcionaran de la misma forma y no tomaran la entrada, de cualquier manera con el WING podemos debuggear programas que fueron arrancados desde otro y no en el mismo WING, refresquemos el truco de como se hacia. En la carpeta donde esta instalado el WING tenemos el archivo wingdbstub.py, recordemos sacarlo de aquí, pues a veces ocurre que tenemos algún otro en nuestra maquina y como actualizamos el WING a una versión mas nueva, archivos de viejas versiones ya no son compatibles y no funcionan, el que esta en la carpeta de instalación del WING siempre es el mas nuevo y esta actualizado.
Lo copiamos a la misma carpeta donde esta el script a debuggear.
Y en el script hay que agregar: import wingdbstub
Allí agregamos dicha linea el resto es verificar que Enable Passive Listen esta activado en las preferencias del Wing, cualquier otro problema, pues mirar la ayuda del Wing que vemos aquí, con eso debería funcionar ya, ponemos un Breakpoint en alguna linea del programa.
Allí esta
Bueno pongamos todo el marcha el webserver y ejecutemos la pagina HTML nuevamente.
Apretemos SEND, debería parar en el BREAKPOINT del WING.
Allí paro, así que ahora podemos ver muchas de las cosas que explicamos, como por ejemplo ver el diccionario de datos form en el Debug Probe.
Allí vemos que no es un diccionario real sino una instancia de FieldStorage, que simula ser un diccionario, y que se maneja con keys y valores asociados igual que los mismos, de cualquier manera sabemos manejarlo así que importa poco lo que es, tenemos métodos para chequear las keys y para leer los valores asociados, así que todo bien.
Vemos en una tirada diferente con nombre fff y age 22 como podemos seguir debuggeando y ir mirando las variables en el Debug Probe.
Bueno en la próxima parte seguiremos complicando de a poco los scripts CGI para poder avanzar lentamente en el tema sin marearnos. Hasta la próxima parte 21 Ricardo Narvaja
MAS CGI SCRIPTS OTROS TIPOS DE ENTRADA Hemos visto en los ejemplos que hemos hecho hasta ahora, paginas HTML donde el usuario ingresa el texto en una simple campo o caja de texto, veremos algunos ejemplos mas de diferentes formas de ingresar datos en el siguiente ejemplo tutor 5a.html. Les recuerdo a los que bajan el curso en forma rápida apenas aparecen las partes en la web, que algunas de las mismas han sido completadas y mejoradas y vueltas a subir así que por favor chequeen los tamaños de las partes que tengan, para tener la ultima versión de cada parte. Abrimos el bloc de notas y tipeamos el código y lo guardamos como tutor 5a.html.
Cuando lo abrimos en un browser vemos.
Allí vemos diferentes tipos de entrada, bastante parecidas a los widgets que vimos en el estudio de GUIs. Ya conocemos los tags para los títulos, TITLE, para el titulo de la pagina, H1 para letras grandes resaltadas y H3 para letras medianas resaltadas.
Vemos también que en el FORM, se usa el método POST para enviarle datos al script CGI y en action se configura el path al script que en este caso es cgi-bin/tutor5.py.
Luego se inicia la tabla encerrada lógicamente entre tags TABLE.
Allí vemos los tags TR que inician cada nueva fila de la tabla, y resaltado en amarillo alineado a la derecha vemos la primera casilla que como es un tag TH usara la letra resaltada pues es un HEADER donde se muestra la palabra Name, y la segunda casilla marcada en rosado, es el campo de texto simple como hemos visto hasta ahora.
Recordemos que el nombre de la key del diccionario para acceder a la data tipeada aquí en el script, sera name.
A continuación vemos unos radio buttons.
Vemos que la tabla continua con otro header con el texto Shoe size y una minitabla dentro de la misma linea que incluye tres casillas para los radio buttons.
En este caso la key para averiguar cual de los tres radio buttons fue marcado es shoesize. A continuación vemos un menú pulldown en la siguiente linea de la tabla, con el header TH Occupation y a su derecha select es lo que indica que es un menú pulldown y las option son cada una de las opciones que se pueden elegir al moverse en el.
En esto caso la key sera job, en todos los casos sera lo que nos indica la asignación name=xxxx, en este caso name=job. A continuación un checkbox, que tendrá un header TH con el titulo Political affiliations y una tablita en la casilla siguiente con tres casillas para los checkbox.
Y vemos los últimos dos, una área de texto que tendrá la key comment y luego el botón para enviar
Veamos el script tutor5.py al cual se llamará y se le enviarán esos datos, por supuesto lo ubicamos en la carpeta cgi-bin.
Vemos que luego de importar cgi y sys form = cgi.FieldStorage( ) Recupera la data tipeada por el usuario y arma el diccionario con los datos usando las diferentes
keys y valores asociados, luego imprime la linea del Content Type y arma en la variable html una string entre triple comillas que será la base de la futura web de respuesta, que lógicamente se inicia con una linea vacía, por convención.
Vemos que la completará con los datos mediante form string mas adelante, tiene un titulo tutor5.py entre tags TITLE, luego entre tags H1 el saludo, luego una linea Greetings horizontal HR y luego entre tags H4 de letra chica las respuestas preparadas para mediante format string reemplazar name, por el nombre que tipeo el usuario y así sucesivamente con shoesize, job, language y comment.
Luego crea un diccionario vacío en data y se fija dentro de un for donde la variable field tomara los valores de las keys posibles, si form contiene dicha key, si es inexistente le asignara a dicha key el valor asociado '(unknown)'. Por otro lado keys como language que provienen de un múltiple choice, si se elijio mas de una opción, su valor asociado sera una lista y no una string por eso el script debe chequear y arreglar esto. Agregamos import wingdbstub para debuggear con WING y ponemos un Breakpoint como vemos en la imagen, arrancamos el webserver y accedemos a la pagina web que armamos.
Llenamos los datos de la web y apretamos SEND para enviárselos al script.
Allí paramos en el breakpoint
Verificamos lo que nos dice el libro >>> field 'language' >>> type(form[field]) >>> Ah vemos que cuando marcamos mas de una opción nos devuelve una lista como valor asociado, en language.
Por lo tanto en los casos que devuelva una simple string, directamente asignara al diccionario data a la correspondiente key el valor asociado que hallo en form que sera una string.
Ahora en el caso que el valor asociado sea una lista va al else donde armara una lista en values, con los valores asociados. >>> values ['Python', 'Tcl'] Y luego los unirá en una sola string usando join.
Vemos como quedo data al terminar
Vemos que unió en una sola string los dos valores asociados que tipeamos, por supuesto hará format string con estos valores reemplazando e imprimiendo para enviar la respuesta al browser.
Acá vemos un ejemplo de que pasaría si nos cambian el orden de la pagina HTML, mientras que mantengan los mismos nombres de las keys, el script CGI no le importara y seguirá funcionando perfectamente.
Vemos que la pagina cambio pero sigue usando las mismas keys y llamando al mismo script.
Y si la completamos
Vemos que el script sigue funcionando perfectamente.
PASANDO PARAMETROS EN CAMPOS OCULTOS EN UN FORM En una pagina HTML los valores de entrada que pueden ser enviados a un script pueden no ser mostrados y si enviados al script como entrada en forma harcodeada, veamos este ejemplo se llama tutor5d.html.
Allí vemos el código HTML , como siempre el titulo de la pagina encerrado entre tags TITLE y luego entre tags H1, con letra grande Common input devices ... , entre tags P se coloca el texto común, y una raya horizontal HR, luego viene el form. Vemos que el mismo invoca en action al mismo script tutor5d.py, pero en este caso, vemos que solo le pasa tres de las cinco entradas que espera.
Vemos que hay dos parámetros ocultos y harcodeados, que están definidos con type=hidden, el valor value=Sue será directamente el valor relacionado que en el script el diccionario tomará para la key name y igualmente será Python el valor relacionado para la key language, como si el usuario hubiera tipeado en un input Sue y Python, vemos que ambos no son strings, en cambio en el caso del campo para tipear ademas de ser del tipo type=text, la variable value si tiene una string que será la que se mostrará en el campo de texto, luego lo que el usuario tipee sera interpretado como valor relacionado de la key job. O sea que le pedimos al usuario que tipee un solo valor de los 3 que enviamos, los otros dos los ponemos directamente nosotros. Arrancamos el webserver y vamos en el browser a la pagina
Vemos que en la respuesta, como maneja 5 entradas y le enviamos solo 3, las otras 2 restantes el script las toma como desconocidas o unknown, mientras que los dos valores harcodeados, y el que tipeo el usuario son tomados como entrada .
Por supuesto también le podemos enviar en el link al script los valores como siempre.
Tomara los parámetros que detecte como validos y los otros los pondrá como unknown. Hasta la parte 22 Ricardo Narvaja
22-MANEJANDO DATOS EN CGI SCRIPTS-COOKIES Hemos visto en las partes anteriores diferentes formas es que los scripts pueden recibir y manejar datos que provienen del cliente, hemos visto el método GET que permite enviar datos en el mismo link, hemos enviado datos con POST que no aparecen en el link, hemos mandando datos ocultos o HIDDEN, el problema que tenemos es que estos datos solo permanecen validos en una sola sesión y si el usuario vuelve a entrar a nuestra pagina, no tendremos una real información de las sesiones anteriores en las que ha participado. O datos guardados de ellas. Una web podría tener una base de datos con información guardada de miles de visitantes, lo cual es una opción compleja que ya veremos, pero una de las soluciones sencillas, son las cookies que la pagina web guarda en pequeños archivos en la maquina del cliente fácilmente localizables, con los datos o preferencias del mismo, de forma que la siguiente vez que entre ya los recupere y no haya que volver a pedir la misma información. Operacionalmente las cookies son strings de información guardada en la maquina del cliente dentro de archivos de texto, y transferidas entre cliente y server en los headers HTTP. Realmente los cookies se envían en los streams de datos de la misma forma que los que hemos visto hasta ahora, solo que son enviados en los headers, y no en los links usando GET o las variantes que vimos hasta ahora de enviar datos sea POST, HIDDEN etc, en este caso es info en los headers HTTP. Python incluye librerías (cuando no jeje) para simplificar la tarea de enviar y recibir cookies la llamada cookielib hace el trabajo de manejarlas para los clientes HTTP que hablan con web servers y el modulo Cookie simplifica la creación y la recepción de los mismos en el server. CREANDO UNA COOKIE Los web browsers como Firefox y Internet Explorer generalmente manejan el lado del cliente del protocolo, guardando y enviando los datos de las cookies, por ahora en este capitulo nosotros estamos interesados en como se manejan los datos de las cookies del lado del server. Las cookies son creadas enviando headers HTTP especiales al inicio del código HTML. Set-Cookie: foo=bar; Content-type: text/html ... En Python crear una cookie es sencillo tipeemos las siguientes lineas en una consola de Python import Cookie, time cook = Cookie.SimpleCookie( ) cook["visited"] = str(time.time( )) print cook.output( ) print 'Content-type: text/html\n' Vemos que la llamada a SimpleCookie( ) crea una especie de diccionario cuyas keys son strings y son los nombres de las cookies por ejemplo visited en este caso y el valor relacionado sera el que me devuelve la función time.time() o sea un valor numerico que refiere al momento en que se visito la pagina, que se lo convierte en string.
Vemos que realmente cook no es un diccionario típico de Python solo se comporta como si lo fuera, podemos agregarle keys y valores relacionados tal como si lo fuera. cook["visited"] =xxxxxx y veo que cuando envío a imprimir usando el método output() y a continuación imprimió el Content Type para armar el código HTML. Veamos otro ejemplo:
Allí vemos la salida no vemos diferencia entre el print común y el que usa output(), veremos cuando lo usemos dentro de un script.
RECIBIENDO UN COOKIE En el punto anterior vimos como se crea un cookie, este se guardara en la maquina del cliente, cuando el mismo visite la misma pagina web en el futuro, los datos guardados en el cookie se enviaran del browser al server en headers HTTP nuevamente en la forma: Cookie: nombre1:valor1; nombre2:valor2...... o Cookie: visited=1137268854.98 Una vez que el browser chequea que pertenecen al mismo server y path, los envía y del lado del server el script CGI los recibe, y usa la environment variable HTTP_COOKIE la cual contiene los datos en forma cruda subidos desde el browser, estos son recuperados desde Python en la forma que sigue: import os, Cookie cooks = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE")) vcook = cooks.get("visited") time = vcook.value Vemos como usamos otra vez aquí SimpleCookie para transformar desde los datos crudos que recibimos por medio de las environment variables en el diccionario el cual se maneja en la forma habitual, en el ejemplo recuperamos el tiempo y lo guardamos en la variable time. Veamos un ejemplo en un script llamado cookies.py.
Vemos que lo que primero hace el script es tratar de leer si existe una cookie con key user eso lo hace aquí. import Cookie, os cookstr = os.environ.get("HTTP_COOKIE") cookies = Cookie.SimpleCookie(cookstr) usercook = cookies.get("user") Vemos que lee las cookies ya creadas las recupera mediante os.environ.get (“HTTP_COOKIE”) Luego arma el diccionario usando SimpleCookie() de forma tal que en cookies tendremos el diccionario con todas las cookies creadas anteriormente por nosotros, del mismo transfiere a usercook si existe una cookie user. Si es la primera vez que se ejecuta el script no existirá ninguna cookie, vemos que a continuación chequea eso: if usercook == None: cookies = Cookie.SimpleCookie( ) cookies['user'] = 'Brian' print cookies greeting = 'His name shall be... %s' % cookies['user'] Si usercook es igual a None, o sea la primera vez que se ejecuta este script y aun no se crearon cookies, entramos y allí se arma un diccionario vacío con SimpleCookie() y se setea la key 'user' y el valor asociado 'Brian', y se manda a imprimir pues va antes que el Content Type. cookies['user'] = 'Brian'
De esa forma se arma en greetings un saludo mediante format string si es la primera vez dirá His name shall be... Set-Cookie: user=Brian ya que no hallamos el valor con value, sino que en el format srings usamos la cookie entera como la setea para su uso. y si ya existe el cookie creado o sea la siguiente vez que vayamos a esa pagina, el saludo cambiara a Welcome back, Brian Allí si se vera Brian pues hallamos el valor relacionado con value aquí
En este primer ejemplo no estamos preguntándole nada al usuario, solo la primera vez que entre se seteará un cookie con un valor harcodeado y la segunda vez se recuperara dicho valor y se imprimirá una string diferente para ver que el script diferencia cuando hay o no cookie seteada. Así que al final se imprime el Content Type, se deja la linea vacía que corresponde y luego se imprime greetings que es el tag con la felicitación por eso esta entre . Veamos si funciona pongamos en script cookies.py en la carpeta cgi-bin y arranquemos el webserver. Corramos el script por primera vez desde el browser http://localhost/cgi-bin/cookie.py Recordemos que debemos usar el link entero no podremos entrar navegando a la carpeta cgi-bin
Si vamos de nuevo (si muestra lo mismo refrescar la pagina)
Vemos como la primera vez seteo la cookie y la segunda la recupero y uso. Hasta la parte 23 Ricardo Narvaja
PYTHON SERVER PAGES El mundo de los scripts CGI es interminable, creo que hemos dado las bases para que cualquiera que tenga interés en el tema, pueda profundizar mas en el, al menos con algunos ejemplos, mostramos lo básico de su funcionamiento, cada cual luego podrá ampliar buscando mas información sobre el mismo. Ahora veremos otra posibilidad de ejecutar código Python en servers, que es la tecnología server side PSP (Python Server Pages), que permite embeber código Python dentro de HTML y es comparable a ASP de Microsoft, JSP de Sun o PHP otro lenguaje de script que se puede embeber en HTML. Por supuesto no daremos todas las posibilidades del código en PSP, solamente mostraremos algun ejemplo básico. Nuestro script se llamara hello.psp y lo escribimos en un archivo de texto el cual le cambiamos la extensión a psp.
Como vemos PSP usa $[ y ]$ para encerrar código Python dentro de ellos, todo lo que esta fuera de ellos es enviado al browser para ser mostrado, mientras lo que esta encerrado es ejecutado. Un script como el anterior debe instalarse en un server que permita la ejecución de PSP, nuestro webserver.py no lo hace, de cualquier forma vemos que su funcionamiento es muy simple. El primer bloque de código es un comentario.
Vemos el # delante correspondiente a los comentarios en Python. La segunda parte es un include que simplemente inserta otro PSP aquí, o sea que allí ejecutaría el código del otro PSP llamado banner.psp.
La tercera parte del código Python es mas útil, como cualquier tecnología Active Scripting, el código Python embebido en HTML, usa apis objeto o exposed object apis, para interactuar con el contexto donde se esta ejecutando, en este caso, el objeto Response es usado para escribir en el browser (tal cual el print en un script CGI) y Request es usado para acceder a los headers HTTP. El objeto Request también tiene un diccionario conteniendo los parámetros GET y POST, así también como las cookies guardadas en el cliente. Veamos que el ejemplo anterior podría haber sido implementado usando un script CGI, y usando print, pero PSP es mas claro en paginas de gran extensión y puede embeber código mucho mas complejo para producir una respuesta. Realmente PSP a pesar de poder realizar el mismo trabajo, es lo opuesto a los ejemplos CGI que vimos anteriormente donde embebíamos HTML en código Python, aquí embebemos Python en código HTML Para mas detalles del código PSP se puede visitar http://www.webwareforpython.org, http://www.python.org Algunas implementaciones de PSP están disponibles como componentes muy populares como el caso de la suite of tools WEBWARE y también como extensión de Apache, llamada mod_python, las cuales son similar a lo visto pero solo cambian los símbolos para encerrar el código Python a .
Muy bueno es este pdf sobre el tema. http://gcubo.org/documentacion/cursos/Programacion_Webware.pdf Por lo tanto para ejecutar este código debemos guardarlo con extensión .psp y colocarlo en un server que utilice Webware o mod_python, lo cual hará que el código Python encerrado entre sea ejecutado como tal.
Aquí se explica como configurar un servidor Apache para ejecutar PSP.
Bueno creo que dimos una mirada a vuelo de pájaro sobre PSP, al menos para que se tenga conocimiento de que existe dicha tecnología. Hasta la parte siguiente FIN