<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5539267871087722416</id><updated>2011-07-30T21:35:20.126-07:00</updated><title type='text'>.</title><subtitle type='html'>Lenguajes C++, python, Clipper</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://wifi4.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://wifi4.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Wifi4.0</name><uri>http://www.blogger.com/profile/16699432101663038724</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>9</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5539267871087722416.post-5795271302840185033</id><published>2009-11-29T04:07:00.000-08:00</published><updated>2009-11-29T07:09:01.533-08:00</updated><title type='text'>Programacion en python</title><content type='html'>&lt;span style="font-size:130%;"&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Herramientas básicas&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Existen dos formas de ejecutar código Python. Podemos escribir líneas de código en el intérprete y obtener una respuesta del intérprete para cada línea (sesión interactiva) o bien podemos escribir el código de un programa en un archivo de texto y ejecutarlo.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;A la hora de realizar una sesión interactiva os aconsejo instalar y utilizar iPython, en lugar de la consola interactiva de Python. Se puede encontrar en http://ipython.scipy.org/. iPython cuenta con características añadidas muy interesantes, como el autocompletado o el operador ?. (para activar la característica de autocompletado en Windows es necesario instalar PyReadline, que puede descargarse desde http://ipython.scipy.org/ moin/PyReadline/Intro)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;La función de autocompletado se lanza pulsando el tabulador. Si escribimos fi y pulsamos Tab nos mostrará una lista de los objetos que comienzan con fi (file, filter y finally). Si escribimos file. y pulsamos Tab nos mostrará una lista de los métodos y propiedades del objeto file.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El operador ? nos muestra información sobre los objetos. Se utiliza añadiendo el símbolo de interrogación al final del nombre del objeto del cual queremos más información. Por ejemplo:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;In [3]: str?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Introducción&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Type: type&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Base Class:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;String Form:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Namespace: Python builtin&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Docstring:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;str(object) -&gt; string&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Return a nice string representation of the object.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;If the argument is a string, the return value is the same object.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En el campo de IDEs y editores de código gratuitos PyDEV (http://pydev.sourceforge.net/) se alza como cabeza de serie. PyDEV es un plugin para Eclipse que permite utilizar este IDE multiplataforma para programar en Python. Cuenta con autocompletado de código (con información sobre cada elemento), resaltado de sintaxis, un depurador gráfico, resaltado de errores, explorador de clases, formateo del código, refactorización, etc. Sin duda es la opción más completa, sobre todo si instalamos las extensiones comerciales, aunque necesita de una cantidad importante de memoria y no es del todo estable.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Otras opciones gratuitas a considerar son SPE o Stani’s Python Editor (http://sourceforge.net/projects/spe/), Eric (http://die-offenbachs.de/eric/), BOA Constructor (http://boa-constructor.sourceforge.net/) o incluso emacs o vim.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Si no te importa desembolsar algo de dinero, Komodo (http://www.activestate.com/komodo_ide/) y Wing IDE (http://www.wingware.com/) son también muy buenas opciones, con montones de características interesantes, como PyDEV, pero mucho más estables y robustos. Además, si desarrollas software libre no comercial puedes contactar con Wing Ware y obtener, con un poco de suerte, una licencia gratuita para Wing IDE Professional :)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Mi primer programa en Python&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Como comentábamos en el capítulo anterior existen dos formas de ejecutar código Python, bien en una sesión interactiva (línea a línea) con el intérprete, o bien de la forma habitual, escribiendo el código en un archivo de código fuente y ejecutándolo.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El primer programa que vamos a escribir en Python es el clásico Hola Mundo, y en este lenguaje es tan simple como:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Hola Mundo”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Vamos a probarlo primero en el intérprete. Ejecuta python o ipython según tus preferencias, escribe la línea anterior y pulsa Enter. El intérprete responderá mostrando en la consola el texto Hola Mundo.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Vamos ahora a crear un archivo de texto con el código anterior, de forma que pudiéramos distribuir nuestro pequeño gran programa entre nuestros amigos. Abre tu editor de texto preferido o bien el IDE que hayas elegido y copia la línea anterior. Guárdalo como hola.py, por ejemplo.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Ejecutar este programa es tan sencillo como indicarle el nombre del archivo a ejecutar al intérprete de Python&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;python hola.py&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;pero vamos a ver cómo simplificarlo aún más.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Si utilizas Windows los archivos .py ya estarán asociados al intérprete de Python, por lo que basta hacer doble clic sobre el archivo para ejecutar el programa. Sin embargo como este programa no hace más que imprimir un texto en la consola, la ejecución es demasiado rápida para poder verlo si quiera. Para remediarlo, vamos a añadir una nueva línea que espere la entrada de datos por parte del usuario.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Hola Mundo”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;raw_input()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;De esta forma se mostrará una consola con el texto Hola Mundo hasta que pulsemos Enter.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Si utilizas Linux (u otro Unix) para conseguir este comportamiento, es decir, para que el sistema operativo abra el archivo .py con el intérprete adecuado, es necesario añadir una nueva línea al principio del archivo:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;#!/usr/bin/python&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Hola Mundo”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;raw_input()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;A esta línea se le conoce en el mundo Unix como shebang, hashbang o sharpbang. El par de caracteres #! indica al sistema operativo que dicho script se debe ejecutar utilizando el intérprete especificado a continuación. De esto se desprende, evidentemente, que si esta no es la ruta en la que está instalado nuestro intérprete de Python, es necesario cambiarla.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Otra opción es utilizar el programa env (de environment, entorno) para preguntar al sistema por la ruta al intérprete de Python, de forma que nuestros usuarios no tengan ningún problema si se diera el caso de que el programa no estuviera instalado en dicha ruta:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;#!/usr/bin/env python&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Hola Mundo”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;raw_input()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Por supuesto además de añadir el shebang, tendremos que dar permisos de ejecución al programa.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;chmod +x hola.py&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Y listo, si hacemos doble clic el programa se ejecutará, mostrando una consola con el texto Hola Mundo, como en el caso de Windows.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;También podríamos correr el programa desde la consola como si tratara de un ejecutable cualquiera:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;./hola.py&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Tipos básicos&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En Python los tipos básicos se dividen en:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Números, como pueden ser • 3 (entero), 15.57 (de coma flotante) o 7 + 5j (complejos)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Cadenas de texto, como • “Hola Mundo”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Valores booleanos: • True (cierto) y False (falso).&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Vamos a crear un par de variables a modo de ejemplo. Una de tipo cadena y una de tipo entero:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;# esto es una cadena&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;c = “Hola Mundo”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;# y esto es un entero&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;e = 23&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;# podemos comprobarlo con la función type&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;type(c)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;type(e)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Como veis en Python, a diferencia de muchos otros lenguajes, no se declara el tipo de la variable al crearla. En Java, por ejemplo, escribiríamos:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;String c = “Hola Mundo”;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;int e = 23;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Este pequeño ejemplo también nos ha servido para presentar los comentarios inline en Python: cadenas de texto que comienzan con el carácter # y que Python ignora totalmente. Hay más tipos de comentarios, de los que hablaremos más adelante.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Números&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Como decíamos, en Python se pueden representar números enteros, reales y complejos.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Enteros&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Los números enteros son aquellos números positivos o negativos que no tienen decimales (además del cero). En Python se pueden representar mediante el tipo int (de integer, entero) o el tipo long (largo). La única diferencia es que el tipo long permite almacenar números más grandes. Es aconsejable no utilizar el tipo long a menos que sea necesario, para no malgastar memoria.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El tipo int de Python se implementa a bajo nivel mediante un tipo long de C. Y dado que Python utiliza C por debajo, como C, y a diferencia de Java, el rango de los valores que puede representar depende de la plataforma.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En la mayor parte de las máquinas el long de C se almacena utilizando 32 bits, es decir, mediante el uso de una variable de tipo int de Python podemos almacenar números de -231 a 231 - 1, o lo que es lo mismo, de -2.147.483.648 a 2.147.483.647. En plataformas de 64 bits, el rango es de -9.223.372.036.854.775.808 hasta 9.223.372.036.854.775.807.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El tipo long de Python permite almacenar números de cualquier precisión, estando limitados solo por la memoria disponible en la máquina.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Al asignar un número a una variable esta pasará a tener tipo int, a menos que el número sea tan grande como para requerir el uso del tipo long.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;# type(entero) devolvería int&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;entero = 23&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;También podemos indicar a Python que un número se almacene usando long añadiendo una L al final:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;# type(entero) devolvería long&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;entero = 23L&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El literal que se asigna a la variable también se puede expresar como un octal, anteponiendo un cero:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;# 027 octal = 23 en base 10&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;entero = 027&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;o bien en hexadecimal, anteponiendo un 0x:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;# 0×17 hexadecimal = 23 en base 10&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;entero = 0×17&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Reales&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Los números reales son los que tienen decimales. En Python se expresan mediante el tipo float. En otros lenguajes de programación, como C, tenemos también el tipo double, similar a float pero de mayor precisión (double = doble precisión). Python, sin embargo, implementa su tipo float a bajo nivel mediante una variable de tipo double de C, es decir, utilizando 64 bits, luego en Python siempre se utiliza doble precisión, y en concreto se sigue el estándar IEEE 754: 1 bit para el signo, 11 para el exponente, y 52 para la mantisa. Esto significa que los valores que podemos representar van desde ±2,2250738585072020 x 10-308 hasta ±1,7976931348623157×10308.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;La mayor parte de los lenguajes de programación siguen el mismo esquema para la representación interna. Pero como muchos sabréis esta tiene sus limitaciones, impuestas por el hardware. Por eso desde Python 2.4 contamos también con un nuevo tipo Decimal, para el caso de que se necesite representar fracciones de forma más precisa. Sin embargo este tipo está fuera del alcance de este tutorial, y sólo es necesario para el ámbito de la programación científica y otros relacionados. Para aplicaciones normales podeis utilizar el tipo float sin miedo, como ha venido haciéndose desde hace años, aunque teniendo en cuenta que los números en coma flotante no son precisos (ni en este ni en otros lenguajes de programación).&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Para representar un número real en Python se escribe primero la parte entera, seguido de un punto y por último la parte decimal.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;real = 0.2703&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;También se puede utilizar notación científica, y añadir una e (de exponente) para indicar un exponente en base 10. Por ejemplo:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;real = 0.1e-3&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;sería equivalente a 0.1 x 10-3 = 0.1 x 0.001 = 0.0001&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Complejos&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Los números complejos son aquellos que tienen parte imaginaria. Si no conocías de su existencia, es más que probable que nunca lo vayas a necesitar, por lo que puedes saltarte este apartado tranquilamente. De hecho la mayor parte de lenguajes de programación carecen de este tipo, aunque sea muy utilizado por ingenieros y científicos en general.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En el caso de que necesitéis utilizar números complejos, o simplemente tengáis curiosidad, os diré que este tipo, llamado complex en Python, también se almacena usando coma flotante, debido a que estos números son una extensión de los números reales. En concreto se almacena en una estructura de C, compuesta por dos variables de tipo double, sirviendo una de ellas para almacenar la parte real y la otra para la parte imaginaria.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Los números complejos en Python se representan de la siguiente forma:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;complejo = 2.1 + 7.8j&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Operadores&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Veamos ahora qué podemos hacer con nuestros números usando los operadores por defecto. Para operaciones más complejas podemos recurrir al módulo math.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Operadores aritméticos&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Operador&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Descripción&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Ejemplo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;+&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Suma&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 3 + 2 # r es 5&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;-&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Resta&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 4 - 7 # r es -3&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Operador&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Descripción&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Ejemplo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;-&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Negación&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = -7 # r es -7&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Multiplicación&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 2 * 6 # r es 12&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;**&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Exponente&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 2 ** 6 # r es 64&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;División&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 3.5 / 2 # r es 1.75&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;//&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;División entera&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 3.5 // 2 # r es 1.0&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;%&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Módulo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 7 % 2 # r es 1&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Puede que tengáis dudas sobre cómo funciona el operador de módulo, y cuál es la diferencia entre división y división entera.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El operador de módulo no hace otra cosa que devolvernos el resto de la división entre los dos operandos. En el ejemplo, 7/2 sería 3, con 1 de resto, luego el módulo es 1.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;La diferencia entre división y división entera no es otra que la que indica su nombre. En la división el resultado que se devuelve es un número real, mientras que en la división entera el resultado que se devuelve es solo la parte entera.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;No obstante hay que tener en cuenta que si utilizamos dos operandos enteros, Python determinará que queremos que la variable resultado también sea un entero, por lo que el resultado de, por ejemplo, 3 / 2 y 3 // 2 sería el mismo: 1.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Si quisiéramos obtener los decimales necesitaríamos que al menos uno de los operandos fuera un número real, bien indicando los decimales&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 3.0 / 2&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;o bien utilizando la función float (no es necesario que sepais lo que significa el término función, ni que recordeis esta forma, lo veremos un poco más adelante):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = float(3) / 2&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Esto es así porque cuando se mezclan tipos de números, Python con&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;vierte todos los operandos al tipo más complejo de entre los tipos de los operandos.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Operadores a nivel de bit&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Si no conocéis estos operadores es poco probable que vayáis a necesitarlos, por lo que podéis obviar esta parte. Si aún así tenéis curiosidad os diré que estos son operadores que actúan sobre las representaciones en binario de los operandos.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Por ejemplo, si veis una operación como 3 &amp;amp; 2, lo que estais viendo es un and bit a bit entre los números binarios 11 y 10 (las representaciones en binario de 3 y 2).&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El operador and (&amp;amp;), del inglés “y”, devuelve 1 si el primer bit operando es 1 y el segundo bit operando es 1. Se devuelve 0 en caso contrario.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El resultado de aplicar and bit a bit a 11 y 10 sería entonces el número binario 10, o lo que es lo mismo, 2 en decimal (el primer dígito es 1 para ambas cifras, mientras que el segundo es 1 sólo para una de ellas).&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El operador or (|), del inglés “o”, devuelve 1 si el primer operando es 1 o el segundo operando es 1. Para el resto de casos se devuelve 0.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El operador xor u or exclusivo (^) devuelve 1 si uno de los operandos es 1 y el otro no lo es.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El operador not (~), del inglés “no”, sirve para negar uno a uno cada bit; es decir, si el operando es 0, cambia a 1 y si es 1, cambia a 0.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Por último los operadores de desplazamiento (&lt;&lt;&gt;&gt;) sirven para desplazar los bits n posiciones hacia la izquierda o la derecha.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Operador&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Descripción&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Ejemplo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&amp;amp;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;and&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 3 &amp;amp; 2 # r es 2&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;or&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 3 | 2 # r es 3&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;^&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;xor&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 3 ^ 2 # r es 1&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;~&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;not&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = ~3 # r es -4&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&lt;&lt; r =" 3"&gt;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Desplazamiento der.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 3 &gt;&gt; 1 # r es 1&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Cadenas&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Las cadenas no son más que texto encerrado entre comillas simples (‘cadena’) o dobles (“cadena”). Dentro de las comillas se pueden añadir caracteres especiales escapándolos con \, como \n, el carácter de nueva línea, o \t, el de tabulación.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Una cadena puede estar precedida por el carácter u o el carácter r, los cuales indican, respectivamente, que se trata de una cadena que utiliza codificación Unicode y una cadena raw (del inglés, cruda). Las cadenas raw se distinguen de las normales en que los caracteres escapados mediante la barra invertida (\) no se sustituyen por sus contrapartidas. Esto es especialmente útil, por ejemplo, para las expresiones regulares, como veremos en el capítulo correspondiente.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;unicode = u”äóè”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;raw = r”\n”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;También es posible encerrar una cadena entre triples comillas (simples o dobles). De esta forma podremos escribir el texto en varias líneas, y al imprimir la cadena, se respetarán los saltos de línea que introdujimos sin tener que recurrir al carácter \n, así como las comillas sin tener que escaparlas.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;triple = “““primera linea&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;esto se vera en otra linea”””&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Las cadenas también admiten operadores como +, que funciona realizando una concatenación de las cadenas utilizadas como operandos y *, en la que se repite la cadena tantas veces como lo indique el número utilizado como segundo operando.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;a = “uno”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;b = “dos”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;c = a + b # c es “unodos”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;c = a * 3 # c es “unounouno”&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Booleanos&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Como decíamos al comienzo del capítulo una variable de tipo booleano sólo puede tener dos valores: True (cierto) y False (falso). Estos valores son especialmente importantes para las expresiones condicionales y los bucles, como veremos más adelante.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En realidad el tipo bool (el tipo de los booleanos) es una subclase del tipo int. Puede que esto no tenga mucho sentido para tí si no conoces los términos de la orientación a objetos, que veremos más adelante, aunque tampoco es nada importante.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Estos son los distintos tipos de operadores con los que podemos trabajar con valores booleanos, los llamados operadores lógicos o condicionales:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Operador&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Descripción&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Ejemplo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;and&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;¿se cumple a y b?&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = True and False # r es False&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;or&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;¿se cumple a o b?&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = True or False # r es True&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;not&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;No a&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = not True # r es False&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Los valores booleanos son además el resultado de expresiones que utilizan operadores relacionales (comparaciones entre valores):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Operador&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Descripción&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Ejemplo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;==&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;¿son iguales a y b?&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 5 == 3 # r es False&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;!=&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;¿son distintos a y b?&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 5 != 3 # r es True&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&lt; ¿es a menor que b? r = 5 &lt;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;¿es a mayor que b?&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 5 &gt; 3 # r es True&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&lt;= ¿es a menor o igual que b? r = 5 &lt;= 5 # r es True &gt;=&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;¿es a mayor o igual que b?&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;r = 5 &gt;= 3 # r es True&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Colecc iones&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En el capítulo anterior vimos algunos tipos básicos, como los números, las cadenas de texto y los booleanos. En esta lección veremos algunos tipos de colecciones de datos: listas, tuplas y diccionarios.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Listas&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;La lista es un tipo de colección ordenada. Sería equivalente a lo que en otros lenguajes se conoce por arrays, o vectores.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Las listas pueden contener cualquier tipo de dato: números, cadenas, booleanos, … y también listas.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Crear una lista es tan sencillo como indicar entre corchetes, y separados por comas, los valores que queremos incluir en la lista:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l = [22, True, “una lista”, [1, 2]]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Podemos acceder a cada uno de los elementos de la lista escribiendo el nombre de la lista e indicando el índice del elemento entre corchetes. Ten en cuenta sin embargo que el índice del primer elemento de la lista es 0, y no 1:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l = [11, False]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = l[0] # mi_var vale 11&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Si queremos acceder a un elemento de una lista incluida dentro de otra lista tendremos que utilizar dos veces este operador, primero para indicar a qué posición de la lista exterior queremos acceder, y el segundo para seleccionar el elemento de la lista interior:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l = [“una lista”, [1, 2]]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = l[1][0] # mi_var vale 1&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;También podemos utilizar este operador para modificar un elemento de la lista si lo colocamos en la parte izquierda de una asignación:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l = [22, True]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l[0] = 99 # Con esto l valdrá [99, True]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El uso de los corchetes para acceder y modificar los elementos de una lista es común en muchos lenguajes, pero Python nos depara varias sorpresas muy agradables.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Una curiosidad sobre el operador [] de Python es que podemos utilizar también números negativos. Si se utiliza un número negativo como índice, esto se traduce en que el índice empieza a contar desde el final, hacia la izquierda; es decir, con [-1] accederíamos al último elemento de la lista, con [-2] al penúltimo, con [-3], al antepenúltimo, y así sucesivamente.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Otra cosa inusual es lo que en Python se conoce como slicing o particionado, y que consiste en ampliar este mecanismo para permitir seleccionar porciones de la lista. Si en lugar de un número escribimos dos números inicio y fin separados por dos puntos (inicio:fin) Python interpretará que queremos una lista que vaya desde la posición inicio a la posición fin, sin incluir este último. Si escribimos tres números (inicio:fin:salto) en lugar de dos, el tercero se utiliza para determinar cada cuantas posiciones añadir un elemento a la lista.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l = [99, True, “una lista”, [1, 2]]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = l[0:2] # mi_var vale [99, True]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = l[0:4:2] # mi_var vale [99, “una lista”]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Los números negativos también se pueden utilizar en un slicing, con el mismo comportamiento que se comentó anteriormente.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Hay que mencionar así mismo que no es necesario indicar el principio y el final del slicing, sino que, si estos se omiten, se usarán por defecto las posiciones de inicio y fin de la lista, respectivamente:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l = [99, True, “una lista”]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = l[1:] # mi_var vale [True, “una lista”]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = l[:2] # mi_var vale [99, True]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = l[:] # mi_var vale [99, True, “una lista”]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = l[::2] # mi_var vale [99, “una lista”]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;También podemos utilizar este mecanismo para modificar la lista:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l = [99, True, “una lista”, [1, 2]]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l[0:2] = [0, 1] # l vale [0, 1, “una lista”, [1, 2]]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;pudiendo incluso modificar el tamaño de la lista si la lista de la parte derecha de la asignación tiene un tamaño menor o mayor que el de la selección de la parte izquierda de la asignación:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;l[0:2] = [False] # l vale [False, “una lista”, [1, 2]]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En todo caso las listas ofrecen mecanismos más cómodos para ser modificadas a través de las funciones de la clase correspondiente, aunque no veremos estos mecanismos hasta más adelante, después de explicar lo que son las clases, los objetos y las funciones.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Tuplas&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Todo lo que hemos explicado sobre las listas se aplica también a las tuplas, a excepción de la forma de definirla, para lo que se utilizan paréntesis en lugar de corchetes.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;t = (1, 2, True, “python”)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En realidad el constructor de la tupla es la coma, no el paréntesis, pero el intérprete muestra los paréntesis, y nosotros deberíamos utilizarlos, por claridad.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&gt;&gt;&gt; t = 1, 2, 3&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&gt;&gt;&gt; type(t)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;type “tuple”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Además hay que tener en cuenta que es necesario añadir una coma para tuplas de un solo elemento, para diferenciarlo de un elemento entre paréntesis.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&gt;&gt;&gt; t = (1)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&gt;&gt;&gt; type(t)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Colecciones&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;27&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;type “int”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&gt;&gt;&gt; t = (1,)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;&gt;&gt;&gt; type(t)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;type “tuple”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Para referirnos a elementos de una tupla, como en una lista, se usa el operador []:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = t[0] # mi_var es 1&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;mi_var = t[0:2] # mi_var es (1, 2)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Podemos utilizar el operador [] debido a que las tuplas, al igual que las listas, forman parte de un tipo de objetos llamados secuencias. Permitirme un pequeño inciso para indicaros que las cadenas de texto también son secuencias, por lo que no os extrañará que podamos hacer cosas como estas:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;c = “hola mundo”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;c[0] # h&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;c[5:] # mundo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;c[::3] # hauo&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Volviendo al tema de las tuplas, su diferencia con las listas estriba en que las tuplas no poseen estos mecanismos de modificación a través de funciones tan útiles de los que hablábamos al final de la anterior sección.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Además son inmutables, es decir, sus valores no se pueden modificar una vez creada; y tienen un tamaño fijo.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;A cambio de estas limitaciones las tuplas son más “ligeras” que las listas, por lo que si el uso que le vamos a dar a una colección es muy básico, puedes utilizar tuplas en lugar de listas y ahorrar memoria.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Diccionarios&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Los diccionarios, también llamados matrices asociativas, deben su nombre a que son colecciones que relacionan una clave y un valor. Por ejemplo, veamos un diccionario de películas y directores:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;d = {“Love Actually “: “Richard Curtis”,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;“Kill Bill”: “Tarantino”,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;“Amélie”: “Jean-Pierre Jeunet”}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El primer valor se trata de la clave y el segundo del valor asociado a la clave. Como clave podemos utilizar cualquier valor inmutable: podríamos usar números, cadenas, booleanos, tuplas, … pero no listas o diccionarios, dado que son mutables. Esto es así porque los diccionarios se implementan como tablas hash, y a la hora de introducir un nuevo par clave-valor en el diccionario se calcula el hash de la clave para después poder encontrar la entrada correspondiente rápidamente. Si se modificara el objeto clave después de haber sido introducido en el diccionario, evidentemente, su hash también cambiaría y no podría ser encontrado.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;La diferencia principal entre los diccionarios y las listas o las tuplas es que a los valores almacenados en un diccionario se les accede no por su índice, porque de hecho no tienen orden, sino por su clave, utilizando de nuevo el operador [].&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;d[“Love Actually “] # devuelve “Richard Curtis”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Al igual que en listas y tuplas también se puede utilizar este operador para reasignar valores.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;d[“Kill Bill”] = “Quentin Tarantino”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Sin embargo en este caso no se puede utilizar slicing, entre otras cosas porque los diccionarios no son secuencias, si no mappings (mapeados, asociaciones).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Control de flujo&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En esta lección vamos a ver los condicionales y los bucles.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Sentencias condicionales&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Si un programa no fuera más que una lista de órdenes a ejecutar de forma secuencial, una por una, no tendría mucha utilidad. Los condicionales nos permiten comprobar condiciones y hacer que nuestro programa se comporte de una forma u otra, que ejecute un fragmento de código u otro, dependiendo de esta condición.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Aquí es donde cobran su importancia el tipo booleano y los operadores lógicos y relacionales que aprendimos en el capítulo sobre los tipos básicos de Python.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;La forma más simple de un estamento condicional es un if (del inglés si) seguido de la condición a evaluar, dos puntos (:) y en la siguiente línea e indentado, el código a ejecutar en caso de que se cumpla dicha condición.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;fav = “mundogeek.net”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;# si (if) fav es igual a “mundogeek.net”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if fav == “mundogeek.net”:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Tienes buen gusto!”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Gracias”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Como veis es bastante sencillo.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Eso si, aseguraros de que indentáis el código tal cual se ha hecho en el ejemplo, es decir, aseguraros de pulsar Tabulación antes de las dos órdenes print, dado que esta es la forma de Python de saber que vuestra intención es la de que los dos print se ejecuten sólo en el caso de que se cumpla la condición, y no la de que se imprima la primera cadena si se cumple la condición y la otra siempre, cosa que se expresaría así:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if fav == “mundogeek.net”:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Tienes buen gusto!”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Gracias”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;En otros lenguajes de programación los bloques de código se determinan encerrándolos entre llaves, y el indentarlos no se trata más que de una buena práctica para que sea más sencillo seguir el flujo del programa con un solo golpe de vista. Por ejemplo, el código anterior expresado en Java sería algo así:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;String fav = “mundogeek.net”;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if (fav.equals(“mundogeek.net”)){&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;System.out.println(“Tienes buen gusto!”);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;System.out.println(“Gracias”);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Sin embargo, como ya hemos comentado, en Python se trata de una obligación, y no de una elección. De esta forma se obliga a los programadores a indentar su código para que sea más sencillo de leer :)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if … else&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Vamos a ver ahora un condicional algo más complicado. ¿Qué haríamos si quisiéramos que se ejecutaran unas ciertas órdenes en el caso de que la condición no se cumpliera? Sin duda podríamos añadir otro if que tuviera como condición la negación del primero:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if fav == “mundogeek.net”:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Tienes buen gusto!”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Gracias”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if fav != “mundogeek.net”:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Vaya, que lástima”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;pero el condicional tiene una segunda construcción mucho más útil:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if fav == “mundogeek.net”:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Tienes buen gusto!”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Gracias”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;else:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Vaya, que lástima”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Vemos que la segunda condición se puede sustituir con un else (del inglés: si no, en caso contrario). Si leemos el código vemos que tiene bastante sentido: “si fav es igual a mundogeek.net, imprime esto y esto, si no, imprime esto otro”.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if … elif … elif … else&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Todavía queda una construcción más que ver, que es la que hace uso del elif.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if numero &lt;&gt; 0:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Positivo”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;else:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print “Cero”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;elif es una contracción de else if, por lo tanto elif numero &gt; 0 puede leerse como “si no, si numero es mayor que 0”. Es decir, primero se evalúa la condición del if. Si es cierta, se ejecuta su código y se continúa ejecutando el código posterior al condicional; si no se cumple, se evalúa la condición del elif. Si se cumple la condición del elif se ejecuta su código y se continua ejecutando el código posterior al condicional; si no se cumple y hay más de un elif se continúa con el siguiente en orden de aparición. Si no se cumple la condición del if ni de ninguno de los elif, se ejecuta el código del else.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;A if C else B&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;También existe una construcción similar al operador ? de otros lenguajes, que no es más que una forma compacta de expresar un if else. En esta construcción se evalúa el predicado C y se devuelve A si se cumple o B si no se cumple: A if C else B. Veamos un ejemplo:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;var = “par” if (num % 2 == 0) else “impar”&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Y eso es todo. Si conocéis otros lenguajes de programación puede que esperarais que os hablara ahora del switch, pero en Python no existe esta construcción, que podría emularse con un simple diccionario, así que pasemos directamente a los bucles.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Bucles&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Mientras que los condicionales nos permiten ejecutar distintos fragmentos de código dependiendo de ciertas condiciones, los bucles nos permiten ejecutar un mismo fragmento de código un cierto número de veces, mientras se cumpla una determinada condición.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;while&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;El bucle while (mientras) ejecuta un fragmento de código mientras se cumpla una condición.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;edad = 0&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;while edad &lt; edad =" edad" entrada =" raw_input(“"&gt; “)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if entrada == “adios”:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;break&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;else:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print entrada&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Para obtener lo que el usuario escriba en pantalla utilizamos la función raw_input. No es necesario que sepais qué es una función ni cómo funciona exactamente, simplemente aceptad por ahora que en cada iteración del bucle la variable entrada contendrá lo que el usuario escribió hasta pulsar Enter.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Comprobamos entonces si lo que escribió el usuario fue adios, en cuyo caso se ejecuta la orden break o si era cualquier otra cosa, en cuyo caso se imprime en pantalla lo que el usuario escribió.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;La palabra clave break (romper) sale del bucle en el que estamos.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Este bucle se podría haber escrito también, no obstante, de la siguiente forma:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;salir = False&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;while not salir:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;entrada = raw_input()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;if entrada == “adios”:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;salir = True&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;else:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;print entrada&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;pero nos ha servido para ver cómo funciona break.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Otra palabra clave que nos podemos encontrar dentro de los bucles es continue (continuar). Como habréis adivinado no hace otra cosa que pasar directamente a la siguiente iteración del bucle.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;edad = 0&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;while edad &lt; edad =" edad" 2 ="="" secuencia =" [“uno”," i =" 0;" param2 =" 2," param1 =" “hola”)"&gt;&gt;&gt; mi_funcion(“hola”)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;Traceback (most recent call last):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;File “&lt;/span&gt;&lt;stdin style="color: rgb(255, 255, 255); font-family: times new roman;"&gt;”, line 1, in &lt;module&gt;&lt;br /&gt;TypeError: mi_funcion() takes exactly 2 arguments (1 given)&lt;br /&gt;También es posible, no obstante, definir funciones con un número variable de argumentos, o bien asignar valores por defecto a los parámetros para el caso de que no se indique ningún valor para ese parámetro al llamar a la función.&lt;br /&gt;Los valores por defecto para los parámetros se definen situando un signo igual después del nombre del parámetro y a continuación el valor por defecto:&lt;br /&gt;def imprimir(texto, veces = 1):&lt;br /&gt;print veces * texto&lt;br /&gt;En el ejemplo anterior si no indicamos un valor para el segundo parámetro se imprimirá una sola vez la cadena que le pasamos como primer parámetro:&lt;br /&gt;&gt;&gt;&gt; imprimir(“hola”)&lt;br /&gt;hola&lt;br /&gt;si se le indica otro valor, será este el que se utilice:&lt;br /&gt;&gt;&gt;&gt; imprimir(“hola”, 2)&lt;br /&gt;holahola&lt;br /&gt;Para definir funciones con un número variable de argumentos colocamos un último parámetro para la función cuyo nombre debe precederse de un signo *:&lt;br /&gt;def varios(param1, param2, *otros):&lt;br /&gt;for val in otros:&lt;br /&gt;print val&lt;br /&gt;varios(1, 2)&lt;br /&gt;varios(1, 2, 3)&lt;br /&gt;varios(1, 2, 3, 4)&lt;br /&gt;Esta sintaxis funciona creando una tupla (de nombre otros en el ejemplo) en la que se almacenan los valores de todos los parámetros extra pasados como argumento. Para la primera llamada, varios(1, 2), la tupla otros estaría vacía dado que no se han pasado más parámetros que los dos definidos por defecto, por lo tanto no se imprimiría nada. En la segunda llamada otros valdría (3, ), y en la tercera (3, 4).&lt;br /&gt;También se puede preceder el nombre del último parámetro con **, en cuyo caso en lugar de una tupla se utilizaría un diccionario. Las claves de este diccionario serían los nombres de los parámetros indicados al&lt;br /&gt;Funciones llamar a la función y los valores del diccionario, los valores asociados a estos parámetros.&lt;br /&gt;En el siguiente ejemplo se utiliza la función items de los diccionarios, que devuelve una lista con sus elementos, para imprimir los parámetros que contiene el diccionario.&lt;br /&gt;def varios(param1, param2, **otros):&lt;br /&gt;for i in otros.items():&lt;br /&gt;print i&lt;br /&gt;varios(1, 2, tercero = 3)&lt;br /&gt;Los que conozcáis algún otro lenguaje de programación os estaréis preguntando si en Python al pasar una variable como argumento de una función estas se pasan por referencia o por valor. En el paso por referencia lo que se pasa como argumento es una referencia o puntero a la variable, es decir, la dirección de memoria en la que se encuentra el contenido de la variable, y no el contenido en si. En el paso por valor, por el contrario, lo que se pasa como argumento es el valor que contenía la variable.&lt;br /&gt;La diferencia entre ambos estriba en que en el paso por valor los cambios que se hagan sobre el parámetro no se ven fuera de la función, dado que los argumentos de la función son variables locales a la función que contienen los valores indicados por las variables que se pasaron como argumento. Es decir, en realidad lo que se le pasa a la función son copias de los valores y no las variables en si.&lt;br /&gt;Si quisiéramos modificar el valor de uno de los argumentos y que estos cambios se reflejaran fuera de la función tendríamos que pasar el parámetro por referencia.&lt;br /&gt;En C los argumentos de las funciones se pasan por valor, aunque se puede simular el paso por referencia usando punteros. En Java también se usa paso por valor, aunque para las variables que son objetos lo que se hace es pasar por valor la referencia al objeto, por lo que en realidad parece paso por referencia.&lt;br /&gt;En Python también se utiliza el paso por valor de referencias a objetos,&lt;br /&gt;como en Java, aunque en el caso de Python, a diferencia de Java, todo es un objeto (para ser exactos lo que ocurre en realidad es que al objeto se le asigna otra etiqueta o nombre en el espacio de nombres local de la función).&lt;br /&gt;Sin embargo no todos los cambios que hagamos a los parámetros dentro de una función Python se reflejarán fuera de esta, ya que hay que tener en cuenta que en Python existen objetos inmutables, como las tuplas, por lo que si intentáramos modificar una tupla pasada como parámetro lo que ocurriría en realidad es que se crearía una nueva instancia, por lo que los cambios no se verían fuera de la función.&lt;br /&gt;Veamos un pequeño programa para demostrarlo. En este ejemplo se hace uso del método append de las listas. Un método no es más que una función que pertenece a un objeto, en este caso a una lista; y append, en concreto, sirve para añadir un elemento a una lista.&lt;br /&gt;def f(x, y):&lt;br /&gt;x = x + 3&lt;br /&gt;y.append(23)&lt;br /&gt;print x, y&lt;br /&gt;x = 22&lt;br /&gt;y = [22]&lt;br /&gt;f(x, y)&lt;br /&gt;print x, y&lt;br /&gt;El resultado de la ejecución de este programa sería&lt;br /&gt;25 [22, 23]&lt;br /&gt;22 [22, 23]&lt;br /&gt;Como vemos la variable x no conserva los cambios una vez salimos de la función porque los enteros son inmutables en Python. Sin embargo la variable y si los conserva, porque las listas son mutables.&lt;br /&gt;En resumen: los valores mutables se comportan como paso por referencia, y los inmutables como paso por valor.&lt;br /&gt;Con esto terminamos todo lo relacionado con los parámetros de las funciones. Veamos por último cómo devolver valores, para lo que se utiliza la palabra clave return:&lt;br /&gt;def sumar(x, y):&lt;br /&gt;return x + y&lt;br /&gt;print sumar(3, 2)&lt;br /&gt;Como vemos esta función tan sencilla no hace otra cosa que sumar los valores pasados como parámetro y devolver el resultado como valor de retorno.&lt;br /&gt;También podríamos pasar varios valores que retornar a return.&lt;br /&gt;def f(x, y):&lt;br /&gt;return x * 2, y * 2&lt;br /&gt;a, b = f(1, 2)&lt;br /&gt;Sin embargo esto no quiere decir que las funciones Python puedan devolver varios valores, lo que ocurre en realidad es que Python crea una tupla al vuelo cuyos elementos son los valores a retornar, y esta única variable es la que se devuelve.&lt;br /&gt;&lt;br /&gt;Orientación a Objetos&lt;br /&gt;&lt;br /&gt;En el capítulo de introducción ya comentábamos que Python es un lenguaje multiparadigma en el se podía trabajar con programación estructurada, como veníamos haciendo hasta ahora, o con programación orientada a objetos o programación funcional.&lt;br /&gt;La Programación Orientada a Objetos (POO u OOP según sus siglas en inglés) es un paradigma de programación en el que los conceptos del mundo real relevantes para nuestro problema se modelan a través de clases y objetos, y en el que nuestro programa consiste en una serie de interacciones entre estos objetos.&lt;br /&gt;Clases y objetos&lt;br /&gt;Para entender este paradigma primero tenemos que comprender qué es una clase y qué es un objeto. Un objeto es una entidad que agrupa un estado y una funcionalidad relacionadas. El estado del objeto se define a través de variables llamadas atributos, mientras que la funcionalidad se modela a través de funciones a las que se les conoce con el nombre de métodos del objeto.&lt;br /&gt;Un ejemplo de objeto podría ser un coche, en el que tendríamos atributos como la marca, el número de puertas o el tipo de carburante y métodos como arrancar y parar. O bien cualquier otra combinación de atributos y métodos según lo que fuera relevante para nuestro programa.&lt;br /&gt;Una clase, por otro lado, no es más que una plantilla genérica a partir&lt;br /&gt;de la cuál instanciar los objetos; plantilla que es la que define qué atributos y métodos tendrán los objetos de esa clase.&lt;br /&gt;Volviendo a nuestro ejemplo: en el mundo real existe un conjunto de objetos a los que llamamos coches y que tienen un conjunto de atributos comunes y un comportamiento común, esto es a lo que llamamos clase. Sin embargo, mi coche no es igual que el coche de mi vecino, y aunque pertenecen a la misma clase de objetos, son objetos distintos.&lt;br /&gt;En Python las clases se definen mediante la palabra clave class seguida del nombre de la clase, dos puntos (:) y a continuación, indentado, el cuerpo de la clase. Como en el caso de las funciones, si la primera línea del cuerpo se trata de una cadena de texto, esta será la cadena de documentación de la clase o docstring.&lt;br /&gt;class Coche:&lt;br /&gt;“””Abstraccion de los objetos coche.”””&lt;br /&gt;def __init__(self, gasolina):&lt;br /&gt;self.gasolina = gasolina&lt;br /&gt;print “Tenemos”, gasolina, “litros”&lt;br /&gt;def arrancar(self):&lt;br /&gt;if self.gasolina &gt; 0:&lt;br /&gt;print “Arranca”&lt;br /&gt;else:&lt;br /&gt;print “No arranca”&lt;br /&gt;def conducir(self):&lt;br /&gt;if self.gasolina &gt; 0:&lt;br /&gt;self.gasolina -= 1&lt;br /&gt;print “Quedan”, self.gasolina, “litros”&lt;br /&gt;else:&lt;br /&gt;print “No se mueve”&lt;br /&gt;Lo primero que llama la atención en el ejemplo anterior es el nombre tan curioso que tiene el método __init__. Este nombre es una convención y no un capricho. El método __init__, con una doble barra baja al principio y final del nombre, se ejecuta justo después de crear un nuevo objeto a partir de la clase, proceso que se conoce con el nombre de instanciación. El método __init__ sirve, como sugiere su nombre, para realizar cualquier proceso de inicialización que sea necesario.&lt;br /&gt;Como vemos el primer parámetro de __init__ y del resto de métodos&lt;br /&gt;de la clase es siempre self. Esta es una idea inspirada en Modula-3 y sirve para referirse al objeto actual. Este mecanismo es necesario para poder acceder a los atributos y métodos del objeto diferenciando, por ejemplo, una variable local mi_var de un atributo del objeto self.mi_var.&lt;br /&gt;Si volvemos al método __init__ de nuestra clase Coche veremos cómo se utiliza self para asignar al atributo gasolina del objeto (self.gasolina) el valor que el programador especificó para el parámetro gasolina. El parámetro gasolina se destruye al final de la función, mientras que el atributo gasolina se conserva (y puede ser accedido) mientras el objeto viva.&lt;br /&gt;Para crear un objeto se escribiría el nombre de la clase seguido de cualquier parámetro que sea necesario entre paréntesis. Estos parámetros son los que se pasarán al método __init__, que como decíamos es el método que se llama al instanciar la clase.&lt;br /&gt;mi_coche = Coche(3)&lt;br /&gt;Os preguntareis entonces cómo es posible que a la hora de crear nuestro primer objeto pasemos un solo parámetro a __init__, el número 3, cuando la definición de la función indica claramente que precisa de dos parámetros (self y gasolina). Esto es así porque Python pasa el primer argumento (la referencia al objeto que se crea) automágicamente.&lt;br /&gt;Ahora que ya hemos creado nuestro objeto, podemos acceder a sus atributos y métodos mediante la sintaxis objeto.atributo y objeto.metodo():&lt;br /&gt;&gt;&gt;&gt; print mi_coche.gasolina&lt;br /&gt;3&lt;br /&gt;&gt;&gt;&gt; mi_coche.arrancar()&lt;br /&gt;Arranca&lt;br /&gt;&gt;&gt;&gt; mi_coche.conducir()&lt;br /&gt;Quedan 2 litros&lt;br /&gt;&gt;&gt;&gt; mi_coche.conducir()&lt;br /&gt;Quedan 1 litros&lt;br /&gt;&gt;&gt;&gt; mi_coche.conducir()&lt;br /&gt;Quedan 0 litros&lt;br /&gt;&gt;&gt;&gt; mi_coche.conducir()&lt;br /&gt;No se mueve&lt;br /&gt;&gt;&gt;&gt; mi_coche.arrancar()&lt;br /&gt;No arranca&lt;br /&gt;&gt;&gt;&gt; print mi_coche.gasolina&lt;br /&gt;0&lt;br /&gt;Como último apunte recordar que en Python, como ya se comentó en repetidas ocasiones anteriormente, todo son objetos. Las cadenas, por ejemplo, tienen métodos como upper(), que devuelve el texto en mayúsculas o count(sub), que devuelve el número de veces que se encontró la cadena sub en el texto.&lt;br /&gt;Herencia&lt;br /&gt;Hay tres conceptos que son básicos para cualquier lenguaje de programación orientado a objetos: el encapsulamiento, la herencia y el polimorfismo.&lt;br /&gt;En un lenguaje orientado a objetos cuando hacemos que una clase (subclase) herede de otra clase (superclase) estamos haciendo que la subclase contenga todos los atributos y métodos que tenía la superclase. No obstante al acto de heredar de una clase también se le llama a menudo “extender una clase”.&lt;br /&gt;Supongamos que queremos modelar los instrumentos musicales de una banda, tendremos entonces una clase Guitarra, una clase Batería, una clase Bajo, etc. Cada una de estas clases tendrá una serie de atributos y métodos, pero ocurre que, por el mero hecho de ser instrumentos musicales, estas clases compartirán muchos de sus atributos y métodos; un ejemplo sería el método tocar().&lt;br /&gt;Es más sencillo crear un tipo de objeto Instrumento con las atributos y métodos comunes e indicar al programa que Guitarra, Batería y Bajo son tipos de instrumentos, haciendo que hereden de Instrumento.&lt;br /&gt;Para indicar que una clase hereda de otra se coloca el nombre de la clase de la que se hereda entre paréntesis después del nombre de la clase:&lt;br /&gt;class Instrumento:&lt;br /&gt;def __init__(self, precio):&lt;br /&gt;self.precio = precio&lt;br /&gt;def tocar(self):&lt;br /&gt;print “Estamos tocando musica”&lt;br /&gt;def romper(self):&lt;br /&gt;print “Eso lo pagas tu”&lt;br /&gt;print “Son”, self.precio, “$$$”&lt;br /&gt;class Bateria(Instrumento):&lt;br /&gt;pass&lt;br /&gt;class Guitarra(Instrumento):&lt;br /&gt;pass&lt;br /&gt;Como Bateria y Guitarra heredan de Instrumento, ambos tienen un método tocar() y un método romper(), y se inicializan pasando un parámetro precio. Pero, ¿qué ocurriría si quisiéramos especificar un nuevo parámetro tipo_cuerda a la hora de crear un objeto Guitarra? Bastaría con escribir un nuevo método __init__ para la clase Guitarra que se ejecutaría en lugar del __init__ de Instrumento. Esto es lo que se conoce como sobreescribir métodos.&lt;br /&gt;Ahora bien, puede ocurrir en algunos casos que necesitemos sobreescribir un método de la clase padre, pero que en ese método queramos ejecutar el método de la clase padre porque nuestro nuevo método no necesite más que ejecutar un par de nuevas instrucciones extra. En ese caso usaríamos la sintaxis SuperClase.metodo(self, args) para llamar al método de igual nombre de la clase padre. Por ejemplo, para llamar al método __init__ de Instrumento desde Guitarra usaríamos Instrumento.__init__(self, precio)&lt;br /&gt;Observad que en este caso si es necesario especificar el parámetro self.&lt;br /&gt;Herencia múltiple&lt;br /&gt;En Python, a diferencia de otros lenguajes como Java o C#, se permite la herencia múltiple, es decir, una clase puede heredar de varias clases a la vez. Por ejemplo, podríamos tener una clase Cocodrilo que heredara de la clase Terrestre, con métodos como caminar() y atributos como velocidad_caminar y de la clase Acuatico, con métodos como nadar() y atributos como velocidad_nadar. Basta con enumerar las clases de&lt;br /&gt;las que se hereda separándolas por comas:&lt;br /&gt;class Cocodrilo(Terrestre, Acuatico):&lt;br /&gt;pass&lt;br /&gt;En el caso de que alguna de las clases padre tuvieran métodos con el mismo nombre y número de parámetros las clases sobreescribirían la implementación de los métodos de las clases más a su derecha en la definición.&lt;br /&gt;En el siguiente ejemplo, como Terrestre se encuentra más a la izquierda, sería la definición de desplazar de esta clase la que prevalecería, y por lo tanto si llamamos al método desplazar de un objeto de tipo Cocodrilo lo que se imprimiría sería “El animal anda”.&lt;br /&gt;class Terrestre:&lt;br /&gt;def desplazar(self):&lt;br /&gt;print “El animal anda”&lt;br /&gt;class Acuatico:&lt;br /&gt;def desplazar(self):&lt;br /&gt;print “El animal nada”&lt;br /&gt;class Cocodrilo(Terrestre, Acuatico):&lt;br /&gt;pass&lt;br /&gt;c = Cocodrilo()&lt;br /&gt;c.desplazar()&lt;br /&gt;Polimorfismo&lt;br /&gt;La palabra polimorfismo, del griego poly morphos (varias formas), se refiere a la habilidad de objetos de distintas clases de responder al mismo mensaje. Esto se puede conseguir a través de la herencia: un objeto de una clase derivada es al mismo tiempo un objeto de la clase padre, de forma que allí donde se requiere un objeto de la clase padre también se puede utilizar uno de la clase hija.&lt;br /&gt;Python, al ser de tipado dinámico, no impone restricciones a los tipos que se le pueden pasar a una función, por ejemplo, más allá de que el objeto se comporte como se espera: si se va a llamar a un método f() del objeto pasado como parámetro, por ejemplo, evidentemente el objeto tendrá que contar con ese método. Por ese motivo, a diferencia&lt;br /&gt;de lenguajes de tipado estático como Java o C++, el polimorfismo en Python no es de gran importancia.&lt;br /&gt;En ocasiones también se utiliza el término polimorfismo para referirse a la sobrecarga de métodos, término que se define como la capacidad del lenguaje de determinar qué método ejecutar de entre varios métodos con igual nombre según el tipo o número de los parámetros que se le pasa. En Python no existe sobrecarga de métodos (el último método sobreescribiría la implementación de los anteriores), aunque se puede conseguir un comportamiento similar recurriendo a funciones con valores por defecto para los parámetros o a la sintaxis *params o **params explicada en el capítulo sobre las funciones en Python, o bien usando decoradores (mecanismo que veremos más adelante).&lt;br /&gt;Encapsulación&lt;br /&gt;La encapsulación se refiere a impedir el acceso a determinados métodos y atributos de los objetos estableciendo así qué puede utilizarse desde fuera de la clase.&lt;br /&gt;Esto se consigue en otros lenguajes de programación como Java utilizando modificadores de acceso que definen si cualquiera puede acceder a esa función o variable (public) o si está restringido el acceso a la propia clase (private).&lt;br /&gt;En Python no existen los modificadores de acceso, y lo que se suele hacer es que el acceso a una variable o función viene determinado por su nombre: si el nombre comienza con dos guiones bajos (y no termina también con dos guiones bajos) se trata de una variable o función privada, en caso contrario es pública. Los métodos cuyo nombre comienza y termina con dos guiones bajos son métodos especiales que Python llama automáticamente bajo ciertas circunstancias, como veremos al final del capítulo.&lt;br /&gt;En el siguiente ejemplo sólo se imprimirá la cadena correspondiente al método publico(), mientras que al intentar llamar al método __privado() Python lanzará una excepción quejándose de que no existe (evidentemente existe, pero no lo podemos ver porque es privado).&lt;br /&gt;class Ejemplo:&lt;br /&gt;def publico(self):&lt;br /&gt;print “Publico”&lt;br /&gt;def __privado(self):&lt;br /&gt;print “Privado”&lt;br /&gt;ej = Ejemplo()&lt;br /&gt;ej.publico()&lt;br /&gt;ej.__privado()&lt;br /&gt;Este mecanismo se basa en que los nombres que comienzan con un doble guión bajo se renombran para incluir el nombre de la clase (característica que se conoce con el nombre de name mangling). Esto implica que el método o atributo no es realmente privado, y podemos acceder a él mediante una pequeña trampa:&lt;br /&gt;ej._Ejemplo__privado()&lt;br /&gt;En ocasiones también puede suceder que queramos permitir el acceso a algún atributo de nuestro objeto, pero que este se produzca de forma controlada. Para esto podemos escribir métodos cuyo único cometido sea este, métodos que normalmente, por convención, tienen nombres como getVariable y setVariable; de ahí que se conozcan también con el nombre de getters y setters.&lt;br /&gt;class Fecha():&lt;br /&gt;def __init__(self):&lt;br /&gt;self.__dia = 1&lt;br /&gt;def getDia(self):&lt;br /&gt;return self.__dia&lt;br /&gt;def setDia(self, dia):&lt;br /&gt;if dia &gt; 0 and dia &lt; __dia =" dia" mi_fecha =" Fecha()" __dia =" 1"&gt; 0 and dia &lt; __dia =" dia" dia =" property(getDia," mi_fecha =" Fecha()" dia =" 33"&gt; o &gt;= se lanzará una excepción. Si se utilizan los operadores == o != para comprobar si dos objetos son iguales, se comprueba si son el mismo objeto (si tienen el mismo id).&lt;br /&gt;__len__(self)&lt;br /&gt;Método llamado para comprobar la longitud del objeto. Se utiliza, por&lt;br /&gt;ejemplo, cuando se llama a la función len(obj) sobre nuestro objeto. Como es de suponer, el método debe devolver la longitud del objeto.&lt;br /&gt;Existen bastantes más métodos especiales, que permite entre otras cosas utilizar el mecanismo de slicing sobre nuestro objeto, utilizar los operadores aritméticos o usar la sintaxis de diccionarios, pero un estudio exhaustivo de todos los métodos queda fuera del propósito del capítulo.&lt;br /&gt;&lt;br /&gt;Revisitando Objetos&lt;br /&gt;&lt;br /&gt;En los capítulos dedicados a los tipos simples y las colecciones veíamos por primera vez algunos de los objetos del lenguaje Python: números, booleanos, cadenas de texto, diccionarios, listas y tuplas.&lt;br /&gt;Ahora que sabemos qué son las clases, los objetos, las funciones, y los métodos es el momento de revisitar estos objetos para descubrir su verdadero potencial.&lt;br /&gt;Veremos a continuación algunos métodos útiles de estos objetos. Evidentemente, no es necesario memorizarlos, pero si, al menos, recordar que existen para cuando sean necesarios.&lt;br /&gt;Diccionarios&lt;br /&gt;D.get(k[, d])&lt;br /&gt;Busca el valor de la clave k en el diccionario. Es equivalente a utilizar D[k] pero al utilizar este método podemos indicar un valor a devolver por defecto si no se encuentra la clave, mientras que con la sintaxis D[k], de no existir la clave se lanzaría una excepción.&lt;br /&gt;D.has_key(k)&lt;br /&gt;Comprueba si el diccionario tiene la clave k. Es equivalente a la sintaxis k in D.&lt;br /&gt;D.items()&lt;br /&gt;Devuelve una lista de tuplas con pares clave-valor.&lt;br /&gt;D.keys()&lt;br /&gt;Devuelve una lista de las claves del diccionario.&lt;br /&gt;D.pop(k[, d])&lt;br /&gt;Borra la clave k del diccionario y devuelve su valor. Si no se encuentra dicha clave se devuelve d si se especificó el parámetro o bien se lanza una excepción.&lt;br /&gt;D.values()&lt;br /&gt;Devuelve una lista de los valores del diccionario.&lt;br /&gt;Cadenas&lt;br /&gt;S.count(sub[, start[, end]])&lt;br /&gt;Devuelve el número de veces que se encuentra sub en la cadena. Los parámetros opcionales start y end definen una subcadena en la que buscar.&lt;br /&gt;S.find(sub[, start[, end]])&lt;br /&gt;Devuelve la posición en la que se encontró por primera vez sub en la cadena o -1 si no se encontró.&lt;br /&gt;S.join(sequence)&lt;br /&gt;Devuelve una cadena resultante de concatenar las cadenas de la secuencia seq separadas por la cadena sobre la que se llama el método.&lt;br /&gt;S.partition(sep)&lt;br /&gt;Busca el separador sep en la cadena y devuelve una tupla con la subcadena hasta dicho separador, el separador en si, y la subcadena del separador hasta el final de la cadena. Si no se encuentra el separador, la tupla contendrá la cadena en si y dos cadenas vacías.&lt;br /&gt;S.replace(old, new[, count])&lt;br /&gt;Devuelve una cadena en la que se han reemplazado todas las ocurrencias de la cadena old por la cadena new. Si se especifica el parámetro count, este indica el número máximo de ocurrencias a reemplazar.&lt;br /&gt;S.split([sep [,maxsplit]])&lt;br /&gt;Devuelve una lista conteniendo las subcadenas en las que se divide nuestra cadena al dividirlas por el delimitador sep. En el caso de que&lt;br /&gt;no se especifique sep, se usan espacios. Si se especifica maxsplit, este indica el número máximo de particiones a realizar.&lt;br /&gt;Listas&lt;br /&gt;L.append(object)&lt;br /&gt;Añade un objeto al final de la lista.&lt;br /&gt;L.count(value)&lt;br /&gt;Devuelve el número de veces que se encontró value en la lista.&lt;br /&gt;L.extend(iterable)&lt;br /&gt;Añade los elementos del iterable a la lista.&lt;br /&gt;L.index(value[, start[, stop]])&lt;br /&gt;Devuelve la posición en la que se encontró la primera ocurrencia de value. Si se especifican, start y stop definen las posiciones de inicio y fin de una sublista en la que buscar.&lt;br /&gt;L.insert(index, object)&lt;br /&gt;Inserta el objeto object en la posición index.&lt;br /&gt;L.pop([index])&lt;br /&gt;Devuelve el valor en la posición index y lo elimina de la lista. Si no se especifica la posición, se utiliza el último elemento de la lista.&lt;br /&gt;L.remove(value)&lt;br /&gt;Eliminar la primera ocurrencia de value en la lista.&lt;br /&gt;L.reverse()&lt;br /&gt;Invierte la lista. Esta función trabaja sobre la propia lista desde la que se invoca el método, no sobre una copia.&lt;br /&gt;L.sort(cmp=None, key=None, reverse=False)&lt;br /&gt;Ordena la lista. Si se especifica cmp, este debe ser una función que tome como parámetro dos valores x e y de la lista y devuelva -1 si x es menor que y, 0 si son iguales y 1 si x es mayor que y.&lt;br /&gt;El parámetro reverse es un booleano que indica si se debe ordenar la lista de forma inversa, lo que sería equivalente a llamar primero a L.sort() y después a L.reverse().&lt;br /&gt;Por último, si se especifica, el parámetro key debe ser una función que tome un elemento de la lista y devuelva una clave a utilizar a la hora de comparar, en lugar del elemento en si.&lt;br /&gt;&lt;br /&gt;Programación funcional&lt;br /&gt;&lt;br /&gt;La programación funcional es un paradigma en el que la programación se basa casi en su totalidad en funciones, entendiendo el concepto de función según su definición matemática, y no como los simples subprogramas de los lenguajes imperativos que hemos visto hasta ahora.&lt;br /&gt;En los lenguajes funcionales puros un programa consiste exclusivamente en la aplicación de distintas funciones a un valor de entrada para obtener un valor de salida.&lt;br /&gt;Python, sin ser un lenguaje puramente funcional incluye varias características tomadas de los lenguajes funcionales como son las funciones de orden superior o las funciones lambda (funciones anónimas).&lt;br /&gt;Funciones de orden superior&lt;br /&gt;El concepto de funciones de orden superior se refiere al uso de funciones como si de un valor cualquiera se tratara, posibilitando el pasar funciones como parámetros de otras funciones o devolver funciones como valor de retorno.&lt;br /&gt;Esto es posible porque, como hemos insistido ya en varias ocasiones, en Python todo son objetos. Y las funciones no son una excepción.&lt;br /&gt;Veamos un pequeño ejemplo&lt;br /&gt;def saludar(lang):&lt;br /&gt;def saludar_es():&lt;br /&gt;print “Hola”&lt;br /&gt;def saludar_en():&lt;br /&gt;print “Hi”&lt;br /&gt;def saludar_fr():&lt;br /&gt;print “Salut”&lt;br /&gt;lang_func = {“es”: saludar_es,&lt;br /&gt;“en”: saludar_en,&lt;br /&gt;“fr”: saludar_fr}&lt;br /&gt;return lang_func[lang]&lt;br /&gt;f = saludar(“es”)&lt;br /&gt;f()&lt;br /&gt;Como podemos observar lo primero que hacemos en nuestro pequeño programa es llamar a la función saludar con un parámetro “es”. En la función saludar se definen varias funciones: saludar_es, saludar_en y saludar_fr y a continuación se crea un diccionario que tiene como claves cadenas de texto que identifican a cada lenguaje, y como valores las funciones. El valor de retorno de la función es una de estas funciones. La función a devolver viene determinada por el valor del parámetro lang que se pasó como argumento de saludar.&lt;br /&gt;Como el valor de retorno de saludar es una función, como hemos visto, esto quiere decir que f es una variable que contiene una función. Podemos entonces llamar a la función a la que se refiere f de la forma en que llamaríamos a cualquier otra función, añadiendo unos paréntesis y, de forma opcional, una serie de parámetros entre los paréntesis.&lt;br /&gt;Esto se podría acortar, ya que no es necesario almacenar la función que nos pasan como valor de retorno en una variable para poder llamarla:&lt;br /&gt;&gt;&gt;&gt; saludar(“en”)()&lt;br /&gt;Hi&lt;br /&gt;&gt;&gt;&gt; saludar(“fr”)()&lt;br /&gt;Salut&lt;br /&gt;En este caso el primer par de paréntesis indica los parámetros de la función saludar, y el segundo par, los de la función devuelta por saludar.&lt;br /&gt;&lt;br /&gt;Iteraciones de orden superior sobre listas&lt;br /&gt;&lt;br /&gt;Una de las cosas más interesantes que podemos hacer con nuestras funciones de orden superior es pasarlas como argumentos de las funciones map, filter y reduce. Estas funciones nos permiten sustituir los bucles típicos de los lenguajes imperativos mediante construcciones equivalentes.&lt;br /&gt;map(function, sequence[, sequence, ...])&lt;br /&gt;La función map aplica una función a cada elemento de una secuencia y devuelve una lista con el resultado de aplicar la función a cada elemento. Si se pasan como parámetros n secuencias, la función tendrá que aceptar n argumentos. Si alguna de las secuencias es más pequeña que las demás, el valor que le llega a la función function para posiciones mayores que el tamaño de dicha secuencia será None.&lt;br /&gt;A continuación podemos ver un ejemplo en el que se utiliza map para elevar al cuadrado todos los elementos de una lista:&lt;br /&gt;def cuadrado(n):&lt;br /&gt;return n ** 2&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = map(cuadrado, l)&lt;br /&gt;filter(function, sequence)&lt;br /&gt;La funcion filter verifica que los elementos de una secuencia cumplan una determinada condición, devolviendo una secuencia con los elementos que cumplen esa condición. Es decir, para cada elemento de sequence se aplica la función function; si el resultado es True se añade a la lista y en caso contrario se descarta.&lt;br /&gt;A continuación podemos ver un ejemplo en el que se utiliza filter para conservar solo los números que son pares.&lt;br /&gt;def es_par(n):&lt;br /&gt;return (n % 2.0 == 0)&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = filter(es_par, l)&lt;br /&gt;reduce(function, sequence[, initial])&lt;br /&gt;La función reduce aplica una función a pares de elementos de una secuencia hasta dejarla en un solo valor.&lt;br /&gt;A continuación podemos ver un ejemplo en el que se utiliza reduce para sumar todos los elementos de una lista.&lt;br /&gt;def sumar(x, y):&lt;br /&gt;return x + y&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = reduce(sumar, l)&lt;br /&gt;Funciones lambda&lt;br /&gt;El operador lambda sirve para crear funciones anónimas en línea. Al ser funciones anónimas, es decir, sin nombre, estas no podrán ser referenciadas más tarde.&lt;br /&gt;Las funciones lambda se construyen mediante el operador lambda, los parámetros de la función separados por comas (atención, SIN paréntesis), dos puntos (:) y el código de la función.&lt;br /&gt;Esta construcción podrían haber sido de utilidad en los ejemplos anteriores para reducir código. El programa que utilizamos para explicar filter, por ejemplo, podría expresarse así:&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = filter(lambda n: n % 2.0 == 0, l)&lt;br /&gt;Comparemoslo con la versión anterior:&lt;br /&gt;def es_par(n):&lt;br /&gt;return (n % 2.0 == 0)&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = filter(es_par, l)&lt;br /&gt;Las funciones lambda están restringidas por la sintaxis a una sola&lt;br /&gt;expresión.&lt;br /&gt;&lt;br /&gt;Comprensión de listas&lt;br /&gt;&lt;br /&gt;En Python 3000 map, filter y reduce perderán importancia. Y aunque estas funciones se mantendrán, reduce pasará a formar parte del módulo functools, con lo que quedará fuera de las funciones disponibles por defecto, y map y filter se desaconsejarán en favor de las list comprehensions o comprensión de listas.&lt;br /&gt;La comprensión de listas es una característica tomada del lenguaje de programación funcional Haskell que está presente en Python desde la versión 2.0 y consiste en una construcción que permite crear listas a partir de otras listas.&lt;br /&gt;Cada una de estas construcciones consta de una expresión que determina cómo modificar el elemento de la lista original, seguida de una o varias clausulas for y opcionalmente una o varias clausulas if.&lt;br /&gt;Veamos un ejemplo de cómo se podría utilizar la comprensión de listas para elevar al cuadrado todos los elementos de una lista, como hicimos en nuestro ejemplo de map.&lt;br /&gt;l2 = [n ** 2 for n in l]&lt;br /&gt;Esta expresión se leería como “para cada n en l haz n ** 2”. Como vemos tenemos primero la expresión que modifica los valores de la lista original (n ** 2), después el for, el nombre que vamos a utilizar para referirnos al elemento actual de la lista original, el in, y la lista sobre la que se itera.&lt;br /&gt;El ejemplo que utilizamos para la función filter (conservar solo los números que son pares) se podría expresar con comprensión de listas así:&lt;br /&gt;l2 = [n for n in l if n % 2.0 == 0]&lt;br /&gt;Veamos por último un ejemplo de compresión de listas con varias clausulas for:&lt;br /&gt;l = [0, 1, 2, 3]&lt;br /&gt;m = [“a”, “b”]&lt;br /&gt;n = [s * v for s in m&lt;br /&gt;for v in l&lt;br /&gt;if v &gt; 0]&lt;br /&gt;Esta construcción sería equivalente a una serie de for-in anidados:&lt;br /&gt;l = [0, 1, 2, 3]&lt;br /&gt;m = [“a”, “b”]&lt;br /&gt;n = []&lt;br /&gt;for s in m:&lt;br /&gt;for v in l:&lt;br /&gt;if v &gt; 0:&lt;br /&gt;n.append(s* v)&lt;br /&gt;Generadores&lt;br /&gt;Las expresiones generadoras funcionan de forma muy similar a la comprensión de listas. De hecho su sintaxis es exactamente igual, a excepción de que se utilizan paréntesis en lugar de corchetes:&lt;br /&gt;l2 = (n ** 2 for n in l)&lt;br /&gt;Sin embargo las expresiones generadoras se diferencian de la comprensión de listas en que no se devuelve una lista, sino un generador.&lt;br /&gt;&gt;&gt;&gt; l2 = [n ** 2 for n in l]&lt;br /&gt;&gt;&gt;&gt; l2&lt;br /&gt;[0, 1, 4, 9]&lt;br /&gt;&gt;&gt;&gt; l2 = (n ** 2 for n in l)&lt;br /&gt;&gt;&gt;&gt; l2&lt;br /&gt;&lt;generator&gt;&lt;br /&gt;Un generador es una clase especial de función que genera valores sobre los que iterar. Para devolver el siguiente valor sobre el que iterar se utiliza la palabra clave yield en lugar de return. Veamos por ejemplo un generador que devuelva números de n a m con un salto s.&lt;br /&gt;def mi_generador(n, m, s):&lt;br /&gt;while(n &lt;= m): yield n n += s Programación funcional 63 &gt;&gt;&gt; x = mi_generador(0, 5, 1)&lt;br /&gt;&gt;&gt;&gt; x&lt;br /&gt;&lt;generator&gt;&lt;br /&gt;El generador se puede utilizar en cualquier lugar donde se necesite un objeto iterable. Por ejemplo en un for-in:&lt;br /&gt;for n in mi_generador(0, 5, 1):&lt;br /&gt;print n&lt;br /&gt;Como no estamos creando una lista completa en memoria, sino generando un solo valor cada vez que se necesita, en situaciones en las que no sea necesario tener la lista completa el uso de generadores puede suponer una gran diferencia de memoria. En todo caso siempre es posible crear una lista a partir de un generador mediante la función list:&lt;br /&gt;lista = list(mi_generador)&lt;br /&gt;Decoradores&lt;br /&gt;Un decorador no es es mas que una función que recibe una función como parámetro y devuelve otra función como resultado. Por ejemplo podríamos querer añadir la funcionalidad de que se imprimiera el nombre de la función llamada por motivos de depuración:&lt;br /&gt;def mi_decorador(funcion):&lt;br /&gt;def nueva(*args):&lt;br /&gt;print “Llamada a la funcion”, funcion.__name__&lt;br /&gt;retorno = funcion(*args)&lt;br /&gt;return retorno&lt;br /&gt;return nueva&lt;br /&gt;Como vemos el código de la función mi_decorador no hace más que crear una nueva función y devolverla. Esta nueva función imprime el nombre de la función a la que “decoramos”, ejecuta el código de dicha función, y devuelve su valor de retorno. Es decir, que si llamáramos a la nueva función que nos devuelve mi_decorador, el resultado sería el mismo que el de llamar directamente a la función que le pasamos como parámetro, exceptuando el que se imprimiría además el nombre de la función.&lt;br /&gt;Supongamos como ejemplo una función imp que no hace otra cosa que mostrar en pantalla la cadena pasada como parámetro.&lt;br /&gt;&gt;&gt;&gt; imp(“hola”)&lt;br /&gt;hola&lt;br /&gt;&gt;&gt;&gt; mi_decorador(imp)(“hola”)&lt;br /&gt;Llamada a la función imp&lt;br /&gt;hola&lt;br /&gt;La sintaxis para llamar a la función que nos devuelve mi_decorador no es muy clara, aunque si lo estudiamos detenidamente veremos que no tiene mayor complicación. Primero se llama a la función que decora con la función a decorar: mi_decorador(imp); y una vez obtenida la función ya decorada se la puede llamar pasando el mismo parámetro que se pasó anteriormente: mi_decorador(imp)(“hola”)&lt;br /&gt;Esto se podría expresar más claramente precediendo la definición de la función que queremos decorar con el signo @ seguido del nombre de la función decoradora:&lt;br /&gt;@mi_decorador&lt;br /&gt;def imp(s):&lt;br /&gt;print s&lt;br /&gt;De esta forma cada vez que se llame a imp se estará llamando realmente a la versión decorada. Python incorpora esta sintaxis desde la versión 2.4 en adelante.&lt;br /&gt;Si quisiéramos aplicar más de un decorador bastaría añadir una nueva línea con el nuevo decorador.&lt;br /&gt;@otro_decorador&lt;br /&gt;@mi_decorador&lt;br /&gt;def imp(s):&lt;br /&gt;print s&lt;br /&gt;Es importante advertir que los decoradores se ejecutarán de abajo a arriba. Es decir, en este ejemplo primero se ejecutaría mi_decorador y después otro_decorador.&lt;br /&gt;&lt;br /&gt;Excepciones&lt;br /&gt;Las excepciones son errores detectados por Python durante la ejecución del programa. Cuando el intérprete se encuentra con una situación excepcional, como el intentar dividir un número entre 0 o el intentar acceder a un archivo que no existe, este genera o lanza una excepción, informando al usuario de que existe algún problema.&lt;br /&gt;Si la excepción no se captura el flujo de ejecución se interrumpe y se muestra la información asociada a la excepción en la consola de forma que el programador pueda solucionar el problema.&lt;br /&gt;Veamos un pequeño programa que lanzaría una excepción al intentar dividir 1 entre 0.&lt;br /&gt;def division(a, b):&lt;br /&gt;return a / b&lt;br /&gt;def calcular():&lt;br /&gt;division(1, 0)&lt;br /&gt;calcular()&lt;br /&gt;Si lo ejecutamos obtendremos el siguiente mensaje de error:&lt;br /&gt;$ python ejemplo.py&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;File “ejemplo.py”, line 7, in&lt;br /&gt;calcular()&lt;br /&gt;File “ejemplo.py”, line 5, in calcular&lt;br /&gt;division(1, 0)&lt;br /&gt;File “ejemplo.py”, line 2, in division&lt;br /&gt;a / b&lt;br /&gt;ZeroDivisionError: integer division or modulo by zero&lt;br /&gt;Lo primero que se muestra es el trazado de pila o traceback, que consiste en una lista con las llamadas que provocaron la excepción. Como&lt;br /&gt;vemos en el trazado de pila, el error estuvo causado por la llamada a calcular() de la línea 7, que a su vez llama a division(1, 0) en la línea 5 y en última instancia por la ejecución de la sentencia a / b de la línea 2 de division.&lt;br /&gt;A continuación vemos el tipo de la excepción, ZeroDivisionError, junto a una descripción del error: “integer division or modulo by zero” (módulo o división entera entre cero).&lt;br /&gt;En Python se utiliza una construcción try-except para capturar y tratar las excepciones. El bloque try (intentar) define el fragmento de código en el que creemos que podría producirse una excepción. El bloque except (excepción) permite indicar el tratamiento que se llevará a cabo de producirse dicha excepción. Muchas veces nuestro tratamiento de la excepción consistirá simplemente en imprimir un mensaje más amigable para el usuario, otras veces nos interesará registrar los errores y de vez en cuando podremos establecer una estrategia de resolución del problema.&lt;br /&gt;En el siguiente ejemplo intentamos crear un objeto f de tipo fichero. De no existir el archivo pasado como parámetro, se lanza una excepción de tipo IOError, que capturamos gracias a nuestro try-except.&lt;br /&gt;try:&lt;br /&gt;f = file(“archivo.txt”)&lt;br /&gt;except:&lt;br /&gt;print “El archivo no existe”&lt;br /&gt;Python permite utilizar varios except para un solo bloque try, de forma que podamos dar un tratamiento distinto a la excepción dependiendo del tipo de excepción de la que se trate. Esto es una buena práctica, y es tan sencillo como indicar el nombre del tipo a continuación del except.&lt;br /&gt;try:&lt;br /&gt;num = int(“3a”)&lt;br /&gt;print no_existe&lt;br /&gt;except NameError:&lt;br /&gt;print “La variable no existe”&lt;br /&gt;except ValueError:&lt;br /&gt;print “El valor no es un numero”&lt;br /&gt;Cuando se lanza una excepción en el bloque try, se busca en cada una de las clausulas except un manejador adecuado para el tipo de error que se produjo. En caso de que no se encuentre, se propaga la excepción.&lt;br /&gt;Además podemos hacer que un mismo except sirva para tratar más de una excepción usando una tupla para listar los tipos de error que queremos que trate el bloque:&lt;br /&gt;try:&lt;br /&gt;num = int(“3a”)&lt;br /&gt;print no_existe&lt;br /&gt;except (NameError, ValueError):&lt;br /&gt;print “Ocurrio un error”&lt;br /&gt;La construcción try-except puede contar además con una clausula else, que define un fragmento de código a ejecutar sólo si no se ha producido ninguna excepción en el try.&lt;br /&gt;try:&lt;br /&gt;num = 33&lt;br /&gt;except:&lt;br /&gt;print “Hubo un error!”&lt;br /&gt;else:&lt;br /&gt;print “Todo esta bien”&lt;br /&gt;También existe una clausula finally que se ejecuta siempre, se produzca o no una excepción. Esta clausula se suele utilizar, entre otras cosas, para tareas de limpieza.&lt;br /&gt;try:&lt;br /&gt;z = x / y&lt;br /&gt;except ZeroDivisionError:&lt;br /&gt;print “Division por cero”&lt;br /&gt;finally:&lt;br /&gt;print “Limpiando”&lt;br /&gt;También es interesante comentar que como programadores podemos crear y lanzar nuestras propias excepciones. Basta crear una clase que herede de Exception o cualquiera de sus hijas y lanzarla con raise.&lt;br /&gt;class MiError(Exception):&lt;br /&gt;def __init__(self, valor):&lt;br /&gt;self.valor = valor&lt;br /&gt;def __str__(self):&lt;br /&gt;return “Error “ + str(self.valor)&lt;br /&gt;try:&lt;br /&gt;if resultado &gt; 20:&lt;br /&gt;raise MiError(33)&lt;br /&gt;except MiError, e:&lt;br /&gt;print e&lt;br /&gt;Por último, a continuación se listan a modo de referencia las excepciones disponibles por defecto, así como la clase de la que deriva cada una de ellas entre paréntesis.&lt;br /&gt;BaseException: Clase de la que heredan todas las excepciones.&lt;br /&gt;Exception(BaseException): Super clase de todas las excepciones que no sean de salida.&lt;br /&gt;GeneratorExit(Exception): Se pide que se salga de un generador.&lt;br /&gt;StandardError(Exception): Clase base para todas las excepciones que no tengan que ver con salir del intérprete.&lt;br /&gt;ArithmeticError(StandardError): Clase base para los errores aritméticos.&lt;br /&gt;FloatingPointError(ArithmeticError): Error en una operación de coma flotante.&lt;br /&gt;OverflowError(ArithmeticError): Resultado demasiado grande para poder representarse.&lt;br /&gt;ZeroDivisionError(ArithmeticError): Lanzada cuando el segundo argumento de una operación de división o módulo era 0.&lt;br /&gt;AssertionError(StandardError): Falló la condición de un estamento assert.&lt;br /&gt;AttributeError(StandardError): No se encontró el atributo.&lt;br /&gt;EOFError(StandardError): Se intentó leer más allá del final de fichero.&lt;br /&gt;EnvironmentError(StandardError): Clase padre de los errores relacionados con la entrada/salida.&lt;br /&gt;IOError(EnvironmentError): Error en una operación de entrada/salida.&lt;br /&gt;OSError(EnvironmentError): Error en una llamada a sistema.&lt;br /&gt;WindowsError(OSError): Error en una llamada a sistema en Windows.&lt;br /&gt;ImportError(StandardError): No se encuentra el módulo o el elemento del módulo que se quería importar.&lt;br /&gt;LookupError(StandardError): Clase padre de los errores de acceso.&lt;br /&gt;IndexError(LookupError): El índice de la secuencia está fuera del rango posible.&lt;br /&gt;KeyError(LookupError): La clave no existe.&lt;br /&gt;MemoryError(StandardError): No queda memoria suficiente.&lt;br /&gt;NameError(StandardError): No se encontró ningún elemento con ese nombre.&lt;br /&gt;UnboundLocalError(NameError): El nombre no está asociado a ninguna variable.&lt;br /&gt;ReferenceError(StandardError): El objeto no tiene ninguna referencia fuerte apuntando hacia él.&lt;br /&gt;RuntimeError(StandardError): Error en tiempo de ejecución no especificado.&lt;br /&gt;NotImplementedError(RuntimeError): Ese método o función no está implementado.&lt;br /&gt;SyntaxError(StandardError): Clase padre para los errores sintácticos.&lt;br /&gt;IndentationError(SyntaxError): Error en la indentación del archivo.&lt;br /&gt;TabError(IndentationError): Error debido a la mezcla de espacios y tabuladores.&lt;br /&gt;SystemError(StandardError): Error interno del intérprete.&lt;br /&gt;TypeError(StandardError): Tipo de argumento no apropiado.&lt;br /&gt;ValueError(StandardError): Valor del argumento no apropiado.&lt;br /&gt;UnicodeError(ValueError): Clase padre para los errores relacionados con unicode.&lt;br /&gt;UnicodeDecodeError(UnicodeError): Error de decodificación unicode.&lt;br /&gt;UnicodeEncodeError(UnicodeError): Error de codificación unicode.&lt;br /&gt;UnicodeTranslateError(UnicodeError): Error de traducción unicode.&lt;br /&gt;StopIteration(Exception): Se utiliza para indicar el final del iterador.&lt;br /&gt;Warning(Exception): Clase padre para los avisos.&lt;br /&gt;DeprecationWarning(Warning): Clase padre para avisos sobre características obsoletas.&lt;br /&gt;FutureWarning(Warning): Aviso. La semántica de la construcción cambiará en un futuro.&lt;br /&gt;ImportWarning(Warning): Aviso sobre posibles errores a la hora de importar.&lt;br /&gt;PendingDeprecationWarning(Warning): Aviso sobre características que se marcarán como obsoletas en un futuro próximo.&lt;br /&gt;RuntimeWarning(Warning): Aviso sobre comportmaientos dudosos en tiempo de ejecución.&lt;br /&gt;SyntaxWarning(Warning): Aviso sobre sintaxis dudosa.&lt;br /&gt;UnicodeWarning(Warning): Aviso sobre problemas relacionados con Unicode, sobre todo con problemas de conversión.&lt;br /&gt;UserWarning(Warning): Clase padre para avisos creados por el programador.&lt;br /&gt;KeyboardInterrupt(BaseException): El programa fué interrumpido por el usuario.&lt;br /&gt;SystemExit(BaseException): Petición del intérprete para terminar la ejecución.&lt;br /&gt;&lt;br /&gt;Módulos y Paquetes&lt;br /&gt;Módulos&lt;br /&gt;Para facilitar el mantenimiento y la lectura los programas demasiado largos pueden dividirse en módulos, agrupando elementos relacionados. Los módulos son entidades que permiten una organización y división lógica de nuestro código. Los ficheros son su contrapartida física: cada archivo Python almacenado en disco equivale a un módulo.&lt;br /&gt;Vamos a crear nuestro primer módulo entonces creando un pequeño archivo modulo.py con el siguiente contenido:&lt;br /&gt;def mi_funcion():&lt;br /&gt;print “una funcion”&lt;br /&gt;class MiClase:&lt;br /&gt;def __init__(self):&lt;br /&gt;print “una clase”&lt;br /&gt;print “un modulo”&lt;br /&gt;Si quisiéramos utilizar la funcionalidad definida en este módulo en nuestro programa tendríamos que importarlo. Para importar un módulo se utiliza la palabra clave import seguida del nombre del módulo, que consiste en el nombre del archivo menos la extensión. Como ejemplo, creemos un archivo programa.py en el mismo directorio en el que guardamos el archivo del módulo (esto es importante, porque si no se encuentra en el mismo directorio Python no podrá encontrarlo), con el siguiente contenido:&lt;br /&gt;import modulo&lt;br /&gt;modulo.mi_funcion()&lt;br /&gt;El import no solo hace que tengamos disponible todo lo definido dentro del módulo, sino que también ejecuta el código del módulo. Por esta razón nuestro programa, además de imprimir el texto “una funcion” al llamar a mi_funcion, también imprimiría el texto “un modulo”, debido al print del módulo importado. No se imprimiría, no obstante, el texto “una clase”, ya que lo que se hizo en el módulo fue tan solo definir de la clase, no instanciarla.&lt;br /&gt;La clausula import también permite importar varios módulos en la misma línea. En el siguiente ejemplo podemos ver cómo se importa con una sola clausula import los módulos de la distribución por defecto de Python os, que engloba funcionalidad relativa al sistema operativo; sys, con funcionalidad relacionada con el propio intérprete de Python y time, en el que se almacenan funciones para manipular fechas y horas.&lt;br /&gt;import os, sys, time&lt;br /&gt;print time.asctime()&lt;br /&gt;Sin duda os habréis fijado en este y el anterior ejemplo en un detalle importante, y es que, como vemos, es necesario preceder el nombre de los objetos que importamos de un módulo con el nombre del módulo al que pertenecen, o lo que es lo mismo, el espacio de nombres en el que se encuentran. Esto permite que no sobreescribamos accidentalmente algún otro objeto que tuviera el mismo nombre al importar otro módulo.&lt;br /&gt;Sin embargo es posible utilizar la construcción from-import para ahorrarnos el tener que indicar el nombre del módulo antes del objeto que nos interesa. De esta forma se importa el objeto o los objetos que indiquemos al espacio de nombres actual.&lt;br /&gt;from time import asctime&lt;br /&gt;print asctime()&lt;br /&gt;Aunque se considera una mala práctica, también es posible importar todos los nombres del módulo al espacio de nombres actual usando el caracter *:&lt;br /&gt;from time import *&lt;br /&gt;Ahora bien, recordareis que a la hora de crear nuestro primer módulo insistí en que lo guardarais en el mismo directorio en el que se encontraba el programa que lo importaba. Entonces, ¿cómo podemos importar los módulos os, sys o time si no se encuentran los archivos os.py, sys.py y time.py en el mismo directorio?&lt;br /&gt;A la hora de importar un módulo Python recorre todos los directorios indicados en la variable de entorno PYTHONPATH en busca de un archivo con el nombre adecuado. El valor de la variable PYTHONPATH se puede consultar desde Python mediante sys.path&lt;br /&gt;&gt;&gt;&gt; import sys&lt;br /&gt;&gt;&gt;&gt; sys.path&lt;br /&gt;De esta forma para que nuestro módulo estuviera disponible para todos los programas del sistema bastaría con que lo copiáramos a uno de los directorios indicados en PYTHONPATH.&lt;br /&gt;En el caso de que Python no encontrara ningún módulo con el nombre especificado, se lanzaría una excepción de tipo ImportError.&lt;br /&gt;Por último es interesante comentar que en Python los módulos también son objetos; de tipo module en concreto. Por supuesto esto significa que pueden tener atributos y métodos. Uno de sus atributos, __name__, se utiliza a menudo para incluir código ejecutable en un módulo pero que este sólo se ejecute si se llama al módulo como programa, y no al importarlo. Para lograr esto basta saber que cuando se ejecuta el módulo directamente __name__ tiene como valor “__main__”, mientras que cuando se importa, el valor de __name__ es el nombre del módulo:&lt;br /&gt;print “Se muestra siempre”&lt;br /&gt;if __name__ == “__main__”:&lt;br /&gt;print “Se muestra si no es importacion”&lt;br /&gt;Otro atributo interesante es __doc__, que, como en el caso de funciones y clases, sirve a modo de documentación del objeto (docstring o cadena de documentación). Su valor es el de la primera línea del cuerpo del módulo, en el caso de que esta sea una cadena de texto; en caso contrario valdrá None.&lt;br /&gt;Paquetes&lt;br /&gt;Si los módulos sirven para organizar el código, los paquetes sirven para organizar los módulos. Los paquetes son tipos especiales de módulos (ambos son de tipo module) que permiten agrupar módulos relacionados. Mientras los módulos se corresponden a nivel físico con los archivos, los paquetes se representan mediante directorios.&lt;br /&gt;En una aplicación cualquiera podríamos tener, por ejemplo, un paquete iu para la interfaz o un paquete bbdd para la persistencia a base de datos.&lt;br /&gt;Para hacer que Python trate a un directorio como un paquete es necesario crear un archivo __init__.py en dicha carpeta. En este archivo se pueden definir elementos que pertenezcan a dicho paquete, como una constante DRIVER para el paquete bbdd, aunque habitualmente se tratará de un archivo vacío. Para hacer que un cierto módulo se encuentre dentro de un paquete, basta con copiar el archivo que define el módulo al directorio del paquete.&lt;br /&gt;Como los modulos, para importar paquetes también se utiliza import y from-import y el caracter . para separar paquetes, subpaquetes y módulos.&lt;br /&gt;import paq.subpaq.modulo&lt;br /&gt;paq.subpaq.modulo.func()&lt;br /&gt;A lo largo de los próximos capítulos veremos algunos módulos y paquetes de utilidad. Para encontrar algún módulo o paquete que cubra una cierta necesidad, puedes consultar la lista de PyPI (Python Package Index) en http://pypi.python.org/, que cuenta a la hora de escribir&lt;br /&gt;estas líneas, con más de 4000 paquetes distintos.&lt;br /&gt;&lt;br /&gt;Entrada/Salida Y Ficheros&lt;br /&gt;Nuestros programas serían de muy poca utilidad si no fueran capaces de interaccionar con el usuario. En capítulos anteriores vimos, de pasada, el uso de la palabra clave print para mostrar mensajes en pantalla.&lt;br /&gt;En esta lección, además de describir más detalladamente del uso de print para mostrar mensajes al usuario, aprenderemos a utilizar las funciones input y raw_input para pedir información, así como los argumentos de línea de comandos y, por último, la entrada/salida de ficheros.&lt;br /&gt;Entrada estándar&lt;br /&gt;La forma más sencilla de obtener información por parte del usuario es mediante la función raw_input. Esta función toma como parámetro una cadena a usar como prompt (es decir, como texto a mostrar al usuario pidiendo la entrada) y devuelve una cadena con los caracteres introducidos por el usuario hasta que pulsó la tecla Enter. Veamos un pequeño ejemplo:&lt;br /&gt;nombre = raw_input(“Como te llamas? “)&lt;br /&gt;print “Encantado, “ + nombre&lt;br /&gt;Si necesitáramos un entero como entrada en lugar de una cadena, por ejemplo, podríamos utilizar la función int para convertir la cadena a entero, aunque sería conveniente tener en cuenta que puede lanzarse una excepción si lo que introduce el usuario no es un número.&lt;br /&gt;try:&lt;br /&gt;edad = raw_input(“Cuantos anyos tienes? “)&lt;br /&gt;dias = int(edad) * 365&lt;br /&gt;print “Has vivido “ + str(dias) + “ dias”&lt;br /&gt;except ValueError:&lt;br /&gt;print “Eso no es un numero”&lt;br /&gt;La función input es un poco más complicada. Lo que hace esta función es utilizar raw_input para leer una cadena de la entrada estándar, y después pasa a evaluarla como si de código Python se tratara; por lo tanto input debería tratarse con sumo cuidado.&lt;br /&gt;Parámetros de línea de comando&lt;br /&gt;Además del uso de input y raw_input el programador Python cuenta con otros métodos para obtener datos del usuario. Uno de ellos es el uso de parámetros a la hora de llamar al programa en la línea de comandos. Por ejemplo:&lt;br /&gt;python editor.py hola.txt&lt;br /&gt;En este caso hola.txt sería el parámetro, al que se puede acceder a través de la lista sys.argv, aunque, como es de suponer, antes de poder utilizar dicha variable debemos importar el módulo sys. sys.argv[0] contiene siempre el nombre del programa tal como lo ha ejecutado el usuario, sys.argv[1], si existe, sería el primer parámetro; sys.argv[2] el segundo, y así sucesivamente.&lt;br /&gt;import sys&lt;br /&gt;if(len(sys.argv) &gt; 1):&lt;br /&gt;print “Abriendo “ + sys.argv[1]&lt;br /&gt;else:&lt;br /&gt;print “Debes indicar el nombre del archivo”&lt;br /&gt;Existen módulos, como optparse, que facilitan el trabajo con los argumentos de la línea de comandos, pero explicar su uso queda fuera del objetivo de este capítulo.&lt;br /&gt;Salida estándar&lt;br /&gt;La forma más sencilla de mostrar algo en la salida estándar es mediante el uso de la sentencia print, como hemos visto multitud de veces en&lt;br /&gt;Entrada/Salida. Ficheros&lt;br /&gt;ejemplos anteriores. En su forma más básica a la palabra clave print le sigue una cadena, que se mostrará en la salida estándar al ejecutarse el estamento.&lt;br /&gt;&gt;&gt;&gt; print “Hola mundo”&lt;br /&gt;Hola mundo&lt;br /&gt;Después de imprimir la cadena pasada como parámetro el puntero se sitúa en la siguiente línea de la pantalla, por lo que el print de Python funciona igual que el println de Java.&lt;br /&gt;En algunas funciones equivalentes de otros lenguajes de programación es necesario añadir un carácter de nueva línea para indicar explícitamente que queremos pasar a la siguiente línea. Este es el caso de la función printf de C o la propia función print de Java.&lt;br /&gt;Ya explicamos el uso de estos caracteres especiales durante la explicación del tipo cadena en el capítulo sobre los tipos básicos de Python. La siguiente sentencia, por ejemplo, imprimiría la palabra “Hola”, seguida de un renglón vacío (dos caracteres de nueva línea, ‘\n’), y a continuación la palabra “mundo” indentada (un carácter tabulador, ‘\t’).&lt;br /&gt;print “Hola\n\n\tmundo”&lt;br /&gt;Para que la siguiente impresión se realizara en la misma línea tendríamos que colocar una coma al final de la sentencia. Comparemos el resultado de este código:&lt;br /&gt;&gt;&gt;&gt; for i in range(3):&lt;br /&gt;&gt;&gt;&gt; ...print i,&lt;br /&gt;0 1 2&lt;br /&gt;Con el de este otro, en el que no utiliza una coma al final de la sentencia:&lt;br /&gt;&gt;&gt;&gt; for i in range(3):&lt;br /&gt;&gt;&gt;&gt; ...print i&lt;br /&gt;0&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;Este mecanismo de colocar una coma al final de la sentencia funciona debido a que es el símbolo que se utiliza para separar cadenas que queramos imprimir en la misma línea.&lt;br /&gt;&gt;&gt;&gt; print “Hola”, “mundo”&lt;br /&gt;Hola mundo&lt;br /&gt;Esto se diferencia del uso del operador + para concatenar las cadenas en que al utilizar las comas print introduce automáticamente un espacio para separar cada una de las cadenas. Este no es el caso al utilizar el operador +, ya que lo que le llega a print es un solo argumento: una cadena ya concatenada.&lt;br /&gt;&gt;&gt;&gt; print “Hola” + “mundo”&lt;br /&gt;Holamundo&lt;br /&gt;Además, al utilizar el operador + tendríamos que convertir antes cada argumento en una cadena de no serlo ya, ya que no es posible concatenar cadenas y otros tipos, mientras que al usar el primer método no es necesaria la conversión.&lt;br /&gt;&gt;&gt;&gt; print “Cuesta”, 3, “euros”&lt;br /&gt;Cuesta 3 euros&lt;br /&gt;&gt;&gt;&gt; print “Cuesta” + 3 + “euros”&lt;br /&gt;&lt;type&gt;: cannot concatenate ‘str’ and ‘int’ objects&lt;br /&gt;La sentencia print, o más bien las cadenas que imprime, permiten también utilizar técnicas avanzadas de formateo, de forma similar al sprintf de C. Veamos un ejemplo bastante simple:&lt;br /&gt;print “Hola %s” % “mundo”&lt;br /&gt;print “%s %s” % (“Hola”, “mundo”)&lt;br /&gt;Lo que hace la primera línea es introducir los valores a la derecha del símbolo % (la cadena “mundo”) en las posiciones indicadas por los especificadores de conversión de la cadena a la izquierda del símbolo %, tras convertirlos al tipo adecuado.&lt;br /&gt;En la segunda línea, vemos cómo se puede pasar más de un valor a sustituir, por medio de una tupla.&lt;br /&gt;&lt;br /&gt;Entrada/Salida. Ficheros&lt;br /&gt;En este ejemplo sólo tenemos un especificador de conversión: %s.&lt;br /&gt;Los especificadores más sencillos están formados por el símbolo % seguido de una letra que indica el tipo con el que formatear el valor Por ejemplo:&lt;br /&gt;Especificador&lt;br /&gt;Formato&lt;br /&gt;%s&lt;br /&gt;Cadena&lt;br /&gt;%d&lt;br /&gt;Entero&lt;br /&gt;%o&lt;br /&gt;Octal&lt;br /&gt;%x&lt;br /&gt;Hexadecimal&lt;br /&gt;%f&lt;br /&gt;Real&lt;br /&gt;Se puede introducir un número entre el % y el carácter que indica el tipo al que formatear, indicando el número mínimo de caracteres que queremos que ocupe la cadena. Si el tamaño de la cadena resultante es menor que este número, se añadirán espacios a la izquierda de la cadena. En el caso de que el número sea negativo, ocurrirá exactamente lo mismo, sólo que los espacios se añadirán a la derecha de la cadena.&lt;br /&gt;&gt;&gt;&gt; print “%10s mundo” % “Hola”&lt;br /&gt;______Hola mundo&lt;br /&gt;&gt;&gt;&gt; print “%-10s mundo” % “Hola”&lt;br /&gt;Hola_______mundo&lt;br /&gt;En el caso de los reales es posible indicar la precisión a utilizar precediendo la f de un punto seguido del número de decimales que queremos mostrar:&lt;br /&gt;&gt;&gt;&gt; from math import pi&lt;br /&gt;&gt;&gt;&gt; print “%.4f” % pi&lt;br /&gt;3.1416&lt;br /&gt;La misma sintaxis se puede utilizar para indicar el número de caracteres de la cadena que queremos mostrar&lt;br /&gt;&gt;&gt;&gt; print “%.4s” % “hola mundo”&lt;br /&gt;hola&lt;br /&gt;&lt;br /&gt;Archivos&lt;br /&gt;Los ficheros en Python son objetos de tipo file creados mediante la función open (abrir). Esta función toma como parámetros una cadena con la ruta al fichero a abrir, que puede ser relativa o absoluta; una cadena opcional indicando el modo de acceso (si no se especifica se accede en modo lectura) y, por último, un entero opcional para especificar un tamaño de buffer distinto del utilizado por defecto.&lt;br /&gt;El modo de acceso puede ser cualquier combinación lógica de los siguientes modos:&lt;br /&gt;‘r’• : read, lectura. Abre el archivo en modo lectura. El archivo tiene que existir previamente, en caso contrario se lanzará una excepción de tipo IOError.&lt;br /&gt;‘w’• : write, escritura. Abre el archivo en modo escritura. Si el archivo no existe se crea. Si existe, sobreescribe el contenido.&lt;br /&gt;‘a’• : append, añadir. Abre el archivo en modo escritura. Se diferencia del modo ‘w’ en que en este caso no se sobreescribe el contenido del archivo, sino que se comienza a escribir al final del archivo.&lt;br /&gt;‘b’• : binary, binario.&lt;br /&gt;‘+’• : permite lectura y escritura simultáneas.&lt;br /&gt;‘U’• : universal newline, saltos de línea universales. Permite trabajar con archivos que tengan un formato para los saltos de línea que no coincide con el de la plataforma actual (en Windows se utiliza el caracter CR LF, en Unix LF y en Mac OS CR).&lt;br /&gt;f = open(“archivo.txt”, “w”)&lt;br /&gt;Tras crear el objeto que representa nuestro archivo mediante la función open podremos realizar las operaciones de lectura/escritura pertinentes utilizando los métodos del objeto que veremos en las siguientes secciones.&lt;br /&gt;Una vez terminemos de trabajar con el archivo debemos cerrarlo utilizando el método close.&lt;br /&gt;&lt;br /&gt;Entrada/Salida. Ficheros&lt;br /&gt;Para la lectura de archivos se utilizan los métodos read, readline y realines.&lt;br /&gt;El método read devuelve una cadena con el contenido del archivo o bien el contenido de los primeros n bytes, si se especifica el tamaño máximo a leer.&lt;br /&gt;completo = f.read()&lt;br /&gt;parte = f2.read(512)&lt;br /&gt;El método readline sirve para leer las líneas del fichero una por una. Es decir, cada vez que se llama a este método, se devuelve el contenido del archivo desde el puntero hasta que se encuentra un carácter de nueva línea, incluyendo este carácter.&lt;br /&gt;while True:&lt;br /&gt;linea = f.readline()&lt;br /&gt;if not linea: break&lt;br /&gt;print linea&lt;br /&gt;Por último, readlines, funciona leyendo todas las líneas del archivo y devolviendo una lista con las líneas leídas.&lt;br /&gt;Escritura de archivos&lt;br /&gt;Para la escritura de archivos se utilizan los método write y writelines. Mientras el primero funciona escribiendo en el archivo una cadena de texto que toma como parámetro, el segundo toma como parámetro una lista de cadenas de texto indicando las líneas que queremos escribir en el fichero.&lt;br /&gt;Mover el puntero de lectura/escritura&lt;br /&gt;Hay situaciones en las que nos puede interesar mover el puntero de lectura/escritura a una posición determinada del archivo. Por ejemplo si queremos empezar a escribir en una posición determinada y no al final o al principio del archivo.&lt;br /&gt;Para esto se utiliza el método seek que toma como parámetro un núPython&lt;br /&gt;para todos&lt;br /&gt;mero positivo o negativo a utilizar como desplazamiento. También es posible utilizar un segundo parámetro para indicar desde dónde queremos que se haga el desplazamiento: 0 indicará que el desplazamiento se refiere al principio del fichero (comportamiento por defecto), 1 se refiere a la posición actual, y 2, al final del fichero.&lt;br /&gt;Para determinar la posición en la que se encuentra actualmente el puntero se utiliza el método tell(), que devuelve un entero indicando la distancia en bytes desde el principio del fichero.&lt;br /&gt;&lt;br /&gt;Expresiones Regulares&lt;br /&gt;&lt;br /&gt;Las expresiones regulares, también llamadas regex o regexp, consisten en patrones que describen conjuntos de cadenas de caracteres.&lt;br /&gt;Algo parecido sería escribir en la línea de comandos de Windows&lt;br /&gt;dir *.exe&lt;br /&gt;‘*.exe’ sería una “expresión regular” que describiría todas las cadenas de caracteres que empiezan con cualquier cosa seguida de ‘.exe’, es decir, todos los archivos exe.&lt;br /&gt;El trabajo con expresiones regulares en Python se realiza mediante el módulo re, que data de Python 1.5 y que proporciona una sintaxis para la creación de patrones similar a la de Perl. En Python 1.6 el módulo se reescribió para dotarlo de soporte de cadenas unicode y mejorar su rendimiento.&lt;br /&gt;El módulo re contiene funciones para buscar patrones dentro de una cadena (search), comprobar si una cadena se ajusta a un determinado criterio descrito mediante un patrón (match), dividir la cadena usando las ocurrencias del patrón como puntos de ruptura (split) o para sustituir todas las ocurrencias del patrón por otra cadena (sub). Veremos estas funciones y alguna más en la próxima sección, pero por ahora, aprendamos algo más sobre la sintaxis de las expresiones regulares.&lt;br /&gt;Patrones&lt;br /&gt;La expresión regular más sencilla consiste en una cadena simple, que&lt;br /&gt;describe un conjunto compuesto tan solo por esa misma cadena. Por ejemplo, veamos cómo la cadena “python” coincide con la expresión regular “python” usando la función match:&lt;br /&gt;import re&lt;br /&gt;if re.match(“python”, “python”):&lt;br /&gt;print “cierto”&lt;br /&gt;Si quisiéramos comprobar si la cadena es “python”, “jython”, “cython” o cualquier otra cosa que termine en “ython”, podríamos utilizar el carácter comodín, el punto ‘.’:&lt;br /&gt;re.match(“.ython”, “python”)&lt;br /&gt;re.match(“.ython”, “jython”)&lt;br /&gt;La expresión regular “.ython” describiría a todas las cadenas que consistan en un carácter cualquiera, menos el de nueva línea, seguido de “ython”. Un carácter cualquiera y solo uno. No cero, ni dos, ni tres.&lt;br /&gt;En el caso de que necesitáramos el carácter ‘.’ en la expresión regular, o cualquier otro de los caracteres especiales que veremos a continuación, tendríamos que escaparlo utilizando la barra invertida.&lt;br /&gt;Para comprobar si la cadena consiste en 3 caracteres seguidos de un punto, por ejemplo, podríamos utilizar lo siguiente:&lt;br /&gt;re.match(“...\.”, “abc.”)&lt;br /&gt;Si necesitáramos una expresión que sólo resultara cierta para las cadenas “python”, “jython” y “cython” y ninguna otra, podríamos utilizar el carácter ‘|’ para expresar alternativa escribiendo los tres subpatrones completos:&lt;br /&gt;re.match(“python|jython|cython”, “python”)&lt;br /&gt;o bien tan solo la parte que pueda cambiar, encerrada entre paréntesis, formando lo que se conoce como un grupo. Los grupos tienen una gran importancia a la hora de trabajar con expresiones regulares y este no es su único uso, como veremos en la siguiente sección.&lt;br /&gt;re.match(“(p|j|c)ython”, “python”)&lt;br /&gt;Otra opción consistiría en encerrar los caracteres ‘p’, ‘j’ y ‘c’ entre corchetes para formar una clase de caracteres, indicando que en esa posición puede colocarse cualquiera de los caracteres de la clase.&lt;br /&gt;re.match(“[pjc]ython”, “python”)&lt;br /&gt;¿Y si quisiéramos comprobar si la cadena es “python0”, “python1”, “python2”, ... , “python9”? En lugar de tener que encerrar los 10 dígitos dentro de los corchetes podemos utilizar el guión, que sirve para indicar rangos. Por ejemplo a-d indicaría todas las letras minúsculas de la ‘a’ a la ‘d’; 0-9 serían todos los números de 0 a 9 inclusive.&lt;br /&gt;re.match(“python[0-9]”, “python0”)&lt;br /&gt;Si quisiéramos, por ejemplo, que el último carácter fuera o un dígito o una letra simplemente se escribirían dentro de los corchetes todos los criterios, uno detras de otro.&lt;br /&gt;re.match(“python[0-9a-zA-Z]”, “pythonp”)&lt;br /&gt;Es necesario advertir que dentro de las clases de caracteres los caracteres especiales no necesitan ser escapados. Para comprobar si la cadena es “python.” o “python,”, entonces, escribiríamos:&lt;br /&gt;re.match(“python[.,]”, “python.”)&lt;br /&gt;y no&lt;br /&gt;re.match(“python[\.,]”, “python.”)&lt;br /&gt;ya que en este último caso estaríamos comprobando si la cadena es “python.”, “python,” o “python\”.&lt;br /&gt;Los conjuntos de caracteres también se pueden negar utilizando el símbolo ‘^’. La expresión “python[^0-9a-z]”, por ejemplo, indicaría que nos interesan las cadenas que comiencen por “python” y tengan como último carácter algo que no sea ni una letra minúscula ni un número.&lt;br /&gt;re.match(“python[^0-9a-z]”, “python+”)&lt;br /&gt;El uso de [0-9] para referirse a un dígito no es muy común, ya que, al ser la comprobación de que un carácter es un dígito algo muy utilizado, existe una secuencia especial equivalente: ‘\d’. Existen otras secuencias disponibles que listamos a continuación:&lt;br /&gt;“\d”• : un dígito. Equivale a [0-9]&lt;br /&gt;“\D”• : cualquier carácter que no sea un dígito. Equivale a [^0-9]&lt;br /&gt;“\w”• : cualquier caracter alfanumérico. Equivale a [a-zA-Z0-9_]&lt;br /&gt;“\W”• : cualquier carácter no alfanumérico. Equivale a [^a-zA-Z0-9_]&lt;br /&gt;“\s”• : cualquier carácter en blanco. Equivale a [ \t\n\r\f\v]&lt;br /&gt;“\S”• : cualquier carácter que no sea un espacio en blanco. Equivale a [^ \t\n\r\f\v]&lt;br /&gt;Veamos ahora cómo representar repeticiones de caracteres, dado que no sería de mucha utilidad tener que, por ejemplo, escribir una expresión regular con 30 caracteres ‘\d’ para buscar números de 30 dígitos. Para este menester tenemos los caracteres especiales +, * y ?, además de las llaves {}.&lt;br /&gt;El carácter + indica que lo que tenemos a la izquierda, sea un carácter como ‘a’, una clase como ‘[abc]’ o un subpatrón como (abc), puede encontrarse una o mas veces. Por ejemplo la expresión regular “python+” describiría las cadenas “python”, “pythonn” y “pythonnn”, pero no “pytho”, ya que debe haber al menos una n.&lt;br /&gt;El carácter * es similar a +, pero en este caso lo que se sitúa a su izquierda puede encontrarse cero o mas veces.&lt;br /&gt;El carácter ? indica opcionalidad, es decir, lo que tenemos a la izquierda puede o no aparecer (puede aparecer 0 o 1 veces).&lt;br /&gt;Finalmente las llaves sirven para indicar el número de veces exacto que puede aparecer el carácter de la izquierda, o bien un rango de veces que puede aparecer. Por ejemplo {3} indicaría que tiene que aparecer exactamente 3 veces, {3,8} indicaría que tiene que aparecer de 3 a 8 veces,&lt;br /&gt;{,8} de 0 a 8 veces y {3,} tres veces o mas (las que sean).&lt;br /&gt;Otro elemento interesante en las expresiones regulares, para terminar, es la especificación de las posiciones en que se tiene que encontrar la cadena, esa es la utilidad de ^ y $, que indican, respectivamente, que el elemento sobre el que actúan debe ir al principio de la cadena o al final de esta.&lt;br /&gt;La cadena “http://mundogeek.net”, por ejemplo, se ajustaría a la expresión regular “^http”, mientras que la cadena “El protocolo es http” no lo haría, ya que el http no se encuentra al principio de la cadena.&lt;br /&gt;Usando el módulo re&lt;br /&gt;Ya hemos visto por encima cómo se utiliza la función match del módulo re para comprobar si una cadena se ajusta a un determinado patrón. El primer parámetro de la función es la expresión regular, el segundo, la cadena a comprobar y existe un tercer parámetro opcional que contiene distintos flags que se pueden utilizar para modificar el comportamiento de las expresiones regulares.&lt;br /&gt;Algunos ejemplos de flags del módulo re son re.IGNORECASE, que hace que no se tenga en cuenta si las letras son mayúsculas o minúsculas o re.VERBOSE, que hace que se ignoren los espacios y los comentarios en la cadena que representa la expresión regular.&lt;br /&gt;El valor de retorno de la función será None en caso de que la cadena no se ajuste al patrón o un objeto de tipo MatchObject en caso contrario. Este objeto MatchObject cuenta con métodos start y end que devuelven la posición en la que comienza y finaliza la subcadena reconocida y métodos group y groups que permiten acceder a los grupos que propiciaron el reconocimiento de la cadena.&lt;br /&gt;Al llamar al método group sin parámetros se nos devuelve el grupo 0 de la cadena reconocida. El grupo 0 es la subcadena reconocida por la expresión regular al completo, aunque no existan paréntesis que delimiten el grupo.&lt;br /&gt;&gt;&gt;&gt; mo = re.match(“http://.+\net”, “http://mundogeek.net”)&lt;br /&gt;&gt;&gt;&gt; print mo.group()&lt;br /&gt;http://mundogeek.net&lt;br /&gt;Podríamos crear grupos utilizando los paréntesis, como aprendimos en la sección anterior, obteniendo así la parte de la cadena que nos interese.&lt;br /&gt;&gt;&gt;&gt; mo = re.match(“http://(.+)\net”, “http://mundogeek.net”)&lt;br /&gt;&gt;&gt;&gt; print mo.group(0)&lt;br /&gt;http://mundogeek.net&lt;br /&gt;&gt;&gt;&gt; print mo.group(1)&lt;br /&gt;mundogeek&lt;br /&gt;El método groups, por su parte, devuelve una lista con todos los grupos, exceptuando el grupo 0, que se omite.&lt;br /&gt;&gt;&gt;&gt; mo = re.match(“http://(.+)\(.{3})”, “http://mundogeek.net”)&lt;br /&gt;&gt;&gt;&gt; print mo.groups()&lt;br /&gt;(‘mundogeek’, ‘net’)&lt;br /&gt;La función search del módulo re funciona de forma similar a match; contamos con los mismos parámetros y el mismo valor de retorno. La única diferencia es que al utilizar match la cadena debe ajustarse al patrón desde el primer carácter de la cadena, mientras que con search buscamos cualquier parte de la cadena que se ajuste al patrón. Por esta razón el método start de un objeto MatchObject obtenido mediante la función match siempre devolverá 0, mientras que en el caso de search esto no tiene por qué ser así.&lt;br /&gt;Otra función de búsqueda del módulo re es findall. Este toma los mismos parámetros que las dos funciones anteriores, pero devuelve una lista con las subcadenas que cumplieron el patrón.&lt;br /&gt;Otra posibilidad, si no queremos todas las coincidencias, es utilizar finditer, que devuelve un iterador con el que consultar uno a uno los distintos MatchObject.&lt;br /&gt;Las expresiones regulares no solo permiten realizar búsquedas o comprobaciones, sino que, como comentamos anteriormente, también&lt;br /&gt;tenemos funciones disponibles para dividir la cadena o realizar reemplazos.&lt;br /&gt;La función split sin ir más lejos toma como parámetros un patrón, una cadena y un entero opcional indicando el número máximo de elementos en los que queremos dividir la cadena, y utiliza el patrón a modo de puntos de separación para la cadena, devolviendo una lista con las subcadenas.&lt;br /&gt;La función sub toma como parámetros un patrón a sustituir, una cadena que usar como reemplazo cada vez que encontremos el patrón, la cadena sobre la que realizar las sustituciones, y un entero opcional indicando el número máximo de sustituciones que queremos realizar.&lt;br /&gt;Al llamar a estos métodos lo que ocurre en realidad es que se crea un nuevo objeto de tipo RegexObject que representa la expresión regular, y se llama a métodos de este objeto que tienen los mismos nombres que las funciones del módulo.&lt;br /&gt;Si vamos a utilizar un mismo patrón varias veces nos puede interesar crear un objeto de este tipo y llamar a sus métodos nosotros mismos; de esta forma evitamos que el intérprete tenga que crear un nuevo objeto cada vez que usemos el patrón y mejoraremos el rendimiento de la aplicación.&lt;br /&gt;Para crear un objeto RegexObject se utiliza la función compile del módulo, al que se le pasa como parámetro la cadena que representa el patrón que queremos utilizar para nuestra expresión regular y, opcionalmente, una serie de flags de entre los que comentamos anteriormente.&lt;br /&gt;&lt;br /&gt;Sockets&lt;br /&gt;La comunicación entre distintas entidades en una red se basa en Python en el clásico concepto de sockets. Los sockets son un concepto abstracto con el que se designa al punto final de una conexión.&lt;br /&gt;Los programas utilizan sockets para comunicarse con otros programas, que pueden estar situados en computadoras distintas.&lt;br /&gt;Un socket queda definido por la dirección IP de la máquina, el puerto en el que escucha, y el protocolo que utiliza.&lt;br /&gt;Los tipos y funciones necesarios para trabajar con sockets se encuentran en Python en el módulo socket, como no podría ser de otra forma.&lt;br /&gt;Los sockets se clasifican en sockets de flujo (socket.SOCK_STREAM) o sockets de datagramas (socket.SOCK_DGRAM) dependiendo de si el servicio utiliza TCP, que es orientado a conexión y fiable, o UDP, respectivamente. En este capítulo sólo cubriremos los sockets de flujo, que cubren un 90% de las necesidades comunes.&lt;br /&gt;Los sockets también se pueden clasificar según la familia. Tenemos sockets UNIX (socket.AF_UNIX) que se crearon antes de la concepción de las redes y se basan en ficheros, sockets socket.AF_INET que son los que nos interesan, sockets socket.AF_INET6 para IPv6, etc.&lt;br /&gt;Para crear un socket se utiliza el constructor socket.socket() que puede tomar como parámetros opcionales la familia, el tipo y el protocolo. Por defecto se utiliza la familia AF_INET y el tipo SOCK_STREAM.&lt;br /&gt;Veremos durante el resto del capítulo cómo crear un par de programas cliente y servidor a modo de ejemplo.&lt;br /&gt;Lo primero que tenemos que hacer es crear un objeto socket para el servidor&lt;br /&gt;socket_s = socket.socket()&lt;br /&gt;Tenemos ahora que indicar en qué puerto se va a mantener a la escucha nuestro servidor utilizando el método bind. Para sockets IP, como es nuestro caso, el argumento de bind es una tupla que contiene el host y el puerto. El host se puede dejar vacío, indicando al método que puede utilizar cualquier nombre que esté disponible.&lt;br /&gt;socket_s.bind((“localhost”, 9999))&lt;br /&gt;Por último utilizamos listen para hacer que el socket acepte conexiones entrantes y accept para comenzar a escuchar. El método listen requiere de un parámetro que indica el número de conexiones máximas que queremos aceptar; evidentemente, este valor debe ser al menos 1.&lt;br /&gt;El método accept se mantiene a la espera de conexiones entrantes, bloqueando la ejecución hasta que llega un mensaje.&lt;br /&gt;Cuando llega un mensaje, accept desbloquea la ejecución, devolviendo un objeto socket que representa la conexión del cliente y una tupla que contiene el host y puerto de dicha conexión.&lt;br /&gt;socket_s.listen(10)&lt;br /&gt;socket_c, (host_c, puerto_c) = socket_s.accept()&lt;br /&gt;Una vez que tenemos este objeto socket podemos comunicarnos con el cliente a través suyo, mediante los métodos recv y send (o recvfrom y sendfrom en UDP) que permiten recibir o enviar mensajes respectivamente. El método send toma como parámetros los datos a enviar, mientras que el método recv toma como parámetro el número máximo de bytes a aceptar.&lt;br /&gt;recibido = socket_c.recv(1024)&lt;br /&gt;print “Recibido: “, recibio&lt;br /&gt;socket_c.send(recibido)&lt;br /&gt;Una vez que hemos terminado de trabajar con el socket, lo cerramos con el método close.&lt;br /&gt;Crear un cliente es aún más sencillo. Solo tenemos que crear el objeto socket, utilizar el método connect para conectarnos al servidor y utilizar los métodos send y recv que vimos anteriormente. El argumento de connect es una tupla con host y puerto, exactamente igual que bind.&lt;br /&gt;socket_c = socket.socket()&lt;br /&gt;socket_c.connect((“localhost”, 9999))&lt;br /&gt;socket_c.send(“hola”)&lt;br /&gt;Veamos por último un ejemplo completo. En este ejemplo el cliente manda al servidor cualquier mensaje que escriba el usuario y el servidor no hace más que repetir el mensaje recibido. La ejecución termina cuando el usuario escribe quit.&lt;br /&gt;Este sería el código del script servidor:&lt;br /&gt;import socket&lt;br /&gt;s = socket.socket()&lt;br /&gt;s.bind((“localhost”, 9999))&lt;br /&gt;s.listen(1)&lt;br /&gt;sc, addr = s.accept()&lt;br /&gt;while True:&lt;br /&gt;recibido = sc.recv(1024)&lt;br /&gt;if recibido == “quit”:&lt;br /&gt;break&lt;br /&gt;print “Recibido:”, recibido&lt;br /&gt;sc.send(recibido)&lt;br /&gt;print “adios”&lt;br /&gt;sc.close()&lt;br /&gt;s.close()&lt;br /&gt;Y a continuación tenemos el del script cliente:&lt;br /&gt;import socket&lt;br /&gt;s = socket.socket()&lt;br /&gt;s.connect((“localhost”, 9999))&lt;br /&gt;while True:&lt;br /&gt;mensaje = raw_input(“&gt; “)&lt;br /&gt;s.send(mensaje)&lt;br /&gt;mensaje == “quit”:&lt;br /&gt;break&lt;br /&gt;print “adios”&lt;br /&gt;s.close()&lt;br /&gt;&lt;br /&gt;Interactuar con webs&lt;br /&gt;Existen dos módulos principales para leer datos de URLs en Python: urllib y urllib2. En esta lección aprenderemos a utilizar urllib2 ya que es mucho más completo, aunque urllib tiene funcionalidades propias que no se pueden encontrar en urllib2, por lo que también lo tocaremos de pasada.&lt;br /&gt;urllib2 puede leer datos de una URL usando varios protocolos como HTTP, HTTPS, FTP, o Gopher.&lt;br /&gt;Se utiliza una función urlopen para crear un objeto parecido a un fichero con el que leer de la URL. Este objeto cuenta con métodos como read, readline, readlines y close, los cuales funcionan exactamente igual que en los objetos file, aunque en realidad estamos trabajando con un wrapper que nos abstrae de un socket que se utiliza por debajo.&lt;br /&gt;El método read, como recordareis, sirve para leer el “archivo” completo o el número de bytes especificado como parámetro, readline para leer una línea, y readlines para leer todas las líneas y devolver una lista con ellas.&lt;br /&gt;También contamos con un par de métodos geturl, para obtener la URL de la que estamos leyendo (que puede ser útil para comprobar si ha habido una redirección) e info que nos devuelve un objeto con las cabeceras de respuesta del servidor (a las que también se puede acceder mediante el atributo headers).&lt;br /&gt;import urllib2&lt;br /&gt;try:&lt;br /&gt;f = urllib2.urlopen(“http://www.python.org”)&lt;br /&gt;print f.read()&lt;br /&gt;f.close()&lt;br /&gt;except HTTPError, e:&lt;br /&gt;print “Ocurrió un error”&lt;br /&gt;print e.code&lt;br /&gt;except URLError, e:&lt;br /&gt;print “Ocurrió un error”&lt;br /&gt;print e.reason&lt;br /&gt;Al trabajar con urllib2 nos podemos encontrar, como vemos, con errores de tipo URLError. Si trabajamos con HTTP podemos encontrarnos también con errores de la subclase de URLError HTTPError, que se lanzan cuando el servidor devuelve un código de error HTTP, como el error 404 cuando no se encuentra el recurso. También podríamos encontrarnos con errores lanzados por la librería que urllib2 utiliza por debajo para las transferencias HTTP: httplib; o con excepciones lanzadas por el propio módulo socket.&lt;br /&gt;La función urlopen cuenta con un parámetro opcional data con el que poder enviar información a direcciones HTTP (y solo HTTP) usando POST (los parámetros se envían en la propia petición), por ejemplo para responder a un formulario. Este parámetro es una cadena codificada adecuadamente, siguiendo el formato utilizado en las URLs:&lt;br /&gt;‘password=contrase%A4a&amp;amp;usuario=manuel’&lt;br /&gt;Lo más sencillo para codificar la cadena es utilizar el método urlencode de urllib, que acepta un diccionario o una lista de tuplas (clave, valor) y genera la cadena codificada correspondiente:&lt;br /&gt;import urllib, urllib2&lt;br /&gt;params = urllib.urlencode({“usuario”: “manuel”,&lt;br /&gt;“password”: “contraseña”})&lt;br /&gt;f = urllib2.urlopen(“http://ejemplo.com/login”, params)&lt;br /&gt;Si lo único que queremos hacer es descargar el contenido de una URL a un archivo local, podemos utilizar la función urlretrieve de urllib en lugar de leer de un objeto creado con urlopen y escribir los datos&lt;br /&gt;leídos.&lt;br /&gt;La función urlretrieve toma como parámetros la URL a descargar y, opcionalmente, un parámetro filename con la ruta local en la que guardar el archivo, un parámetro data similar al de urlopen y un parámetro reporthook con una función que utilizar para informar del progreso.&lt;br /&gt;A excepción de las ocasiones en las que se utiliza el parámetro data las conexiones siempre se realizan utilizando GET (los parámetros se envían en la URL). Para enviar datos usando GET basta con concatenar la cadena resultante de urlencode con la URL a la que nos vamos a conectar mediante el símbolo ?.&lt;br /&gt;params = urllib.urlencode({“usuario”: “manuel”,&lt;br /&gt;“password”: “contraseña”})&lt;br /&gt;f = urllib2.urlopen(“http://ejemplo.com/login” +&lt;br /&gt;“?” + params)&lt;br /&gt;En urllib también se utiliza una función urlopen para crear nuestros pseudo-archivos, pero a diferencia de la versión de urllib, la función urlopen de urllib2 también puede tomar como parámetro un objeto Request, en lugar de la URL y los datos a enviar.&lt;br /&gt;La clase Request define objetos que encapsulan toda la información relativa a una petición. A través de este objeto podemos realizar peticiones más complejas, añadiendo nuestras propias cabeceras, como el User-Agent.&lt;br /&gt;El constructor más sencillo para el objeto Request no toma más que una cadena indicando la URL a la que conectarse, por lo que utilizar este objeto como parámetro de urlopen sería equivalente a utilizar una cadena con la URL directamente.&lt;br /&gt;Sin embargo el constructor de Request también tiene como parámetros opcionales una cadena data para mandar datos por POST y un diccionario headers con las cabeceras (además de un par de campos origin_req_host y unverifiable, que quedan fuera del propósito del capítulo por ser de raro uso).&lt;br /&gt;Veamos cómo añadir nuestras propias cabeceras utilizando como ejemplo la cabecera User-Agent. El User-Agent es una cabecera que sirve para identificar el navegador y sistema operativo que estamos utilizando para conectarnos a esa URL. Por defecto urllib2 se identifica como “Python-urllib/2.5”; si quisiéramos identificarnos como un Linux corriendo Konqueror por ejemplo, usaríamos un código similar al siguiente:&lt;br /&gt;ua = “Mozilla/5.0 (compatible; Konqueror/3.5.8; Linux)”&lt;br /&gt;h = {“User-Agent”: ua}&lt;br /&gt;r = urllib2.Request(“http://www.python.org”, headers=h)&lt;br /&gt;f = urllib2.urlopen(r)&lt;br /&gt;print f.read()&lt;br /&gt;Para personalizar la forma en que trabaja urllib2 podemos instalar un grupo de manejadores (handlers) agrupados en un objeto de la clase OpenerDirector (opener o abridor), que será el que se utilice a partir de ese momento al llamar a urlopen.&lt;br /&gt;Para construir un opener se utiliza la función build_opener a la que se le pasa los manejadores que formarán parte del opener. El opener se encargará de encadenar la ejecución de los distintos manejadores en el orden dado. También se puede usar el constructor de OpenerDirector, y añadir los manejadores usando su método add_handler.&lt;br /&gt;Para instalar el opener una vez creado se utiliza la función install_opener, que toma como parámetro el opener a instalar. También se podría, si sólo queremos abrir la URL con ese opener una sola vez, utilizar el método open del opener.&lt;br /&gt;urllib2 cuenta con handlers que se encargan de manejar los esquemas disponibles (HTTP, HTTPS, FTP), manejar la autenticación, manejar las redirecciones, etc.&lt;br /&gt;Para añadir autenticación tendríamos que instalar un opener que incluyera como manejador HTTPBasicAuthHandler, ProxyBasicAuthHandler, HTTPDigestAuthHandler y/o ProxyDigestAuthHandler.&lt;br /&gt;Para utilizar autenticación HTTP básica, por ejemplo, usaríamos&lt;br /&gt;HTTPBasicAuthHandler:&lt;br /&gt;aut_h = urllib2.HTTPBasicAuthHandler()&lt;br /&gt;aut_h.add_password(“realm”, “host”, “usuario”, “password”)&lt;br /&gt;opener = urllib2.build_opener(aut_h)&lt;br /&gt;urllib2.install_opener(opener)&lt;br /&gt;f = urllib2.urlopen(“http://www.python.org”)&lt;br /&gt;Si quisiéramos especificar un proxy en el código tendríamos que utilizar un opener que contuviera el manejador ProxyHandler. El manejador por defecto incluye una instacia de ProxyHandler construido llamando al inicializador sin parámetros, con lo que se lee la lista de proxies a utilizar de la variable de entorno adecuada. Sin embargo también podemos construir un ProxyHandler pasando como parámetro al inicializador un diccionario cuyas claves son los protocolos y los valores, la URL del proxy a utilizar para dicho protocolo.&lt;br /&gt;proxy_h = urllib2.ProxyHandler({“http” : “http://miproxy.net:123”})&lt;br /&gt;opener = urllib2.build_opener(proxy_h)&lt;br /&gt;urllib2.install_opener(opener)&lt;br /&gt;f = urllib2.urlopen(“http://www.python.org”)&lt;br /&gt;Para que se guarden las cookies que manda HTTP utilizamos el manejador HTTPCookieProcessor.&lt;br /&gt;cookie_h = urllib2.HTTPCookieProcessor()&lt;br /&gt;opener = urllib2.build_opener(cookie_h)&lt;br /&gt;urllib2.install_opener(opener)&lt;br /&gt;f = urllib2.urlopen(“http://www.python.org”)&lt;br /&gt;Si queremos acceder a estas cookies o poder mandar nuestras propias cookies, podemos pasarle como parámetro al inicializador de HTTPCookieProcessor un objeto de tipo CookieJar del módulo cookielib.&lt;br /&gt;Para leer las cookies mandadas basta crear un objeto iterable a partir del CookieJar (también podríamos buscar las cabeceras correspondientes, pero este sistema es más claro y sencillo):&lt;br /&gt;import urllib2, cookielib&lt;br /&gt;cookie_j = cookielib.CookieJar()&lt;br /&gt;cookie_h = urllib2.HTTPCookieProcessor(cookie_j)&lt;br /&gt;opener = urllib2.build_opener(cookie_h)&lt;br /&gt;opener.open(“http://www.python.org”)&lt;br /&gt;for num, cookie in enumerate(cookie_j):&lt;br /&gt;print num, cookie.name&lt;br /&gt;print cookie.value&lt;br /&gt;print&lt;br /&gt;En el improbable caso de que necesitáramos añadir una cookie antes de realizar la conexión, en lugar de conectarnos para que el sitio la mande, podríamos utilizar el método set_cookie de CookieJar, al que le pasamos un objeto de tipo Cookie. El constructor de Cookie, no obstante, es bastante complicado.&lt;br /&gt;&lt;br /&gt;¿Qué son los procesos y los threads?&lt;br /&gt;Las computadoras serían mucho menos útiles si no pudiéramos hacer más de una cosa a la vez. Si no pudiéramos, por ejemplo, escuchar música en nuestro reproductor de audio favorito mientras leemos un tutorial de Python en Mundo Geek.&lt;br /&gt;Pero, ¿cómo se conseguía esto en computadoras antiguas con un solo núcleo / una sola CPU? Lo que ocurría, y lo que ocurre ahora, es que en realidad no estamos ejecutando varios procesos a la vez (se llama proceso a un programa en ejecución), sino que los procesos se van turnando y, dada la velocidad a la que ejecutan las instrucciones, nosotros tenemos la impresión de que las tareas se ejecutan de forma paralela como si tuviéramos multitarea real.&lt;br /&gt;Cada vez que un proceso distinto pasa a ejecutarse es necesario realizar lo que se llama un cambio de contexto, durante el cual se salva el estado del programa que se estaba ejecutando a memoria y se carga el estado del programa que va a entrar a ejecutarse.&lt;br /&gt;En Python podemos crear nuevos procesos mediante la función os.fork, que ejecuta la llamada al sistema fork, o mediante otras funciones más avanzadas como popen2.popen2, de forma que nuestro programa pueda realizar varias tareas de forma paralela.&lt;br /&gt;Sin embargo el cambio de contexto puede ser relativamente lento, y los recursos necesarios para mantener el estado demasiados, por lo que a menudo es mucho más eficaz utilizar lo que se conoce como threads, hilos de ejecución, o procesos ligeros.&lt;br /&gt;Los threads son un concepto similar a los procesos: también se trata de código en ejecución. Sin embargo los threads se ejecutan dentro de un proceso, y los threads del proceso comparten recursos entre si, como la memoria, por ejemplo.&lt;br /&gt;El sistema operativo necesita menos recursos para crear y gestionar los threads, y al compartir recursos, el cambio de contexto es más rápido. Además, dado que los threads comparten el mismo espacio de memoria global, es sencillo compartir información entre ellos: cualquier variable global que tengamos en nuestro programa es vista por todos los threads.&lt;br /&gt;El GIL&lt;br /&gt;La ejecución de los threads en Python está controlada por el GIL (Global Interpreter Lock) de forma que sólo un thread puede ejecutarse a la vez, independientemente del número de procesadores con el que cuente la máquina. Esto posibilita que el escribir extensiones en C para Python sea mucho más sencillo, pero tiene la desventaja de limitar mucho el rendimiento, por lo que a pesar de todo, en Python, en ocasiones nos puede interesar más utilizar procesos que threads, que no sufren de esta limitación.&lt;br /&gt;Cada cierto número de instrucciones de bytecode la máquina virtual para la ejecución del thread y elige otro de entre los que estaban esperando.&lt;br /&gt;Por defecto el cambio de thread se realiza cada 10 instrucciones de bytecode, aunque se puede modificar mediante la función sys.setcheckinterval. También se cambia de thread cuando el hilo se pone a dormir con time.sleep o cuando comienza una operación de entrada/salida, las cuales pueden tardar mucho en finalizar, y por lo tanto, de no realizar el cambio, tendríamos a la CPU demasiado tiempo sin trabajar esperando a que la operación de E/S terminara.&lt;br /&gt;Para minimizar un poco el efecto del GIL en el rendimiento de nuestra aplicación es conveniente llamar al intérprete con el flag -O, lo que hará que se genere un bytecode optimizado con menos instrucciones, y,&lt;br /&gt;por lo tanto, menos cambios de contexto. También podemos plantearnos el utilizar procesos en lugar de threads, como ya comentamos, utilizando por ejemplo el módulo processing; escribir el código en el que el rendimiento sea crítico en una extensión en C o utilizar IronPython o Jython, que carecen de GIL.&lt;br /&gt;Threads en Python&lt;br /&gt;El trabajo con threads se lleva a cabo en Python mediante el módulo thread. Este módulo es opcional y dependiente de la plataforma, y puede ser necesario, aunque no es común, recompilar el intérprete para añadir el soporte de threads.&lt;br /&gt;Además de thread, también contamos con el módulo threading que se apoya en el primero para proporcionarnos una API de más alto nivel, más completa, y orientada a objetos. El módulo threading se basa ligeramente en el modelo de threads de Java.&lt;br /&gt;El módulo threading contiene una clase Thread que debemos extender para crear nuestros propios hilos de ejecución. El método run contendrá el código que queremos que ejecute el thread. Si queremos especificar nuestro propio constructor, este deberá llamar a threading.Thread.__init__(self) para inicializar el objeto correctamente.&lt;br /&gt;import threading&lt;br /&gt;class MiThread(threading.Thread):&lt;br /&gt;def __init__(self, num):&lt;br /&gt;threading.Thread.__init__(self)&lt;br /&gt;self.num = num&lt;br /&gt;def run(self):&lt;br /&gt;print “Soy el hilo”, self.num&lt;br /&gt;Para que el thread comience a ejecutar su código basta con crear una instancia de la clase que acabamos de definir y llamar a su método start. El código del hilo principal y el del que acabamos de crear se ejecutarán de forma concurrente.&lt;br /&gt;print “Soy el hilo principal”&lt;br /&gt;for i in range(0, 10):&lt;br /&gt;t = MiThread(i)&lt;br /&gt;t.start()&lt;br /&gt;t.join()&lt;br /&gt;El método join se utiliza para que el hilo que ejecuta la llamada se bloquee hasta que finalice el thread sobre el que se llama. En este caso se utiliza para que el hilo principal no termine su ejecución antes que los hijos, lo cuál podría resultar en algunas plataformas en la terminación de los hijos antes de finalizar su ejecución. El método join puede tomar como parámetro un número en coma flotante indicando el número máximo de segundos a esperar.&lt;br /&gt;Si se intenta llamar al método start para una instancia que ya se está ejecutando, obtendremos una excepción.&lt;br /&gt;La forma recomendada de crear nuevos hilos de ejecución consiste en extender la clase Thread, como hemos visto, aunque también es posible crear una instancia de Thread directamente, e indicar como parámetros del constructor una clase ejecutable (una clase con el método especial __call__) o una función a ejecutar, y los argumentos en una tupla (parámetro args) o un diccionario (parámetro kwargs).&lt;br /&gt;import threading&lt;br /&gt;def imprime(num):&lt;br /&gt;print “Soy el hilo”, num&lt;br /&gt;print “Soy el hilo principal”&lt;br /&gt;for i in range(0, 10):&lt;br /&gt;t = threading.Thread(target=imprime, args=(i, ))&lt;br /&gt;t.start()&lt;br /&gt;Además de los parámetros target, args y kwargs también podemos pasar al constructor un parámetro de tipo cadena name con el nombre que queremos que tome el thread (el thread tendrá un nombre predeterminado aunque no lo especifiquemos); un parámetro de tipo booleano verbose para indicar al módulo que imprima mensajes sobre el estado de los threads para la depuración y un parámetro group, que por ahora no admite ningún valor pero que en el futuro se utilizará para crear grupos de threads y poder trabajar a nivel de grupos.&lt;br /&gt;Para comprobar si un thread sigue ejecutándose, se puede utilizar el método isAlive. También podemos asignar un nombre al hilo y consultar su nombre con los métodos setName y getName, respectivamente.&lt;br /&gt;Mediante la función threading.enumerate obtendremos una lista de los objetos Thread que se están ejecutando, incluyendo el hilo principal (podemos comparar el objeto Thread con la variable main_thread para comprobar si se trata del hilo principal) y con threading.activeCount podemos consultar el número de threads ejecutándose.&lt;br /&gt;Los objetos Thread también cuentan con un método setDaemon que toma un valor booleano indicando si se trata de un demonio. La utilidad de esto es que si solo quedan threads de tipo demonio ejecutándose, la aplicación terminará automáticamente, terminando estos threads de forma segura.&lt;br /&gt;Por último tenemos en el módulo threading una clase Timer que hereda de Thread y cuya utilidad es la de ejecutar el código de su método run después de un periodo de tiempo indicado como parámetro en su constructor. También incluye un método cancel mediante el que cancelar la ejecución antes de que termine el periodo de espera.&lt;br /&gt;Sincronización&lt;br /&gt;Uno de los mayores problemas a los que tenemos que enfrentarnos al utilizar threads es la necesidad de sincronizar el acceso a ciertos recursos por parte de los threads. Entre los mecanismos de sincronización que tenemos disponibles en el módulo threading se encuentran los locks, locks reentrantes, semáforos, condiciones y eventos.&lt;br /&gt;Los locks, también llamados mutex (de mutual exclusion), cierres de exclusión mutua, cierres o candados, son objetos con dos estados posibles: adquirido o libre. Cuando un thread adquiere el candado, los demás threads que lleguen a ese punto posteriormente y pidan adquirirlo se bloquearán hasta que el thread que lo ha adquirido libere el candado, momento en el cuál podrá entrar otro thread.&lt;br /&gt;El candado se representa mediante la clase Lock. Para adquirir el&lt;br /&gt;candado se utiliza el método acquire del objeto, al que se le puede pasar un booleano para indicar si queremos esperar a que se libere (True) o no (False). Si indicamos que no queremos esperar, el método devolverá True o False dependiendo de si se adquirió o no el candado, respectivamente. Por defecto, si no se indica nada, el hilo se bloquea indefinidamente.&lt;br /&gt;Para liberar el candado una vez hemos terminado de ejecutar el bloque de código en el que pudiera producirse un problema de concurrencia, se utiliza el método release.&lt;br /&gt;lista = []&lt;br /&gt;lock = threading.Lock()&lt;br /&gt;def anyadir(obj):&lt;br /&gt;lock.acquire()&lt;br /&gt;lista.append(obj)&lt;br /&gt;lock.release()&lt;br /&gt;def obtener():&lt;br /&gt;lock.acquire()&lt;br /&gt;obj = lista.pop()&lt;br /&gt;lock.release()&lt;br /&gt;return obj&lt;br /&gt;La clase RLock funciona de forma similar a Lock, pero en este caso el candado puede ser adquirido por el mismo thread varias veces, y no quedará liberado hasta que el thread llame a release tantas veces como llamó a acquire. Como en Lock, y como en todas las primitivas de sincronización que veremos a continuación, es posible indicar a acquire si queremos que se bloquee o no.&lt;br /&gt;Los semáforos son otra clase de candados. La clase correspondiente, Semaphore, también cuenta con métodos acquire y release, pero se diferencia de un Lock normal en que el constructor de Semaphore puede tomar como parámetro opcional un entero value indicando el número máximo de threads que pueden acceder a la vez a la sección de código crítico. Si no se indica nada permite el acceso a un solo thread.&lt;br /&gt;Cuando un thread llama a acquire, la variable que indica el número de threads que pueden adquirir el semáforo disminuye en 1, porque&lt;br /&gt;hemos permitido entrar en la sección de código crítico a un hilo más. Cuando un hilo llama a release, la variable aumenta en 1.&lt;br /&gt;No es hasta que esta variable del semáforo es 0, que llamar a acquire producirá un bloqueo en el thread que realizó la petición, a la espera de que algún otro thread llame a release para liberar su plaza.&lt;br /&gt;Es importante destacar que el valor inicial de la variable tal como lo pasamos en el constructor, no es un límite máximo, sino que múltiples llamadas a release pueden hacer que el valor de la variable sea mayor que su valor original. Si no es esto lo que queremos, podemos utilizar la clase BoundedSemaphore en cuyo caso, ahora si, se consideraría un error llamar a release demasiadas veces, y se lanzaría una excepción de tipo ValueError de superarse el valor inicial.&lt;br /&gt;Podríamos utilizar los semáforos, por ejemplo, en un pequeño programa en el que múltiples threads descargaran datos de una URL, de forma que pudieramos limitar el número de conexiones a realizar al sitio web para no bombardear el sitio con cientos de peticiones concurrentes.&lt;br /&gt;semaforo = threading.Semaphore(4)&lt;br /&gt;def descargar(url):&lt;br /&gt;semaforo.acquire()&lt;br /&gt;urllib.urlretrieve(url)&lt;br /&gt;semaforo.release()&lt;br /&gt;Las condiciones (clase Condition) son de utilidad para hacer que los threads sólo puedan entrar en la sección crítica de darse una cierta condición o evento. Para esto utilizan un Lock pasado como parámetro, o crean un objeto RLock automaticamente si no se pasa ningún parámetro al constructor.&lt;br /&gt;Son especialmente adecuadas para el clásico problema de productor-consumidor. La clase cuenta con métodos acquire y release, que llamarán a los métodos correspondientes del candado asociado. También tenemos métodos wait, notify y notifyAll.&lt;br /&gt;El método wait debe llamarse después de haber adquirido el candado&lt;br /&gt;con acquire. Este método libera el candado y bloquea al thread hasta que una llamada a notify o notifyAll en otro thread le indican que se ha cumplido la condición por la que esperaba. El thread que informa a los demás de que se ha producido la condición, también debe llamar a acquire antes de llamar a notify o notifyAll.&lt;br /&gt;Al llamar a notify, se informa del evento a un solo thread, y por tanto se despierta un solo thread. Al llamar a notifyAll se despiertan todos los threads que esperaban a la condición.&lt;br /&gt;Tanto el thread que notifica como los que son notificados tienen que terminar liberando el lock con release.&lt;br /&gt;lista = []&lt;br /&gt;cond = threading.Condition()&lt;br /&gt;def consumir():&lt;br /&gt;cond.acquire()&lt;br /&gt;cond.wait()&lt;br /&gt;obj = lista.pop()&lt;br /&gt;cond.release()&lt;br /&gt;return obj&lt;br /&gt;def producir(obj):&lt;br /&gt;cond.acquire()&lt;br /&gt;lista.append(obj)&lt;br /&gt;cond.notify()&lt;br /&gt;cond.release()&lt;br /&gt;Los eventos, implementados mediante al clase Event, son un wrapper por encima de Condition y sirven principalmente para coordinar threads mediante señales que indican que se ha producido un evento. Los eventos nos abstraen del hecho de que estemos utilizando un Lock por debajo, por lo que carecen de métodos acquire y release.&lt;br /&gt;El thread que debe esperar el evento llama al método wait y se bloquea, opcionalmente pasando como parámetro un número en coma flotante indicando el número máximo de segundos a esperar. Otro hilo, cuando ocurre el evento, manda la señal a los threads bloqueados a la espera de dicho evento utilizando el método set. Los threads que estaban esperando se desbloquean una vez recibida la señal. El flag que determina si se ha producido el evento se puede volver a establecer a falso usando clear.&lt;br /&gt;Como vemos los eventos son muy similares a las condiciones, a excepción de que se desbloquean todos los threads que esperaban el evento y que no tenemos que llamar a acquire y release.&lt;br /&gt;import threading, time&lt;br /&gt;class MiThread(threading.Thread):&lt;br /&gt;def __init__(self, evento):&lt;br /&gt;threading.Thread.__init__(self)&lt;br /&gt;self.evento = evento&lt;br /&gt;def run(self):&lt;br /&gt;print self.getName(), “esperando al evento”&lt;br /&gt;self.evento.wait()&lt;br /&gt;print self.getName(), “termina la espera”&lt;br /&gt;evento = threading.Event()&lt;br /&gt;t1 = MiThread(evento)&lt;br /&gt;t1.start()&lt;br /&gt;t2 = MiThread(evento)&lt;br /&gt;t2.start()&lt;br /&gt;# Esperamos un poco&lt;br /&gt;time.sleep(5)&lt;br /&gt;evento.set()&lt;br /&gt;Por último, un pequeño extra. Si sois usuarios de Java sin duda estaréis echando en falta una palabra clave syncronized para hacer que sólo un thread pueda acceder al método sobre el que se utiliza a la vez. Una construcción común es el uso de un decorador para implementar esta funcionalidad usando un Lock. Sería algo así:&lt;br /&gt;def synchronized(lock):&lt;br /&gt;def dec(f):&lt;br /&gt;def func_dec(*args, **kwargs):&lt;br /&gt;lock.acquire()&lt;br /&gt;try:&lt;br /&gt;return f(*args, **kwargs)&lt;br /&gt;finally:&lt;br /&gt;lock.release()&lt;br /&gt;return func_dec&lt;br /&gt;return dec&lt;br /&gt;class MyThread(threading.Thread):&lt;br /&gt;@synchronized(mi_lock)&lt;br /&gt;def run(self):&lt;br /&gt;print “metodo sincronizado”&lt;br /&gt;&lt;br /&gt;Datos globales independientes&lt;br /&gt;Como ya hemos comentado los threads comparten las variables globales. Sin embargo pueden existir situaciones en las que queramos utilizar variables globales pero que estas variables se comporten como si fueran locales a un solo thread. Es decir, que cada uno de los threads tengan valores distintos independientes, y que los cambios de un determinado thread sobre el valor no se vean reflejados en las copias de los demás threads.&lt;br /&gt;Para lograr este comportamiento se puede utilizar la clase threading.local, que crea un almacén de datos locales. Primero debemos crear una instancia de la clase, o de una subclase, para después almacenar y obtener los valores a través de parámetros de la clase.&lt;br /&gt;datos_locales = threading.local()&lt;br /&gt;datos_locales.mi_var = “hola”&lt;br /&gt;print datos_locales.mi_var&lt;br /&gt;Fijémonos en el siguiente código, por ejemplo. Para el hilo principal el objeto local tiene un atributo var, y por lo tanto el print imprime su valor sin problemas. Sin embargo para el hilo t ese atributo no existe, y por lo tanto lanza una excepción.&lt;br /&gt;local = threading.local()&lt;br /&gt;def f():&lt;br /&gt;print local.var&lt;br /&gt;local.var = “hola”&lt;br /&gt;t = threading.Thread(target=f)&lt;br /&gt;print local.var&lt;br /&gt;t.start()&lt;br /&gt;t.join()&lt;br /&gt;Compartir información&lt;br /&gt;Para compartir información entre los threads de forma sencilla podemos utilizar la clase Queue.Queue, que implementa una cola (una estructura de datos de tipo FIFO) con soporte multihilo. Esta clase utiliza las primitivas de threading para ahorrarnos tener que sincronizar el acceso a los datos nosotros mismos.&lt;br /&gt;El constructor de Queue toma un parámetro opcional indicando el tamaño máximo de la cola. Si no se indica ningún valor no hay límite de tamaño.&lt;br /&gt;Para añadir un elemento a la cola se utiliza el método put(item); para obtener el siguiente elemento, get(). Ambos métodos tienen un parámetro booleano opcional block que indica si queremos que se espere hasta que haya algún elemento en la cola para poder devolverlo o hasta que la cola deje de estar llena para poder introducirlo.&lt;br /&gt;También existe un parámetro opcional timeout que indica, en segundos, el tiempo máximo a esperar. Si el timeout acaba sin poder haber realizado la operación debido a que la cola estaba llena o vacía, o bien si block era False, se lanzará una excepción de tipo Queue.Full o Queue.Empty, respectivamente.&lt;br /&gt;Con qsize obtenemos el tamaño de la cola y con empty() y full() podemos comprobar si está vacía o llena.&lt;br /&gt;q = Queue.Queue()&lt;br /&gt;class MiThread(threading.Thread):&lt;br /&gt;def __init__(self, q):&lt;br /&gt;self.q = q&lt;br /&gt;threading.Thread.__init__(self)&lt;br /&gt;def run(self):&lt;br /&gt;while True:&lt;br /&gt;try:&lt;br /&gt;obj = q.get(False)&lt;br /&gt;except Queue.Empty:&lt;br /&gt;print “Fin”&lt;br /&gt;break&lt;br /&gt;print obj&lt;br /&gt;for i in range(10):&lt;br /&gt;q.put(i)&lt;br /&gt;t = MiThread(q)&lt;br /&gt;t.start()&lt;br /&gt;t.join()&lt;br /&gt;&lt;br /&gt;Serialización de objetos&lt;br /&gt;Algunas veces tenemos la necesidad de guardar un objeto a disco para poder recuperarlo más tarde, o puede que nos sea necesario mandar un objeto a través de la red, a otro programa en Python ejecutándose en otra máquina.&lt;br /&gt;Al proceso de transformar el estado de un objeto en un formato que se pueda almacenar, recuperar y transportar se le conoce con el nombre de serialización o marshalling.&lt;br /&gt;En Python tenemos varios módulos que nos facilitan esta tarea, como marshal, pickle, cPickle y shelve.&lt;br /&gt;El módulo marshal es el más básico y el más primitivo de los tres, y es que, de hecho, su propósito principal y su razón de ser no es el de serializar objetos, sino trabajar con bytecode Python (archivos .pyc).&lt;br /&gt;marshal sólo permite serializar objetos simples (la mayoría de los tipos incluidos por defecto en Python), y no proporciona ningún tipo de mecanismo de seguridad ni comprobaciones frente a datos corruptos o mal formateados. Es más, el formato utilizado para guardar el bytecode (y por tanto el formato utilizado para guardar los objetos con marshal) puede cambiar entre versiones, por lo que no es adecuado para almacenar datos de larga duración.&lt;br /&gt;pickle, por su parte, permite serializar casi cualquier objeto (objetos de tipos definidos por el usuario, colecciones que contienen colecciones, etc) y cuenta con algunos mecanismos de seguridad básicos. Sin embargo, al ser más complejo que marshal, y, sobre todo, al estar escrito en Python en lugar de en C, como marshal, también es mucho más lento.&lt;br /&gt;La solución, si la velocidad de la serialización es importante para nuestra aplicación, es utilizar cPickle, que no es más que es una implementación en C de pickle. cPickle es hasta 1000 veces más rápido que pickle, y prácticamente igual de rápido que marshal.&lt;br /&gt;Si intentamos importar cPickle y se produce un error por algún motivo, se lanzará una excepción de tipo ImportError. Para utilizar cPickle si está disponible y pickle en caso contrario, podríamos usar un código similar al siguiente:&lt;br /&gt;try:&lt;br /&gt;import cPickle as pickle&lt;br /&gt;except ImportError:&lt;br /&gt;import pickle&lt;br /&gt;as en un import sirve para importar el elemento seleccionado utilizando otro nombre indicado, en lugar de su nombre.&lt;br /&gt;La forma más sencilla de serializar un objeto usando pickle es mediante una llamada a la función dump pasando como argumento el objeto a serializar y un objeto archivo en el que guardarlo (o cualquier otro tipo de objeto similar a un archivo, siempre que ofrezca métodos read, realine y write).&lt;br /&gt;try:&lt;br /&gt;import cPickle as pickle&lt;br /&gt;except ImportError:&lt;br /&gt;import pickle&lt;br /&gt;fichero = file(“datos.dat”, “w”)&lt;br /&gt;animales = [“piton”, “mono”, “camello”]&lt;br /&gt;pickle.dump(animales, fichero)&lt;br /&gt;fichero.close()&lt;br /&gt;La función dump también tiene un parámetro opcional protocol que indica el protocolo a utilizar al guardar. Por defecto su valor es 0, que utiliza formato texto y es el menos eficiente. El protocolo 1 es más eficiente que el 0, pero menos que el 2. Tanto el protocolo 1 como el 2 utilizan un formato binario para guardar los datos.&lt;br /&gt;try:&lt;br /&gt;import cPickle as pickle&lt;br /&gt;except ImportError:&lt;br /&gt;import pickle&lt;br /&gt;fichero = file(“datos.dat”, “w”)&lt;br /&gt;animales = [“piton”, “mono”, “camello”]&lt;br /&gt;pickle.dump(animales, fichero, 2)&lt;br /&gt;fichero.close()&lt;br /&gt;Para volver a cargar un objeto serializado se utiliza la función load, a la que se le pasa el archivo en el que se guardó.&lt;br /&gt;try:&lt;br /&gt;import cPickle as pickle&lt;br /&gt;except ImportError:&lt;br /&gt;import pickle&lt;br /&gt;fichero = file(“datos.dat”, “w”)&lt;br /&gt;animales = [“piton”, “mono”, “camello”]&lt;br /&gt;pickle.dump(animales, fichero)&lt;br /&gt;fichero.close()&lt;br /&gt;fichero = file(“datos.dat”)&lt;br /&gt;animales2 = pickle.load(fichero)&lt;br /&gt;print animales2&lt;br /&gt;Supongamos ahora que queremos almacenar un par de listas en un fichero. Esto sería tan sencillo como llamar una vez a dump por cada lista, y llamar después una vez a load por cada lista.&lt;br /&gt;fichero = file(“datos.dat”, “w”)&lt;br /&gt;animales = [“piton”, “mono”, “camello”]&lt;br /&gt;lenguajes = [“python”, “mono”, “perl”]&lt;br /&gt;pickle.dump(animales, fichero)&lt;br /&gt;pickle.dump(lenguajes, fichero)&lt;br /&gt;fichero = file(“datos.dat”)&lt;br /&gt;animales2 = pickle.load(fichero)&lt;br /&gt;lenguajes2 = pickle.load(fichero)&lt;br /&gt;print animales2&lt;br /&gt;print lenguajes2&lt;br /&gt;Pero, ¿y si hubiéramos guardado 30 objetos y quisiéramos acceder al último de ellos? ¿o si no recordáramos en qué posición lo habíamos guardado? El módulo shelve extiende pickle / cPickle para proporcionar una forma de realizar la serialización más clara y sencilla, en la que podemos acceder a la versión serializada de un objeto mediante una cadena asociada, a través de una estructura parecida a un diccionario.&lt;br /&gt;La única función que necesitamos conocer del módulo shelve es open, que cuenta con un parámetro filename mediante el que indicar la ruta a un archivo en el que guardar los objetos (en realidad se puede crear más de un archivo, con nombres basados en filename, pero esto es transparente al usuario).&lt;br /&gt;La función open también cuenta con un parámetro opcional protocol, con el que especificar el protocolo que queremos que utilice pickle por debajo.&lt;br /&gt;Como resultado de la llamada a open obtenemos un objeto Shelf, con el que podemos trabajar como si de un diccionario normal se tratase (a excepción de que las claves sólo pueden ser cadenas) para almacenar y recuperar nuestros objetos.&lt;br /&gt;Como un diccionario cualquiera la clase Shelf cuenta con métodos get, has_key, items, keys, values, …&lt;br /&gt;Una vez hayamos terminado de trabajar con el objeto Shelf, lo cerraremos utilizando el método close.&lt;br /&gt;import shelve&lt;br /&gt;animales = [“piton”, “mono”, “camello”]&lt;br /&gt;lenguajes = [“python”, “mono”, “perl”]&lt;br /&gt;shelf = shelve.open(“datos.dat”)&lt;br /&gt;shelf[“primera”] = animales&lt;br /&gt;shelf[“segunda”] = lenguajes&lt;br /&gt;print shelf[“segunda”]&lt;br /&gt;shelf.close()&lt;br /&gt;&lt;br /&gt;Bases de Datos&lt;br /&gt;Existen problemas para los que guardar nuestros datos en ficheros de texto plano, en archivos XML, o mediante serialización con pickle o shelve pueden ser soluciones poco convenientes. En ocasiones no queda más remedio que recurrir a las bases de datos, ya sea por cuestiones de escalabilidad, de interoperabilidad, de coherencia, de seguridad, de confidencialidad, etc.&lt;br /&gt;A lo largo de este capítulo aprenderemos a trabajar con bases de datos en Python. Sin embargo se asumen una serie de conocimientos básicos, como puede ser el manejo elemental de SQL. Si este no es el caso, existen miles de recursos a disposición del lector en Internet para introducirse en el manejo de bases de datos.&lt;br /&gt;DB API&lt;br /&gt;Existen cientos de bases de datos en el mercado, tanto comerciales como gratuitas. También existen decenas de módulos distintos para trabajar con dichas bases de datos en Python, lo que significa decenas de APIs distintas por aprender.&lt;br /&gt;En Python, como en otros lenguajes como Java con JDBC, existe una propuesta de API estándar para el manejo de bases de datos, de forma que el código sea prácticamente igual independientemente de la base de datos que estemos utilizando por debajo. Esta especificación recibe el nombre de Python Database API o DB-API y se recoge en el PEP 249 (http://www.python.org/dev/peps/pep-0249/).&lt;br /&gt;DB-API se encuentra en estos momentos en su versión 2.0, y existen implementaciones para las bases de datos relacionales más conocidas, así como para algunas bases de datos no relacionales.&lt;br /&gt;A lo largo de este capítulo utilizaremos la base de datos SQLite para los ejemplos, ya que no se necesita instalar y ejecutar un proceso servidor independiente con el que se comunique el programa, sino que se trata de una pequeña librería en C que se integra con la aplicación y que viene incluida con Python por defecto desde la versión 2.5. Desde la misma versión Python también incorpora un módulo compatible con esta base de datos que sigue la especificación de DB API 2.0: sqlite3, por lo que no necesitaremos ningún tipo de configuración extra.&lt;br /&gt;Nada impide al lector, no obstante, instalar y utilizar cualquier otra base de datos, como MySQL, con la cuál podemos trabajar a través del driver compatible con DB API 2.0 MySQLdb (http://mysql-python.sourceforge.net/).&lt;br /&gt;Variables globales&lt;br /&gt;Antes de comenzar a trabajar con sqlite3, vamos a consultar algunos datos interesantes sobre el módulo. Todos los drivers compatibles con DB-API 2.0 deben tener 3 variables globales que los describen. A saber:&lt;br /&gt;apilevel• : una cadena con la versión de DB API que utiliza. Actualmente sólo puede tomar como valor “1.0” o “2.0”. Si la variable no existe se asume que es 1.0.&lt;br /&gt;threadsafety• : se trata de un entero de 0 a 3 que describe lo seguro que es el módulo para el uso con threads. Si es 0 no se puede compartir el módulo entre threads sin utilizar algún tipo de mecanismo de sincronización; si es 1, pueden compartir el módulo pero no las conexiones; si es 2, módulos y conexiones pero no cursores y, por último, si es 3, es totalmente thread-safe.&lt;br /&gt;paramstyle: informa sobre la sintaxis a utilizar para insertar valores • en la consulta SQL de forma dinámica.&lt;br /&gt;qmarkɣɣ: interrogaciones.&lt;br /&gt;sql = “select all from t where valor=?”&lt;br /&gt;numericɣɣ: un número indicando la posición.&lt;br /&gt;sql = “select all from t where valor=:1”&lt;br /&gt;namedɣɣ: el nombre del valor.&lt;br /&gt;sql = “select all from t where valor=:valor”&lt;br /&gt;formatɣɣ: especificadores de formato similares a los del printf de C.&lt;br /&gt;sql = “select all from t where valor=%s”&lt;br /&gt;pyformatɣɣ: similar al anterior, pero con las extensiones de Python.&lt;br /&gt;sql = “select all from t where valor=%(valor)”&lt;br /&gt;Veamos los valores correspondientes a sqlite3:&lt;br /&gt;&gt;&gt;&gt; import sqlite3 as dbapi&lt;br /&gt;&gt;&gt;&gt; print dbapi.apilevel&lt;br /&gt;2.0&lt;br /&gt;&gt;&gt;&gt; print dbapi.threadsafety&lt;br /&gt;1&lt;br /&gt;&gt;&gt;&gt; print dbapi.paramstyle&lt;br /&gt;qmark&lt;br /&gt;Excepciones&lt;br /&gt;A continuación podéis encontrar la jerarquía de excepciones que deben proporcionar los módulos, junto con una pequeña descripción de cada excepción, a modo de referencia.&lt;br /&gt;StandardError&lt;br /&gt;|__Warning&lt;br /&gt;|__Error&lt;br /&gt;|__InterfaceError&lt;br /&gt;|__DatabaseError&lt;br /&gt;|__DataError&lt;br /&gt;|__OperationalError&lt;br /&gt;|__IntegrityError&lt;br /&gt;|__InternalError&lt;br /&gt;|__ProgrammingError&lt;br /&gt;|__NotSupportedError&lt;br /&gt;StandardError• : Super clase para todas las excepciones de DB API.&lt;br /&gt;Warning• : Excepción que se lanza para avisos importantes.&lt;br /&gt;Error• : Super clase de los errores.&lt;br /&gt;InterfaceError• : Errores relacionados con la interfaz de la base de datos, y no con la base de datos en sí.&lt;br /&gt;DatabaseError• : Errores relacionados con la base de datos.&lt;br /&gt;DataError• : Errores relacionados con los datos, como una división entre cero.&lt;br /&gt;OperationalError• : Errores relacionados con el funcionamiento de la base de datos, como una desconexión inesperada.&lt;br /&gt;IntegrityError• : Errores relacionados con la integridad referencial.&lt;br /&gt;InternalError• : Error interno de la base de datos.&lt;br /&gt;ProgrammingError• : Errores de programación, como errores en el código SQL.&lt;br /&gt;NotSupportedError• : Excepción que se lanza cuando se solicita un método que no está soportado por la base de datos.&lt;br /&gt;Uso básico de DB-API&lt;br /&gt;Pasemos ahora a ver cómo trabajar con nuestra base de datos a través de DB-API.&lt;br /&gt;Lo primero que tendremos que hacer es realizar una conexión con el servidor de la base de datos. Esto se hace mediante la función connect, cuyos parámetros no están estandarizados y dependen de la base de datos a la que estemos conectándonos.&lt;br /&gt;En el caso de sqlite3 sólo necesitamos pasar como parámetro una cadena con la ruta al archivo en el que guardar los datos de la base de datos, o bien la cadena “:memory:” para utilizar la memoria RAM en lugar de un fichero en disco.&lt;br /&gt;Por otro lado, en el caso de MySQLdb, connect toma como parámetros la máquina en la que corre el servidor (host), el puerto (port), nombre de usuario con el que autenticarse (user), contraseña (password) y base de datos a la que conectarnos de entre las que se encuentran en nuestro SGBD (db).&lt;br /&gt;La función connect devuelve un objeto de tipo Connection que representa la conexión con el servidor.&lt;br /&gt;&gt;&gt;&gt; bbdd = dbapi.connect(“bbdd.dat”)&lt;br /&gt;&gt;&gt;&gt; print bbdd&lt;br /&gt;&lt;sqlite3.connection&gt;&lt;br /&gt;Las distintas operaciones que podemos realizar con la base de datos se realizan a través de un objeto Cursor. Para crear este objeto se utiliza el método cursor() del objeto Connection:&lt;br /&gt;c = bbdd.cursor()&lt;br /&gt;Las operaciones se ejecutan a través del método execute de Cursor, pasando como parámetro una cadena con el código SQL a ejecutar.&lt;br /&gt;Como ejemplo creemos una nueva tabla empleados en la base de datos:&lt;br /&gt;c.execute(“””create table empleados (dni text,&lt;br /&gt;nombre text,&lt;br /&gt;departamento text)”””)&lt;br /&gt;y a continuación, insertemos una tupla en nuestra nueva tabla:&lt;br /&gt;c.execute(“””insert into empleados&lt;br /&gt;values (‘12345678-A’, ‘Manuel Gil’, ‘Contabilidad’)”””)&lt;br /&gt;Si nuestra base de datos soporta transacciones, si estas están activadas, y si la característica de auto-commit está desactivada, será necesario llamar al método commit de la conexion para que se lleven a cabo las operaciones definidas en la transacción.&lt;br /&gt;Si en estas circunstancias utilizáramos una herramienta externa para comprobar el contenido de nuestra base de datos sin hacer primero el commit nos encontraríamos entonces con una base de datos vacía.&lt;br /&gt;Si comprobáramos el contenido de la base de datos desde Python, sin cerrar el cursor ni la conexión, recibiríamos el resultado del contexto de la transacción, por lo que parecería que se han llevado a cabo los cambios, aunque no es así, y los cambios sólo se aplican, como comentamos, al llamar a commit.&lt;br /&gt;Para bases de datos que no soporten transacciones el estándar dicta que debe proporcionarse un método commit con implementación vacía, por lo que no es mala idea llamar siempre a commit aunque no sea necesario para poder cambiar de sistema de base de datos con solo modificar la línea del import.&lt;br /&gt;Si nuestra base de datos soporta la característica de rollback también podemos cancelar la transacción actual con:&lt;br /&gt;bbdd.rollback()&lt;br /&gt;Si la base de datos no soporta rollback llamar a este método producirá una excepción.&lt;br /&gt;Veamos ahora un ejemplo completo de uso:&lt;br /&gt;import sqlite3 as dbapi&lt;br /&gt;bbdd = dbapi.connect(“bbdd.dat”)&lt;br /&gt;cursor = bbdd.cursor()&lt;br /&gt;cursor.execute(“””create table empleados (dni text,&lt;br /&gt;nombre text,&lt;br /&gt;departamento text)”””)&lt;br /&gt;cursor.execute(“””insert into empleados&lt;br /&gt;values (‘12345678-A’, ‘Manuel Gil’, ‘Contabilidad’)”””)&lt;br /&gt;bbdd.commit()&lt;br /&gt;cursor.execute(“””select * from empleados&lt;br /&gt;where departamento=’Contabilidad’”””)&lt;br /&gt;for tupla in cursor.fetchall():&lt;br /&gt;print tupla&lt;br /&gt;Como vemos, para realizar consultas a la base de datos también se utiliza execute. Para consultar las tuplas resultantes de la sentencia SQL se puede llamar a los métodos de Cursor fetchone, fetchmany o fetchall o usar el objeto Cursor como un iterador.&lt;br /&gt;cursor.execute(“””select * from empleados&lt;br /&gt;where departamento=’Contabilidad’”””)&lt;br /&gt;for resultado in cursor:&lt;br /&gt;print tupla&lt;br /&gt;El método fetchone devuelve la siguiente tupla del conjunto resultado o None cuando no existen más tuplas, fetchmany devuelve el número de tuplas indicado por el entero pasado como parámetro o bien el número indicado por el atributo Cursor.arraysize si no se pasa ningún parámetro (Cursor.arraysize vale 1 por defecto) y fetchall devuelve un objeto iterable con todas las tuplas.&lt;br /&gt;A la hora de trabajar con selects u otros tipos de sentencias SQL es importante tener en cuenta que no deberían usarse los métodos de cadena habituales para construir las sentencias, dado que esto nos haría vulnerables a ataques de inyección SQL, sino que en su lugar debe usarse la característica de sustitución de parámetros de DB API.&lt;br /&gt;Supongamos que estamos desarrollando una aplicación web con Python para un banco y que se pudiera consultar una lista de sucursales del banco en una ciudad determinada con una URL de la forma http://www.mibanco.com/sucursales?ciudad=Madrid&lt;br /&gt;Podríamos tener una consulta como esta:&lt;br /&gt;cursor.execute(“””select * from sucursales&lt;br /&gt;where ciudad=’” + ciudad + “’”””)&lt;br /&gt;A primera vista podría parecer que no existe ningún problema: no hacemos más que obtener las sucursales que se encuentren en la ciudad indicada por la variable ciudad. Pero, ¿qué ocurriría si un usuario malintencionado accediera a una URL como “http://www.mibanco.com/sucursales?ciudad=Madrid’;SELECT * FROM contrasenyas”?&lt;br /&gt;Como no se realiza ninguna validación sobre los valores que puede contener la variable ciudad, sería sencillo que alguien pudiera hacerse con el control total de la aplicación.&lt;br /&gt;Lo correcto sería, como decíamos, utilizar la característica de sustitución de parámetros de DB API. El valor de paramstyle para el módulo sqlite3 era qmark. Esto significa que debemos escribir un signo de interrogación en el lugar en el que queramos insertar el valor, y basta pasar un segundo parámetro a execute en forma de secuencia o mapping con los valores a utilizar para que el módulo cree la sentencia por nosotros.&lt;br /&gt;cursor.execute(“””select * from sucursales&lt;br /&gt;where ciudad=?”””, (ciudad,))&lt;br /&gt;Por último, al final del programa se debe cerrar el cursor y la conexion:&lt;br /&gt;cursor.close()&lt;br /&gt;bbdd.close()&lt;br /&gt;Tipos SQL&lt;br /&gt;En ocasiones podemos necesitar trabajar con tipos de SQL, y almacenar, por ejemplo, fechas u horas usando Date y Time y no con cadenas. La API de bases de datos de Python incluye una serie de constructores a utilizar para crear estos tipos. Estos son:&lt;br /&gt;Date(year, month, day)• : Para almacenar fechas.&lt;br /&gt;Time(hour, minute, second)• : Para almacenar horas.&lt;br /&gt;Timestamp(year, month, day, hour, minute, second)• : Para almacenar timestamps (una fecha con su hora).&lt;br /&gt;DateFromTicks(ticks)• : Para crear una fecha a partir de un número con los segundos transcurridos desde el epoch (el 1 de Enero de 1970 a las 00:00:00 GMT).&lt;br /&gt;TimeFromTicks(ticks)• : Similar al anterior, para horas en lugar de fechas.&lt;br /&gt;TimestampFromTicks(ticks)• : Similar al anterior, para timestamps.&lt;br /&gt;Binary(string)• : Valor binario.&lt;br /&gt;Otras opciones&lt;br /&gt;Por supuesto no estamos obligados a utilizar DB-API, ni bases de datos relacionales. En Python existen módulos para trabajar con bases de datos orientadas a objetos, como ZODB (Zope Object Database) y motores para mapeo objeto-relacional (ORM) como SQLAlchemy, SQLObject o Storm.&lt;br /&gt;Además, si utilizamos IronPython en lugar de CPython tenemos la posibilidad de utilizar las conexiones a bases de datos de .NET, y si utilizamos Jython, las de Java.&lt;br /&gt;&lt;br /&gt;Documentación&lt;br /&gt;Docstrings&lt;br /&gt;En capítulos anteriores ya comentamos en varias ocasiones que todos los objetos cuentan con una variable especial __doc__ mediante la que indicar el propósito y uso del objeto. Estos son los llamados docstrings o cadenas de documentación.&lt;br /&gt;A estos atributos se les puede asociar el texto correspondiente explícitamente, asignándolo al literal cadena correspondiente, como con cualquier otra variable. Sin embargo, por conveniencia, Python ofrece un mecanismo mucho más sencillo y es que si el primer estamento de la definición del objeto es una cadena, esta se asocia a la variable __doc__ automáticamente.&lt;br /&gt;def haz_algo(arg):&lt;br /&gt;“””Este es el docstring de la funcion.”””&lt;br /&gt;print arg&lt;br /&gt;print haz_algo.__doc__&lt;br /&gt;haz_algo.__doc__ = “””Este es un nuevo docstring.”””&lt;br /&gt;print haz_algo.__doc__&lt;br /&gt;Como vemos lo interesante de estas cadenas es que, a diferencia de los comentarios normales de Python y de los comentarios de otros lenguajes, las cadenas de documentación no se eliminan del bytecode, por lo que se pueden consultar en tiempo de ejecución, usando, por ejemplo, la función help del lenguaje, o utilizando la sentencia print como en el ejemplo anterior.&lt;br /&gt;&gt;&gt;&gt; help(haz_algo)&lt;br /&gt;Help on function haz_algo in module __main__:&lt;br /&gt;haz_algo(arg)&lt;br /&gt;Este es un nuevo docstring.&lt;br /&gt;Pydoc&lt;br /&gt;La función help, que comentamos brevemente con anterioridad, utiliza el módulo pydoc para generar la documentación de un objeto a partir de su docstring y los docstrings de sus miembros. Este módulo, incluido por defecto con Python desde la versión 2.1, se puede importar en nuestro código Python y utilizarse programaticamente, o bien se puede utilizar como una herramienta de línea de comandos que sería el equivalente a la aplicación Javadoc del mundo Java.&lt;br /&gt;pydoc puede mostrar la información como texto en la consola, tal como lo utiliza help, pero también puede generar archivos HTML como javadoc o facilitar la información a través de un pequeño servidor web incluido con el módulo.&lt;br /&gt;Pydoc es muy sencillo de utilizar. Con&lt;br /&gt;pydoc.py nombre1 [nombre2 ...]&lt;br /&gt;se muestra la documentación del tema, módulo, clase, paquete, función o palabra clave indicada de forma similar a la función help. Si el nombre es keywords, topics o modules se listarán las distintas palabras claves, temas y módulos respectivamente.&lt;br /&gt;Si se pasa el flag -w, el script guardará la documentación en uno o varios archivos html en lugar de mostrarla por pantalla.&lt;br /&gt;pydoc.py -w nombre1 [nombre2 ...]&lt;br /&gt;El flag -k sirve para buscar una determinada palabra en las sinopsis de todos los módulos disponibles. La sinopsis es la primera línea de la cadena de documentación.&lt;br /&gt;pydoc.py -k xml&lt;br /&gt;Con -p podemos iniciar el servidor HTTP en el puerto indicado.&lt;br /&gt;pydoc.py -p puerto&lt;br /&gt;Una vez hecho esto podemos acceder a la documentación de todos los módulos disponibles abriendo la página http://localhost:puerto en nuestro navegador.&lt;br /&gt;Por último, mediante el flag -g podemos lanzar una interfaz gráfica para buscar documentación que utiliza el servidor HTTP para mostrar los resultados.&lt;br /&gt;Epydoc y reStructuredText&lt;br /&gt;El problema de pydoc es que es muy simple, y no permite añadir semántica o modificar estilos de la documentación. No podemos, por ejemplo, indicar que en una línea en concreto de entre las líneas de documentación de la función describe un parámetro de la función o mostrar un cierto término en cursiva.&lt;br /&gt;Existen proyectos para generar documentación con funcionalidades más avanzadas como Docutils, Epydoc o Sphinx, aunque es necesario aprender sintaxis especiales.&lt;br /&gt;Docutils es un proyecto desarrollado por David Goodger que incluye distintas herramientas para generar documentación utilizando el formato reStructuredText, un formato de texto plano creado por el mismo autor, y que es el formato más utilizado en la comunidad Python. reStructuredText se utiliza, entre otros, para la creación de los PEPs (Python Enhancement Proposals).&lt;br /&gt;Sin embargo, actualmente Docutils es más indicado para generar documentos a partir de archivos de texto, y no a partir de docstrings extraídos de código fuente Python, ya que el parser encargado de este trabajo dista mucho de estar terminado.&lt;br /&gt;EpyDoc es una de las herramientas de generación de documentación para Python más utilizadas. Además de texto plano y de su propio formato, llamado epytext, soporta reStructuredText y sintaxis Javadoc, cosa que los programadores Java agradecerán.&lt;br /&gt;A lo largo del resto del capítulo utilizaremos reStructuredText como&lt;br /&gt;lenguaje de marcado y EpyDoc para generar los documentos finales.&lt;br /&gt;Epydoc se puede descargar desde su página web en forma de instalador exe para Windows, paquete RPM para Fedora o similares, o en archivos zip y tar.gz que incluyen scripts de instalación: http://epydoc.sourceforge.net/. También se encuentra en los repositorios de varias distribuciones Linux.&lt;br /&gt;Una vez hayamos instalado Epydoc siguiendo el método adecuado para nuestro sistema operativo tendremos acceso a su funcionalidad a través de dos interfaces de usuario distintas: el script epydoc, que consiste en una aplicación de línea de comandos, y el script epydocgui (epydoc.pyw en Windows), que ofrece una interfaz gráfica. Además también podemos acceder a la funcionalidad de epydoc programaticamente, como en el caso de pydoc.&lt;br /&gt;Vamos a crear un pequeño módulo con un par de clases para ver primero el resultado de utilizar epydoc con docstrings de texto plano, sin ningún tipo de marcado especial.&lt;br /&gt;“””Modulo para ejemplificar el uso de epydoc.”””&lt;br /&gt;class Persona:&lt;br /&gt;“””Mi clase de ejemplo.”””&lt;br /&gt;def __init__(self, nombre):&lt;br /&gt;“””Inicializador de la clase Persona.”””&lt;br /&gt;self.nombre = nombre&lt;br /&gt;self.mostrar_nombre()&lt;br /&gt;def mostrar_nombre(self):&lt;br /&gt;“””Imprime el nombre de la persona”””&lt;br /&gt;print “Esta es la persona %s” % self.nombre&lt;br /&gt;class Empleado(Persona):&lt;br /&gt;“””Subclase de Persona.”””&lt;br /&gt;pass&lt;br /&gt;if __name__ == “__main__”:&lt;br /&gt;raul = Persona(“Raul”)&lt;br /&gt;El formato de salida por defecto de epydoc es HTML. Por lo tanto para generar la documentación en forma de documentos HTML bastaría escribir algo así:&lt;br /&gt;epydoc ejemplo.py&lt;br /&gt;o bien&lt;br /&gt;epydoc --html ejemplo.py&lt;br /&gt;Para generar un archivo PDF, utilizando LaTeX, se utilizaría el flag --pdf:&lt;br /&gt;epydoc --pdf ejemplo.py&lt;br /&gt;Si LaTeX no está instalado o epydoc no encuentra el ejecutable no será posible generar el PDF.&lt;br /&gt;También podemos indicar el nombre del proyecto y la URL mediante las opciones --name y --url:&lt;br /&gt;epydoc --name Ejemplo --url http://mundogeek.net ejemplo.py&lt;br /&gt;E incluso añadir diagramas mostrando la clase base y subclases (--graph classtree), las llamadas entre funciones y métodos (--graph callgraph), clases y subclases usando notación UML (--graph umlclasstree) o todos ellos (--graph all).&lt;br /&gt;epydoc --graph all ejemplo.py&lt;br /&gt;Para generar el grafo de llamadas, no obstante, es necesario generar un archivo con la información necesaria utilizando el módulo profile o el módulo hotshot e indicar el archivo resultante utilizando el flag --pstat:&lt;br /&gt;epydoc --graph all --pstat profile.out ejemplo.py&lt;br /&gt;Veamos ahora algunas funcionalidades básicas de marcado en reStructuredText.&lt;br /&gt;Para poner un texto en itálica se rodea el texto con asteriscos:&lt;br /&gt;*itálica* -&gt; itálica&lt;br /&gt;Para ponerlo en negrita, se utilizan dos asteriscos:&lt;br /&gt;**negrita** -&gt; negrita&lt;br /&gt;Para mostrar el texto como monoespacio, por ejemplo para mostrar código inline, se utiliza “.&lt;br /&gt;“monoespacio” -&gt; monoespacio&lt;br /&gt;Si necesitamos utilizar cualquiera de estos caracteres especiales, se pueden escapar utilizando la barra invertida.&lt;br /&gt;\* es un carácter especial -&gt; * es un carácter especial&lt;br /&gt;Los títulos se crean añadiendo una línea de caracteres no alfanuméricos por debajo del texto, o por encima y por debajo del texto. Para crear un subtitulo basta utilizar una nueva combinación.&lt;br /&gt;Título&lt;br /&gt;======&lt;br /&gt;Subtitulo&lt;br /&gt;—————————&lt;br /&gt;Título&lt;br /&gt;Subtitulo&lt;br /&gt;Para crear una lista no ordenada se empieza cada línea con el caracter ‘*’, ‘-’ o ‘+’:&lt;br /&gt;* Python&lt;br /&gt;* C&lt;br /&gt;* Java&lt;br /&gt;Python•&lt;br /&gt;C•&lt;br /&gt;Java•&lt;br /&gt;Para crear una lista numerada se empieza la línea con el número seguido de un punto, o bien con el símbolo ‘#’ para que se introduzca el número automáticamente.&lt;br /&gt;1. Python&lt;br /&gt;2. C&lt;br /&gt;3. Java&lt;br /&gt;Python1.&lt;br /&gt;C2.&lt;br /&gt;Java3.&lt;br /&gt;Para describir propiedades de los elementos que estamos documentando se utilizan los campos o fields. En reStructuredText los campos comienzan con ‘:’, le sigue el nombre del campo y opcionalmente sus argumentos, y se cierra de nuevo con ‘:’, para terminar con el cuerpo del campo.&lt;br /&gt;Estos son algunos de los campos que soporta Epydoc:&lt;br /&gt;Funciones y métodos&lt;br /&gt;:param p: Un parámetro&lt;br /&gt;Describe el parámetro p.&lt;br /&gt;:type p: str&lt;br /&gt;Especifica el tipo esperado para el parámetro p.&lt;br /&gt;:return: True si son iguales&lt;br /&gt;Valor de retorno.&lt;br /&gt;:rtype: str&lt;br /&gt;Tipo del valor de retorno.&lt;br /&gt;:keyword p: Un parámetro&lt;br /&gt;Descripción del parámetro con valor por defecto y nombre p.&lt;br /&gt;:raise e: Si el parámetro es cero&lt;br /&gt;Describe las circunstancias para las que se lanza la excepción e.&lt;br /&gt;Variables&lt;br /&gt;:ivar v: Una variable&lt;br /&gt;Descripción de la instancia v.&lt;br /&gt;:cvar v: Una variable&lt;br /&gt;Descripción de la variable estática de clase v.&lt;br /&gt;:var v: Una variable&lt;br /&gt;Descripción de la variable v del módulo.&lt;br /&gt;Python para todos&lt;br /&gt;132&lt;br /&gt;:type v: str&lt;br /&gt;Tipo de la variable v.&lt;br /&gt;Notas&lt;br /&gt;:note: Una nota&lt;br /&gt;Una nota sobre el objeto.&lt;br /&gt;:attention: Importante&lt;br /&gt;Una nota importante sobre el objeto.&lt;br /&gt;:bug: No funciona para el valor 0&lt;br /&gt;Descripción de un error en el objeto.&lt;br /&gt;:warning: Cuidado con el valor 0&lt;br /&gt;Una advertencia acerca de un objeto.&lt;br /&gt;:see: Ver ‘Python para todos’&lt;br /&gt;Para indicar información relacionada.&lt;br /&gt;Estado&lt;br /&gt;:version: 1.0&lt;br /&gt;Versión actual del objeto.&lt;br /&gt;:change: Versión inicial&lt;br /&gt;Listado de cambios.&lt;br /&gt;:todo: Internacionalización&lt;br /&gt;Un cambio planeado para el objeto.&lt;br /&gt;:status: Versión estable&lt;br /&gt;Estado del objeto.&lt;br /&gt;Autoría&lt;br /&gt;:author: Raul Gonzalez&lt;br /&gt;Autor o autores del objeto.&lt;br /&gt;:organization: Mundo geek&lt;br /&gt;Organización que creó o mantiene el objeto.&lt;br /&gt;:license: GPL&lt;br /&gt;Licencia del objeto.&lt;br /&gt;:contact: zootropo en gmail&lt;br /&gt;Información de contacto del autor.&lt;br /&gt;Para que Epydoc sepa que utilizamos reStructuredText es necesario indicarlo mediante una variable __docformat__ en el código, o bien mediante la opción --docformat de línea de comandos. Las opciones posibles son epytext, plaintext, restructuredtext o javadoc.&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo con campos:&lt;br /&gt;“””Modulo para ejemplificar el uso de *epydoc*.&lt;br /&gt;:author: Raul Gonzalez&lt;br /&gt;:version: 0.1”””&lt;br /&gt;__docformat__ = “restructuredtext”&lt;br /&gt;class Persona:&lt;br /&gt;“””Modela una persona.”””&lt;br /&gt;def __init__(self, nombre, edad):&lt;br /&gt;“””Inicializador de la clase `Persona`.&lt;br /&gt;:param nombre: Nombre de la persona.&lt;br /&gt;:param edad: Edad de la persona”””&lt;br /&gt;self.nombre = nombre&lt;br /&gt;self.edad = edad&lt;br /&gt;self.mostrar_nombre()&lt;br /&gt;def mostrar_nombre(self):&lt;br /&gt;“””Imprime el nombre de la persona”””&lt;br /&gt;print “Esta es la persona %s” % self.nombre&lt;br /&gt;class Empleado(Persona):&lt;br /&gt;“””Subclase de `Persona` correspondiente a las personas&lt;br /&gt;que trabajan para la organizacion.&lt;br /&gt;:todo: Escribir implementacion.”””&lt;br /&gt;pass&lt;br /&gt;if __name__ == “__main__”:&lt;br /&gt;juan = Persona(“Juan”, 26)&lt;br /&gt;reStructuredText también soporta un segundo tipo de campos en el que el cuerpo del campo es una lista. De esta forma podemos, por ejemplo, describir todos los parámetros de una función o método con un solo campo :Parameters:, en lugar de con un campo :param: para cada parámetro.&lt;br /&gt;class Persona:&lt;br /&gt;“””Modela una persona.”””&lt;br /&gt;def __init__(self, nombre, edad):&lt;br /&gt;“””Inicializador de la clase `Persona`.&lt;br /&gt;:Parameters:&lt;br /&gt;- `nombre`: Nombre de la persona.&lt;br /&gt;- `edad`: Edad de la persona.&lt;br /&gt;“””&lt;br /&gt;self.nombre = nombre&lt;br /&gt;self.edad = edad&lt;br /&gt;self.mostrar_nombre()&lt;br /&gt;Python para todos&lt;br /&gt;134&lt;br /&gt;Otros campos que admiten listas son :Exceptions: para indicar varias excepciones (:except:), :Variables: para comentar varias variables (:var:) o :Ivariables: para comentar varias instancias (:ivar:).&lt;br /&gt;&lt;br /&gt;Pruebas&lt;br /&gt;Para asegurar en la medida de lo posible el correcto funcionamiento y la calidad del software se suelen utilizar distintos tipos de pruebas, como pueden ser las pruebas unitarias, las pruebas de integración, o las pruebas de regresión.&lt;br /&gt;A lo largo de este capítulo nos centraremos en las pruebas unitarias, mediante las que se comprueba el correcto funcionamiento de las unidades lógicas en las que se divide el programa, sin tener en cuenta la interrelación con otras unidades.&lt;br /&gt;La solución más extendida para las pruebas unitarias en el mundo Python es unittest, a menudo combinado con doctest para pruebas más sencillas. Ambos módulos están incluídos en la librería estándar de Python.&lt;br /&gt;Doctest&lt;br /&gt;Como es de suponer por el nombre del módulo, doctest permite combinar las pruebas con la documentación. Esta idea de utilizar las pruebas unitarias para probar el código y también a modo de documentación permite realizar pruebas de forma muy sencilla, propicia el que las pruebas se mantengan actualizadas, y sirve a modo de ejemplo de uso del código y como ayuda para entender su propósito.&lt;br /&gt;Cuando doctest encuentra una línea en la documentación que comienza con ‘&gt;&gt;&gt;’ se asume que lo que le sigue es código Python a ejecutar, y que la respuesta esperada se encuentra en la línea o líneas siguientes, sin &gt;&gt;&gt;. El texto de la prueba termina cuando se encuentra una línea en blanco, o cuando se llega al final de la cadena de documentación.&lt;br /&gt;Tomemos como ejemplo la siguiente función, que devuelve una lista con los cuadrados de todos los números que componen la lista pasada como parámetro:&lt;br /&gt;def cuadrados(lista):&lt;br /&gt;“””Calcula el cuadrado de los numeros de una lista”””&lt;br /&gt;return [n ** 2 for n in lista]&lt;br /&gt;Podríamos crear una prueba como la siguiente, en la que comprobamos que el resultado al pasar la lista [0, 1, 2, 3] es el que esperábamos:&lt;br /&gt;def cuadrados(lista):&lt;br /&gt;“””Calcula el cuadrado de los numeros de una lista&lt;br /&gt;&gt;&gt;&gt; l = [0, 1, 2, 3]&lt;br /&gt;&gt;&gt;&gt; cuadrados(l)&lt;br /&gt;[0, 1, 4, 9]&lt;br /&gt;“””&lt;br /&gt;return [n ** 2 for n in lista]&lt;br /&gt;Lo que hacemos en este ejemplo es indicar a doctest que cree un lista l con valor [0, 1, 2, 3], que llame a continuación a la función cuadrados con l como argumento, y que compruebe que el resultado devuelto sea igual a [0, 1, 4, 9].&lt;br /&gt;Para ejecutar las pruebas se utiliza la función testmod del módulo, a la que se le puede pasar opcionalmente el nombre de un módulo a evaluar (parámetro name). En el caso de que no se indique ningún argumento, como en este caso, se evalúa el módulo actual:&lt;br /&gt;def cuadrados(lista):&lt;br /&gt;“””Calcula el cuadrado de los numeros de una lista&lt;br /&gt;&gt;&gt;&gt; l = [0, 1, 2, 3]&lt;br /&gt;&gt;&gt;&gt; cuadrados(l)&lt;br /&gt;[0, 1, 4, 9]&lt;br /&gt;“””&lt;br /&gt;return [n ** 2 for n in lista]&lt;br /&gt;def _test():&lt;br /&gt;import doctest&lt;br /&gt;doctest.testmod()&lt;br /&gt;if __name__ == “__main__”:&lt;br /&gt;_test()&lt;br /&gt;En el caso de que el código no pase alguna de las pruebas que hemos definido, doctest mostrará el resultado obtenido y el resultado esperado. En caso contrario, si todo es correcto, no se mostrará ningún mensaje, a menos que añadamos la opción -v al llamar al script o el parámetro verbose=True a la función tesmod, en cuyo caso se mostrarán todas las pruebas ejecutadas, independientemente de si se ejecutaron con éxito.&lt;br /&gt;Este sería el aspecto de la salida de doctest utilizando el parámetro -v:&lt;br /&gt;Trying:&lt;br /&gt;l = [0, 1, 2, 3]&lt;br /&gt;Expecting nothing&lt;br /&gt;ok&lt;br /&gt;Trying:&lt;br /&gt;cuadrados(l)&lt;br /&gt;Expecting:&lt;br /&gt;[0, 1, 4, 9]&lt;br /&gt;ok&lt;br /&gt;2 items had no tests:&lt;br /&gt;__main__&lt;br /&gt;__main__._test&lt;br /&gt;1 items passed all tests:&lt;br /&gt;2 tests in __main__.cuadrados&lt;br /&gt;2 tests in 3 items.&lt;br /&gt;2 passed and 0 failed.&lt;br /&gt;Test passed.&lt;br /&gt;Ahora vamos a introducir un error en el código de la función para ver el aspecto de un mensaje de error de doctest. Supongamos, por ejemplo, que hubieramos escrito un operador de multiplicación (‘*’) en lugar de uno de exponenciación (‘**’):&lt;br /&gt;def cuadrados(lista):&lt;br /&gt;“””Calcula el cuadrado de los numeros de una lista&lt;br /&gt;&gt;&gt;&gt; l = [0, 1, 2, 3]&lt;br /&gt;&gt;&gt;&gt; cuadrados(l)&lt;br /&gt;[0, 1, 4, 9]&lt;br /&gt;“””&lt;br /&gt;return [n * 2 for n in lista]&lt;br /&gt;def _test():&lt;br /&gt;import doctest&lt;br /&gt;doctest.testmod()&lt;br /&gt;if __name__ == “__main__”:&lt;br /&gt;_test()&lt;br /&gt;Obtendríamos algo parecido a esto:&lt;br /&gt;*********************************************************&lt;br /&gt;File “ejemplo.py”, line 5, in __main__.cuadrados&lt;br /&gt;Failed example:&lt;br /&gt;cuadrados(l)&lt;br /&gt;Expected:&lt;br /&gt;[0, 1, 4, 9]&lt;br /&gt;Got:&lt;br /&gt;[0, 2, 4, 6]&lt;br /&gt;*********************************************************&lt;br /&gt;1 items had failures:&lt;br /&gt;1 of 2 in __main__.cuadrados&lt;br /&gt;***Test Failed*** 1 failures.&lt;br /&gt;Como vemos, el mensaje nos indica que ha fallado la prueba de la línea 5, al llamar a cuadrados(l), cuyo resultado debería ser [0, 1, 4, 9], y sin embargo obtuvimos [0, 2, 4, 6].&lt;br /&gt;Veamos por último cómo utilizar sentencias anidadas para hacer cosas un poco más complicadas con doctest. En el ejemplo siguiente nuestra función calcula el cuadrado de un único número pasado como parámetro, y diseñamos una prueba que compruebe que el resultado es el adecuado para varias llamadas con distintos valores. Las sentencias anidadas comienzan con “...” en lugar de “&gt;&gt;&gt;”:&lt;br /&gt;def cuadrado(num):&lt;br /&gt;“””Calcula el cuadrado de un numero.&lt;br /&gt;&gt;&gt;&gt; l = [0, 1, 2, 3]&lt;br /&gt;&gt;&gt;&gt; for n in l:&lt;br /&gt;... cuadrado(n)&lt;br /&gt;[0, 1, 4, 9]&lt;br /&gt;“””&lt;br /&gt;return num ** 2&lt;br /&gt;def _test():&lt;br /&gt;import doctest&lt;br /&gt;doctest.testmod()&lt;br /&gt;if __name__ == “__main__”:&lt;br /&gt;_test()&lt;br /&gt;unittest / PyUnit&lt;br /&gt;unittest, también llamado PyUnit, forma parte de una familia de herramientas conocida colectivamente como xUnit, un conjunto de frameworks basados en el software SUnit para Smalltalk, creado por Kent Beck, uno de los padres de la eXtreme Programming. Otros ejemplos de herramientas que forman parte de esta familia son JUnit para Java, creada por el propio Kent Beck junto a Erich Gamma, o NUnit, para .NET.&lt;br /&gt;El uso de unittest es muy sencillo. Para cada grupo de pruebas tenemos que crear una clase que herede de unittest.TestCase, y añadir una serie de métodos que comiencen con test, que serán cada una de las pruebas que queremos ejecutar dentro de esa batería de pruebas.&lt;br /&gt;Para ejecutar las pruebas, basta llamar a la función main() del módulo, con lo que se ejecutarán todos los métodos cuyo nombre comience con test, en orden alfanumérico. Al ejecutar cada una de las pruebas el resultado puede ser:&lt;br /&gt;OK• : La prueba ha pasado con éxito.&lt;br /&gt;FAIL• : La prueba no ha pasado con éxito. Se lanza una excepción AssertionError para indicarlo.&lt;br /&gt;ERROR• : Al ejecutar la prueba se lanzó una excepción distinta de AssertionError.&lt;br /&gt;En el siguiente ejemplo, dado que el método que modela nuestra prueba no lanza ninguna excepción, la prueba pasaría con éxito.&lt;br /&gt;import unittest&lt;br /&gt;class EjemploPruebas(unittest.TestCase):&lt;br /&gt;def test(self):&lt;br /&gt;pass&lt;br /&gt;if __name__ == “__main__”:&lt;br /&gt;unittest.main()&lt;br /&gt;En este otro, sin embargo, fallaría:&lt;br /&gt;import unittest&lt;br /&gt;class EjemploPruebas(unittest.TestCase):&lt;br /&gt;def test(self):&lt;br /&gt;raise AssertionError()&lt;br /&gt;if __name__ == “__main__”:&lt;br /&gt;unittest.main()&lt;br /&gt;Nada nos impide utilizar cláusulas if para evaluar las condiciones que nos interesen y lanzar una excepción de tipo AssertionError cuando no sea así, pero la clase TestCase cuenta con varios métodos que nos pueden facilitar la tarea de realizar comprobaciones sencillas. Son los siguientes:&lt;br /&gt;assertAlmostEqual(first, second, places=7, msg=None)• : Comprueba que los objetos pasados como parámetros sean iguales hasta el séptimo decimal (o el número de decimales indicado por places).&lt;br /&gt;assertEqual(first, second, msg=None)• : Comprueba que los objetos pasados como parámetros sean iguales.&lt;br /&gt;assertFalse(expr, msg=None)• : Comprueba que la expresión sea falsa.&lt;br /&gt;assertNotAlmostEqual(first, second, places=7, msg=None)• : Comprueba que los objetos pasados como parámetros no sean iguales hasta el séptimo decimal (o hasta el número de decimales indicado por places).&lt;br /&gt;assertNotEqual(first, second, msg=None)• : Comprueba que los objetos pasados como parámetros no sean iguales.&lt;br /&gt;assertRaises(excClass, callableObj, *args, **kwargs)• : Comprueba que al llamar al objeto callableObj con los parámetros definidos por *args y **kwargs se lanza una excepción de tipo excClass.&lt;br /&gt;assertTrue(expr, msg=None)• : Comprueba que la expresión sea cierta.&lt;br /&gt;assert_(expr, msg=None)• : Comprueba que la expresión sea cierta.&lt;br /&gt;fail(msg=None)• : Falla inmediatamente.&lt;br /&gt;failIf(expr, msg=None)• : Falla si la expresión es cierta.&lt;br /&gt;failIfAlmostEqual(first, second, places=7, msg=None)• : Falla&lt;br /&gt;Pruebas&lt;br /&gt;141&lt;br /&gt;si los objetos pasados como parámetros son iguales hasta el séptimo decimal (o hasta el número de decimales indicado por places).&lt;br /&gt;failIfEqual(first, second, msg=None)• : Falla si los objetos pasados como parámetros son iguales.&lt;br /&gt;failUnless(expr, msg=None)• : Falla a menos que la expresión sea cierta.&lt;br /&gt;failUnlessAlmostEqual(first, second, places=7, msg=None)• : Falla a menos que los objetos pasados como parámetros sean iguales hasta el séptimo decimal (o hasta el número de decimales indicado por places).&lt;br /&gt;failUnlessEqual(first, second, msg=None)• : Falla a menos que los objetos pasados como parámetros sean iguales.&lt;br /&gt;failUnlessRaises(excClass, callableObj, *args, **kwargs)• : Falla a menos que al llamar al objeto callableObj con los parámetros definidos por *args y **kwargs se lance una excepción de tipo excClass.&lt;br /&gt;Como vemos todos los métodos cuentan con un parámetro opcional msg con un mensaje a mostrar cuando dicha comprobación falle.&lt;br /&gt;Retomemos nuestra pequeña función para calcular el cuadrado de un número. Para probar el funcionamiento de la función podríamos hacer, por ejemplo, algo así:&lt;br /&gt;import unittest&lt;br /&gt;def cuadrado(num):&lt;br /&gt;“””Calcula el cuadrado de un numero.”””&lt;br /&gt;return num ** 2&lt;br /&gt;class EjemploPruebas(unittest.TestCase):&lt;br /&gt;def test(self):&lt;br /&gt;l = [0, 1, 2, 3]&lt;br /&gt;r = [cuadrado(n) for n in l]&lt;br /&gt;self.assertEqual(r, [0, 1, 4, 9])&lt;br /&gt;if __name__ == “__main__”:&lt;br /&gt;unittest.main()&lt;br /&gt;Preparación del contexto&lt;br /&gt;En ocasiones es necesario preparar el entorno en el que queremos que se ejecuten las pruebas. Por ejemplo, puede ser necesario introducir unos valores por defecto en una base de datos, crear una conexión con una máquina, crear algún archivo, etc. Esto es lo que se conoce en el mundo de xUnit como test fixture.&lt;br /&gt;La clase TestCase proporciona un par de métodos que podemos sobreescribir para construir y desconstruir el entorno y que se ejecutan antes y después de las pruebas definidas en esa clase. Estos métodos son setUp() y tearDown().&lt;br /&gt;class EjemploFixture(unittest.TestCase):&lt;br /&gt;def setUp(self):&lt;br /&gt;print “Preparando contexto”&lt;br /&gt;self.lista = [0, 1, 2, 3]&lt;br /&gt;def test(self):&lt;br /&gt;print “Ejecutando prueba”&lt;br /&gt;r = [cuadrado(n) for n in self.lista]&lt;br /&gt;self.assertEqual(r, [0, 1, 4, 9])&lt;br /&gt;def tearDown(self):&lt;br /&gt;print “Desconstruyendo contexto”&lt;br /&gt;del self.lista&lt;br /&gt;&lt;br /&gt;Distribuir aplicaciones Python&lt;br /&gt;Una vez terminemos con el desarrollo de nuestra nueva aplicación es conveniente empaquetarla de forma que sea sencillo para los usuarios instalarla, y para nosotros distribuirla.&lt;br /&gt;En Python existen dos módulos principales para este cometido: distutils, que es parte de la librería estándar y era el método más utilizado hasta hace poco, y setuptools, que extiende la funcionalidad de distutils y es cada vez más popular.&lt;br /&gt;En este capítulo veremos el funcionamiento de ambas herramientas, y terminaremos explicando cómo crear ejecutables .exe para Windows a partir de nuestro programa en Python.&lt;br /&gt;distutils&lt;br /&gt;Todo programa distribuido con distutils contiene un script llamado por convención setup.py, que se encarga de instalar la aplicación llamando a la función setup de distutils.core. Esta función tiene montones de argumentos, que controlan, entre otras cosas, cómo instalar la aplicación.&lt;br /&gt;Destinados a describir la aplicación tenemos los siguientes argumentos:&lt;br /&gt;Python para todos&lt;br /&gt;144&lt;br /&gt;name• : El nombre del paquete.&lt;br /&gt;version• : El número de versión.&lt;br /&gt;description• : Una línea describiendo el paquete.&lt;br /&gt;long_description• : Descripción completa del paquete.&lt;br /&gt;author• : Nombre del autor de la aplicación.&lt;br /&gt;author_email• : Correo electrónico del autor.&lt;br /&gt;maintainer• : Nombre de la persona encargada de mantener el paquete, si difiere del autor.&lt;br /&gt;maintainer_email• : Correo de la persona encargada de mantener el paquete, si difiere del autor.&lt;br /&gt;url• : Web de la aplicación.&lt;br /&gt;download_url• : Url de la que descargar la aplicación.&lt;br /&gt;license• : Licencia de la aplicación&lt;br /&gt;También tenemos argumentos que controlan los archivos y directorios que deben instalarse, como son packages, py_modules, scripts y ext_modules.&lt;br /&gt;El parámetro scripts, que es una lista de cadenas, indica el nombre del módulo o módulos principales, es decir, los que ejecuta el usuario. Si nuestra aplicación consistiera, por ejemplo, en un solo script ejemplo.py, el código de setup.py podría tener un aspecto similar al siguiente:&lt;br /&gt;from distutils.core import setup&lt;br /&gt;setup(name=”Aplicacion de ejemplo”,&lt;br /&gt;version=”0.1”,&lt;br /&gt;description=”Ejemplo del funcionamiento de distutils”,&lt;br /&gt;author=”Raul Gonzalez”,&lt;br /&gt;author_email=”zootropo en gmail”,&lt;br /&gt;url=”http://mundogeek.net/tutorial-python/”,&lt;br /&gt;license=”GPL”,&lt;br /&gt;scripts=[“ejemplo.py”]&lt;br /&gt;)&lt;br /&gt;Si hemos escrito otros módulos para ser utilizados por el script principal, estos se indican mediante el parámetro py_modules. Por ejemplo, supongamos que la aplicación consiste en un script principal ejemplo.py, y un módulo de apoyo apoyo.py:&lt;br /&gt;from distutils.core import setup&lt;br /&gt;setup(name=”Aplicacion de ejemplo”,&lt;br /&gt;version=”0.1”,&lt;br /&gt;description=”Ejemplo del funcionamiento de distutils”,&lt;br /&gt;author=”Raul Gonzalez”,&lt;br /&gt;author_email=”zootropo en gmail”,&lt;br /&gt;url=”http://mundogeek.net/tutorial-python/”,&lt;br /&gt;license=”GPL”,&lt;br /&gt;scripts=[“ejemplo.py”],&lt;br /&gt;py_modules=[“apoyo”]&lt;br /&gt;)&lt;br /&gt;Para instalar paquetes Python (directorios que contienen varios módulos y un archivo __init__.py) usaríamos el parámetro packages. Si además del módulo ejemplo.py quisiéramos instalar los paquetes gui y bbdd, por ejemplo, haríamos algo así:&lt;br /&gt;from distutils.core import setup&lt;br /&gt;setup(name=”Aplicacion de ejemplo”,&lt;br /&gt;version=”0.1”,&lt;br /&gt;description=”Ejemplo del funcionamiento de distutils”,&lt;br /&gt;author=”Raul Gonzalez”,&lt;br /&gt;author_email=”zootropo en gmail”,&lt;br /&gt;url=”http://mundogeek.net/tutorial-python/”,&lt;br /&gt;license=”GPL”,&lt;br /&gt;scripts=[“ejemplo.py”],&lt;br /&gt;packages=[“gui”, “bbdd”]&lt;br /&gt;)&lt;br /&gt;ext_modules, por último, sirve para incluir extensiones que utilice el programa, en C, C++, Fortran, …&lt;br /&gt;Veamos ahora cómo se utilizaría el archivo setup.py una vez creado.&lt;br /&gt;Al ejecutar el comando&lt;br /&gt;python setup.py install&lt;br /&gt;los módulos y paquetes especificados por py_modules y packages se instalan en el directorio Lib de Python. Los programas indicados en scripts, se copian al directorio Scripts de Python.&lt;br /&gt;Una vez hemos comprobado que la aplicación se instala correctamente, procedemos a crear archivos mediante los que distribuir la aplicación&lt;br /&gt;a los usuarios. Para crear archivos con el código fuente se utiliza la opción sdist de setup.py, que crea por defecto un archivo tar.gz en Unix y un zip en Windows.&lt;br /&gt;python setup.py sdist&lt;br /&gt;Sin embargo se puede utilizar --formats para especificar el formato o formatos que queramos generar&lt;br /&gt;bztar&lt;br /&gt;.tar.bz2&lt;br /&gt;gztar&lt;br /&gt;.tar.gz&lt;br /&gt;tar&lt;br /&gt;.tar&lt;br /&gt;zip&lt;br /&gt;.zip&lt;br /&gt;ztar&lt;br /&gt;.tar.Z&lt;br /&gt;Para crear un archivo tar.bz2, un tar.gz y un zip, por ejemplo, se utilizaría la siguiente orden:&lt;br /&gt;python setup.py sdist --formats=bztar,gztar,zip&lt;br /&gt;Para generar un archivo de distribución binaria, se usa la opción bdist:&lt;br /&gt;python setup.py bdist&lt;br /&gt;Los formatos que soporta bdist son los siguientes:&lt;br /&gt;rpm&lt;br /&gt;RPM&lt;br /&gt;gztar&lt;br /&gt;.tar.gz&lt;br /&gt;bztar&lt;br /&gt;.tar.bz2&lt;br /&gt;ztar&lt;br /&gt;.tar.Z&lt;br /&gt;tar&lt;br /&gt;.tar&lt;br /&gt;wininst&lt;br /&gt;Instalador Windows&lt;br /&gt;zip&lt;br /&gt;.zip&lt;br /&gt;Para crear un archivo rpm y un instalador de Windows, por ejemplo, escribiríamos:&lt;br /&gt;python setup.py bdist --formats=wininst,rpm&lt;br /&gt;También es posible crear otros tipos de archivos de distribución utilizando scripts que extienden distutils, como es el caso de los paquetes deb mediante el script stdeb (http://stdeb.python-hosting.com/)&lt;br /&gt;setuptools&lt;br /&gt;setuptools extediende distutils añadiendo una serie de funcionalidades muy interesantes: introduce un nuevo formato de archivo para distribución de aplicaciones Python llamado egg, se encarga de buscar todos los paquetes que deben instalarse y añadir las posibles dependencias, permite instalar paquetes de PyPI con un solo comando, etc.&lt;br /&gt;Además, como setuptools se basa en distutils, un script de instalación básico utilizando setuptools es prácticamente igual a su equivalente con distutils. Tan sólo cambiaría la sentencia de importación.&lt;br /&gt;from setuptools import setup&lt;br /&gt;setup(name=”Aplicacion de ejemplo”,&lt;br /&gt;version=”0.1”,&lt;br /&gt;description=”Ejemplo del funcionamiento de distutils”,&lt;br /&gt;author=”Raul Gonzalez”,&lt;br /&gt;author_email=”zootropo en gmail”,&lt;br /&gt;url=”http://mundogeek.net/tutorial-python/”,&lt;br /&gt;license=”GPL”,&lt;br /&gt;scripts=[“ejemplo.py”],&lt;br /&gt;)&lt;br /&gt;El único inconveniente que podríamos encontrar al uso de setuptools es que no está incluido por defecto en Python 2.5, aunque es probable que esto cambie en próximas versiones debido a su gran uso. Pero los desarrolladores de setuptools han pensado en todo, e incluso esto no debería suponer ningún problema, ya que con un mínimo esfuerzo por nuestra parte podemos hacer que setuptools se descargue e instale automáticamente en la máquina del usuario si este no se encuentra ya en el sistema. Basta distribuir con nuestro paquete un pequeño módulo extra ez_setup.py que viene incluido por defecto con setuptools (http://peak.telecommunity.com/dist/ez_setup.py) y llamar a la función use_setuptools del módulo al inicio de setup.py:&lt;br /&gt;from ez_setup import use_setuptools&lt;br /&gt;use_setuptools()&lt;br /&gt;from setuptools import setup&lt;br /&gt;setup(name=”Aplicacion de ejemplo”,&lt;br /&gt;version=”0.1”,&lt;br /&gt;description=”Ejemplo del funcionamiento de distutils”,&lt;br /&gt;author=”Raul Gonzalez”,&lt;br /&gt;author_email=”zootropo en gmail”,&lt;br /&gt;url=”http://mundogeek.net/tutorial-python/”,&lt;br /&gt;license=”GPL”,&lt;br /&gt;scripts=[“ejemplo.py”],&lt;br /&gt;)&lt;br /&gt;Veamos ahora con más detenimiento algunos de los cambios y novedades que introduce setuptools.&lt;br /&gt;Integración con PyPI&lt;br /&gt;Al estilo de CPAN en Perl setuptools permite instalar de forma fácil y sencilla los paquetes pertenecientes a PyPI, el Índice de Paquetes Python (http://pypi.python.org/pypi), así como subir nuestros propios paquetes.&lt;br /&gt;PyPI cuenta en el momento de escribir estas líneas con 4782 paquetes, por lo que poder instalar los paquetes de este repositorio con un simple comando supone una ayuda muy a tener en cuenta.&lt;br /&gt;Instalar un paquete de PyPI es tan sencillo como pasar al comando easy_install el nombre del paquete a instalar&lt;br /&gt;easy_install docutils&lt;br /&gt;Searching for docutils&lt;br /&gt;Reading http://pypi.python.org/simple/docutils/&lt;br /&gt;Reading http://docutils.sourceforge.net/&lt;br /&gt;Best match: docutils 0.5&lt;br /&gt;Downloading http://prdownloads.sourceforge.net/docutils/docutils-0.5.tar.gz?download&lt;br /&gt;Processing docutils-0.5.tar.gz&lt;br /&gt;Running docutils-0.5/setup.py -q bdist_egg --dist-dir /tmp/easy_install-wUAyUZ/docutils-0.5/egg-dist-tmp-kWkkkv&lt;br /&gt;“optparse” module already present; ignoring extras/optparse.py.&lt;br /&gt;“textwrap” module already present; ignoring extras/textwrap.py.&lt;br /&gt;zip_safe flag not set; analyzing archive contents…&lt;br /&gt;docutils.writers.newlatex2e.__init__: module references __file__&lt;br /&gt;docutils.writers.pep_html.__init__: module references __file__&lt;br /&gt;docutils.writers.html4css1.__init__: module references __file__&lt;br /&gt;docutils.writers.s5_html.__init__: module references __file__&lt;br /&gt;docutils.parsers.rst.directives.misc: module references __file__&lt;br /&gt;Adding docutils 0.5 to easy-install.pth file&lt;br /&gt;Installing rst2pseudoxml.py script to /usr/bin&lt;br /&gt;Installing rst2html.py script to /usr/bin&lt;br /&gt;Installing rst2latex.py script to /usr/bin&lt;br /&gt;Installing rst2s5.py script to /usr/bin&lt;br /&gt;Installing rst2newlatex.py script to /usr/bin&lt;br /&gt;Installing rstpep2html.py script to /usr/bin&lt;br /&gt;Installing rst2xml.py script to /usr/bin&lt;br /&gt;Installed /usr/lib/python2.5/site-packages/docutils-0.5-py2.5.egg&lt;br /&gt;Processing dependencies for docutils&lt;br /&gt;Finished processing dependencies for docutils&lt;br /&gt;Poder subir nuestros paquetes a PyPI requiere de un proceso un poco más laborioso. Primero registramos los detalles de nuestra aplicación en PyPI mediante la opción register del script setup.py, el cuál nos preguntará por nuestro nombre de usuario, contraseña y correo electrónico si no tenemos cuenta en PyPI, o nombre de usuario y contraseña si nos registramos anteriormente:&lt;br /&gt;python setup.py register&lt;br /&gt;running register&lt;br /&gt;running egg_info&lt;br /&gt;creating Aplicacion_de_ejemplo.egg-info&lt;br /&gt;writing Aplicacion_de_ejemplo.egg-info/PKG-INFO&lt;br /&gt;writing top-level names to Aplicacion_de_ejemplo.egg-info/top_level.txt&lt;br /&gt;writing dependency_links to Aplicacion_de_ejemplo.egg-info/dependency_links.txt&lt;br /&gt;writing manifest file ‘Aplicacion_de_ejemplo.egg-info/SOURCES.txt’&lt;br /&gt;reading manifest file ‘Aplicacion_de_ejemplo.egg-info/SOURCES.txt’&lt;br /&gt;writing manifest file ‘Aplicacion_de_ejemplo.egg-info/SOURCES.txt’&lt;br /&gt;We need to know who you are, so please choose either:&lt;br /&gt;1. use your existing login,&lt;br /&gt;2. register as a new user,&lt;br /&gt;3. have the server generate a new password for you (and email it to you), or&lt;br /&gt;4. quit&lt;br /&gt;Your selection [default 1]: 1&lt;br /&gt;Username: zootropo&lt;br /&gt;Password:&lt;br /&gt;Server response (200): OK&lt;br /&gt;I can store your PyPI login so future submissions will be faster.&lt;br /&gt;(the login will be stored in /home/zootropo/.pypirc)&lt;br /&gt;Save your login (y/N)?y&lt;br /&gt;Para crear y subir una distribución con el código fuente de nuestra aplicación se utiliza la opción sdist upload:&lt;br /&gt;python setup.py sdist upload&lt;br /&gt;También podríamos crear y subir un egg (un formato de archivo para distribuir aplicaciones Python que veremos en la próxima sección) utilizando la opción bdist_egg upload:&lt;br /&gt;python setup.py bdist_egg upload&lt;br /&gt;O combinar los tres pasos en un solo comando:&lt;br /&gt;python setup.py register sdist bdist_egg upload&lt;br /&gt;Una vez subido el paquete cualquier persona podría instalarlo en su sistema utilizando easy_install, de la misma forma que cualquier otro paquete de PyPI:&lt;br /&gt;easy_install mi-paquete&lt;br /&gt;Eggs&lt;br /&gt;Los eggs (huevo en inglés) son archivos de extensión .egg mediante los que distribuir aplicaciones en Python. Serían algo así como el equivalente a los archivos .jar del mundo Java. Son multiplataforma, permiten manejar dependencias, y permiten instalar distintas versiones del mismo paquete.&lt;br /&gt;La forma más sencilla de instalar aplicaciones distribuidas como archivos egg es mediante el comando easy_install, el cuál comentamos brevemente en el punto anterior al hablar sobre su uso para instalar paquetes de PyPI. Para instalar un archivo egg no tenemos más que pasarle el nombre del archivo al comando easy_install:&lt;br /&gt;easy_install mi-aplicacion.egg&lt;br /&gt;o bien podemos pasarle la URL de la que descargar el egg:&lt;br /&gt;easy_install http://mundogeek.net/mi-aplicacion.egg&lt;br /&gt;Para construir nuestros propios eggs podemos utilizar el comando bdist_egg de setup.py, de forma similar a la manera en que construíamos paquetes RPM o instaladores para Windows con distutils:&lt;br /&gt;python setup.py bdist_egg&lt;br /&gt;Otros cambios destacables&lt;br /&gt;Uno de los cambios más interesantes es la incorporación de un nuevo argumento para la función setup llamado install_requires, que consiste en una cadena o lista de cadenas que indica los paquetes de los que depende la aplicación. Si nuestra aplicación necesitara tener instalado el paquete apoyo para poder ejecutarse, por ejemplo, escribiríamos lo siguiente:&lt;br /&gt;install_requires = [“apoyo”]&lt;br /&gt;Y de esta forma, easy_install se encargaría de buscar e instalar el paquete si fuera necesario, bien en PyPI, o en cualquier otro repositorio indicado por el parámetro dependency_links.&lt;br /&gt;Además podemos especificar que se necesita una versión concreta del paquete requerido, que sea mayor o menor que una cierta versión, o que no se trate de una versión determinada utilizando operadores relacionales (==, !=, &lt;, &lt;=, &gt;, &gt;=):&lt;br /&gt;install_requires = [“apoyo &gt;= 1.0 &lt; 2.0”]&lt;br /&gt;También existen argumentos similares para declarar paquetes que deben instalarse para poder ejecutar el script de instalación (setup_requires), para poder ejecutar las posibles pruebas incluídas con el paquete (tests_require) y para conseguir funcionalidades adicionales&lt;br /&gt;(extras_require, que consiste en este caso en un diccionario).&lt;br /&gt;setuptools incluye también atajos útiles, como la función find_packages() que nos evita tener que listar todos y cada uno de los paquetes que utiliza nuestro script en el parámetro packages, como era el caso de distutils:&lt;br /&gt;from ez_setup import use_setuptools&lt;br /&gt;use_setuptools()&lt;br /&gt;from setuptools import setup, find_packages&lt;br /&gt;setup(name=”Aplicacion de ejemplo”,&lt;br /&gt;version=”0.1”,&lt;br /&gt;description=”Ejemplo del funcionamiento de distutils”,&lt;br /&gt;author=”Raul Gonzalez”,&lt;br /&gt;author_email=”zootropo en gmail”,&lt;br /&gt;url=”http://mundogeek.net/tutorial-python/”,&lt;br /&gt;license=”GPL”,&lt;br /&gt;scripts=[“ejemplo.py”],&lt;br /&gt;packages = find_packages()&lt;br /&gt;)&lt;br /&gt;Crear ejecutables .exe&lt;br /&gt;Tanto en Mac OS como en la mayor parte de las distribuciones Linux el intérprete de Python está instalado por defecto, por lo que los usuarios de estos sistemas no tienen mayor complicación a la hora de instalar y ejecutar aplicaciones escritas en Python.&lt;br /&gt;En el caso de Windows, esto no es así, por lo que sería interesante que los usuarios de este sistema operativo no tuvieran que instalar el intérprete de Python. También sería interesante que nuestro programa consistiera en un archivo .exe en lugar de uno o varios archivos .py, para simplificar las cosas.&lt;br /&gt;Todo esto lo podemos lograr gracias a py2exe, una extensión para distutils que, como su nombre indica, permite crear ejecutables para Windows a partir de código Python, y que permite ejecutar estas aplicaciones sin necesidad de tener instalado el intérprete de Python en el sistema.&lt;br /&gt;Py2exe funciona examinando nuestro código fuente en busca de los módulos y paquetes que utilizamos, compilándolos y construyendo un nuevo archivo que incluye estos archivos y un pequeño intérprete de Python integrado.&lt;br /&gt;Para probar el funcionamiento de py2exe creemos un pequeño programa ejemplo.py&lt;br /&gt;print “Soy un .exe”&lt;br /&gt;y el archivo setup.py correspondiente. Los cambios que tenemos que realizar a setup.py son sencillos: importar py2exe, y utilizar los argumentos console y windows para indicar el nombre del script o scripts que queramos convertir en ejecutables de consola o ejecutables de interfaz gráfica, respectivamente.&lt;br /&gt;from distutils.core import setup&lt;br /&gt;import py2exe&lt;br /&gt;setup(name=”Aplicacion de ejemplo”,&lt;br /&gt;version=”0.1”,&lt;br /&gt;description=”Ejemplo del funcionamiento de distutils”,&lt;br /&gt;author=”Raul Gonzalez”,&lt;br /&gt;author_email=”zootropo en gmail”,&lt;br /&gt;url=”http://mundogeek.net/tutorial-python/”,&lt;br /&gt;license=”GPL”,&lt;br /&gt;scripts=[“ejemplo.py”],&lt;br /&gt;console=[“ejemplo.py”]&lt;br /&gt;)&lt;br /&gt;Para crear el ejecutable, utilizamos una nueva opción de línea de comandos para setup.py disponible tras importar el módulo y llamada, cómo no, py2exe:&lt;br /&gt;python setup.py py2exe&lt;br /&gt;Con esto py2exe generará un directorio build, con las librerías compiladas, y un directorio dist, con los archivos que conforman nuestra aplicación.&lt;br /&gt;Entre los archivos que podemos encontrar en dist tendremos uno o varios ejecutables con el mismo nombre que los scripts indicados en console y windows, un archivo python*.dll, que es el intérprete de Python, y un archivo library.zip, que contiene varios archivos pyc que&lt;/sqlite3.connection&gt;&lt;/type&gt;&lt;/generator&gt;&lt;/generator&gt;&lt;/module&gt;&lt;/stdin&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5539267871087722416-5795271302840185033?l=wifi4.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/5795271302840185033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/5795271302840185033'/><link rel='alternate' type='text/html' href='http://wifi4.blogspot.com/2009/11/programacion-en-python_29.html' title='Programacion en python'/><author><name>Wifi4.0</name><uri>http://www.blogger.com/profile/16699432101663038724</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5539267871087722416.post-5029814604016945014</id><published>2009-11-29T02:37:00.000-08:00</published><updated>2009-11-29T02:41:53.924-08:00</updated><title type='text'>Breve introduccion a clipper</title><content type='html'>&lt;p style="color: rgb(255, 255, 255);"&gt;&lt;a href="http://2.bp.blogspot.com/_IlKwMMnZNV8/SwGu2gpEsLI/AAAAAAAAAAk/m4UZFBJgABo/s1600/ClipperLeft-full.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5404793279319683250" style="margin: 0px auto 10px; display: block; width: 202px; height: 215px; text-align: center;" alt="" src="http://2.bp.blogspot.com/_IlKwMMnZNV8/SwGu2gpEsLI/AAAAAAAAAAk/m4UZFBJgABo/s320/ClipperLeft-full.jpg" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;•&lt;span style="color: rgb(255, 0, 0);"&gt; Paradigma:&lt;/span&gt;Programación Estructurada - Procedural&lt;br /&gt;• &lt;span style="color: rgb(255, 0, 0);"&gt;Apareció en:&lt;/span&gt;1985&lt;br /&gt;• &lt;span style="color: rgb(255, 0, 0);"&gt;Diseñado por:&lt;/span&gt;Nantucket Corporation&lt;br /&gt;• &lt;span style="color: rgb(255, 0, 0);"&gt;Desarrollador:&lt;/span&gt;Nantucket Corporation&lt;br /&gt;• &lt;span style="color: rgb(255, 0, 0);"&gt;Última versión:&lt;/span&gt;CA Clipper 5.3b (20 de mayo de 1997)&lt;br /&gt;• &lt;span style="color: rgb(255, 0, 0);"&gt;Tipo de dato:&lt;/span&gt;Dinámico&lt;br /&gt;•&lt;span style="color: rgb(255, 0, 0);"&gt; Influido por:&lt;/span&gt;xBase, Fox y C&lt;br /&gt;• &lt;span style="color: rgb(255, 0, 0);"&gt;Sistema operativo:&lt;/span&gt;MS-DOS y Windows&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;   Clipper es el nombre comercial de un producto que era un compilador del lenguaje empleado por la base de datos dBase. Durante un tiempo se utilizó mucho para desarrollar aplicaciones de gestión de pequeño tamaño bajo MsDos, pero hoy en día casi no se emplea, al no existir un "sucesor" claro para Windows (las dos herramientas más utilizadas en su lugar son Delphi y Visual Basic).&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 255, 255);font-family:times new roman;font-size:130%;"  &gt;Aun así, existe un proyecto para crear un Clipper de libre distribución, llamado Harbour. Hay más información en http://www.harbour-project.org/&lt;br /&gt;Clipper es un manejador de archivos con ciertas características de un manejador de Base de Datos y surge como la necesidad de suplir algunas deficiencias del DBASE, siendo la mas resaltante la de ser un Compilador, motivo éste por el cual se acelera la ejecución de un programa escrito en DBASE. Provee además un conjunto de órdenes y funciones propias que han sido desarrolladas con el paso de los años convirtiéndolo en el Manejador de Archivos más popular.&lt;br /&gt;&lt;br /&gt;En cuanto a los requerimientos de Hardware Clipper es poco exigente, sin embargo se recomienda que el equipo tenga Disco Duro y por lo menos un Mega de memoria Ram. Sin embargo no es lo mismo cuando se desarrollan aplicaciones ya que los requerimientos dependen en sí más de la aplicación que de Clipper.&lt;br /&gt;De la misma forma en entorno de Software necesario es mínimo, Sistema operativo DOS, y un config.sys con Buffers no menores a 20 y Files no menores de 40.&lt;br /&gt;Breve Historia&lt;br /&gt;Clipper es un lenguaje de programación procedural e imperativo creado en 1985 por Nantucket Corporation y vendido posteriormente a Computer Associates, la que lo comercializó como CA-Clipper. En un principio Clipper se creó como un compilador para el sistema gestor intérprete de bases de datos dBase III (de hecho las versiones estacionales de Nantucket incluían una etiqueta que lo indicaba así), pero con el tiempo el producto evolucionó y maduró, convirtiéndose en un lenguaje compilado más poderoso que el original, no sólo por sus propias implementaciones sino también por las ampliaciones desarrolladas por terceros en C, Ensamblador y Pascal, de los que fue heredando características. Esto lo convirtió en la herramienta líder de desarrollo de aplicaciones de bases de datos relacionales bajo sistema operativo MS-DOS, sobre todo programas de gestión, contabilidad y facturación (SAGE-SP, líder del mercado español, lo usa para ContaPlus y FacturaPlus), agendas comerciales y programas de tarificación (aproximadamente el 80% de las compañías de seguros de España lo utilizaron en los programas de sus agentes).&lt;br /&gt;Características&lt;br /&gt;A diferencia de otros lenguajes xBase, Clipper nunca contó con un modo intérprete, similar al de dBase. Sus utilidades para manejo de base de datos, tales como la de creación de tablas (DBU), se entregaban con el código fuente escrito en Clipper e incluido, el usuario podía adaptarlas a sus necesidades si quería. Se aportaban también muchas rutinas escritas en C y Ensamblador a las que el usuario podía acudir, incluso ampliar y crear bibliotecas de pre-enlace completas.&lt;br /&gt;Clipper trabaja en modo compilador puro generando un código objeto binario; el paquete proveía también un enlazador (RTLINK o DLINK) que con el módulo objeto y las librerías de pre-enlace generaba un módulo ejecuble directo. Esto último le otorgaba a las aplicaciones Clipper una velocidad que otros manejadores de bases de datos no poseían, y, como desventaja, la necesidad de recompilar y enlazar nuevamente cada vez que se corregía algún error en el código fuente (la depuración era lenta).&lt;br /&gt;Posee características que fueron muy atractivas para su época y su entorno de trabajo (DOS), tales como: manejo propio de memoria virtual (RAM en disco); manejo de memoria extendida, las aplicaciones podían superar la barrera de los 640Kb de RAM impuesta por MS-DOS; rutinas y librerías pueden cargarse sólo cuando son necesarias y se descargan de RAM cuando ya no hacen falta (enlace y overlays dinámicos); la cantidad de registros por tablas estaba sólo limitada a la capacidad del disco; gran robustez en las aplicaciones, particularmente en las diseñadas para cliente-servidor (red LAN), etc.&lt;br /&gt;Si bien no poseía prácticamente funciones de cálculo tales como las trigonométricas, que otros lenguajes como FoxPro si incorporaron; el usuario las podía fácilmente elaborar en C y utilizarlas como cualquier otra función de librería propia del paquete, ventaja que devenía de que el compilador Clipper y muchas de sus librerías estaban casi completamente desarrolladas en C.&lt;br /&gt;Su forma, administración, almacenamiento e intercambio de pantallas era sencillo, efectivo y veloz; lo que otorgaba buen dinamismo a las aplicaciones desarrolladas con Clipper.&lt;br /&gt;El lenguaje en si era poderoso, contando con una gran cantidad de sentencias, funciones, administración de memoria y variables que permitían al programador desarrollos muy flexibles y eficientes; en forma razonablemente rápida. También el paquete incluía un completo "manual en línea navegable", que se cargaba en memoria RAM, a requerimiento del programador, y se accedía por la sola presión de un par de teclas.&lt;br /&gt;En su larga época dorada, ha sido, probablemente, el lenguaje gestor de bases de datos relacionales de "bajo y mediano porte" más utilizado en el mundo. Aun hoy existen muchos desarrolladores Clipper (algunos agrupados comunidades y con foros en Internet), que elaboran aplicaciones, incluso estilo Windows, usando sus propias librerías gráficas escritas en C y Ensamblador.&lt;br /&gt;Sin Compararlo con DBASE podríamos decir qué sus características son:&lt;br /&gt;- Compilador Profesional.&lt;br /&gt;- Un conjunto de órdenes y Funciones que permiten un mejor y más fácil manejo de la programación.&lt;br /&gt;- Permite programación por capas, no existiendo por lo tanto límite para la dimensión de un programa.&lt;br /&gt;- Permite la definición de funciones por parte del usuario.&lt;br /&gt;- Puede conectarse con rutinas externas de otros lenguajes.&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5539267871087722416-5029814604016945014?l=wifi4.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/5029814604016945014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/5029814604016945014'/><link rel='alternate' type='text/html' href='http://wifi4.blogspot.com/2009/11/breve-introduccion-clipper_8490.html' title='Breve introduccion a clipper'/><author><name>Wifi4.0</name><uri>http://www.blogger.com/profile/16699432101663038724</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_IlKwMMnZNV8/SwGu2gpEsLI/AAAAAAAAAAk/m4UZFBJgABo/s72-c/ClipperLeft-full.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-5539267871087722416.post-4564279430818223895</id><published>2009-11-29T02:31:00.000-08:00</published><updated>2009-11-29T02:32:36.867-08:00</updated><title type='text'>PROGRAMACION EN CLIPPER</title><content type='html'>&lt;span style="font-size:180%;"&gt;                                      INTRODUCCION&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: times new roman;font-size:130%;" &gt;A principio de los años ochenta, DBASE II hizo su aparición de la mano de George Tate (1943-1984) y su empresa Ashton-Tate. Esta nueva herramienta se presentaba en el emergente mundo de los microordenadores con la intención de facilitar la gestión de las bases de datos.&lt;br /&gt;&lt;br /&gt;Evidentemente, los sistemas de gestión de bases de datos existían desde mucho antes, sobre todo, desarrollados para grandes sistemas, pero la cuestión estaba en cubrir una carencia que más tarde o más temprano debía ser atendida por los ingenieros de software y que era esperada ansiosamente por el creciente número de usuarios de los ordenadores personales.&lt;br /&gt;&lt;br /&gt;El sistema de gestión de bases de datos había que diseñarse no exclusivamente como un entorno de programación, semejante a otros entornos o lenguajes con capacidad de tratamiento de grandes masas de datos. Este debía posibilitar la ejecución interactiva de instrucciones, ser amigable, accesible por usuarios no programadores, y debía estar formado por unas instrucciones potentes y fáciles de memorizar.&lt;br /&gt;&lt;br /&gt;(LA PRIMERA DE LAS VERSIONES DE DBASE II SE UTILIZÓ CON EL SISTEMA OPERATIVO CP/M, SIGUIÉNDOLE OTRAS COMO LA 2.4 DE SEPTIEMBRE DE 1983 BAJO DOS 1.1 Y 2.0).&lt;br /&gt;&lt;br /&gt;También, a principio de los ochenta se comienza a utilizar entre los usuarios de micros una nueva terminología informática de bases de datos, ésta era más familiar en otros ambientes informáticos y definía con precisión los conceptos más básicos:&lt;br /&gt;&lt;br /&gt;   * Una base de datos puede definirse como la agrupación útil y organizada de información.&lt;br /&gt;   * Bases de datos relacionales. Este tipo de estructura define relaciones entre los datos en una base de datos. Un modelo simple organiza la base de datos de igual forma que podemos definir una tabla de dos dimensiones (filas y columnas). Los datos de una fila (registro) se subdividen en columnas (campos). A cada fila se la asigna un número (nº de registro) que representa el orden en que será almacenado el registro en la base de datos. A las distintas columnas se le asignará un nombre de campo. Con esta estructura básica de base de datos era fácil manipular y actualizar gran cantidad de información.&lt;br /&gt;&lt;br /&gt;Es fácil distinguir los componentes básicos de una base de datos:&lt;br /&gt;&lt;br /&gt;Su estructura es descrita por un conjunto de nombres de campos, estos campos pueden ser de varios tipos en función del dato a almacenar (números, fechas, etc) y de longitud definible.&lt;br /&gt;&lt;br /&gt;Otro componente son los datos propiamente dichos.&lt;br /&gt;&lt;br /&gt;   * Los gestores de bases de datos permiten la organizacíón y el tratamiento eficaz de grandes masas de datos proporcionándonos gran variedad de herramientas.&lt;br /&gt;&lt;br /&gt;DBASE II proporciona un gestor de base de datos de tipo relacional con capacidad para gestionar las bases de datos, interpretar interactivamente instrucciones y ejecutar bloques de sentencias (programas).&lt;br /&gt;&lt;br /&gt;DBASE II también contribuyó a la filosofía de la programación estructurada, mejoró sus prestaciones y evolucionó en varias versiones (DBASE III, DBASE III+ y DBASE IV).&lt;br /&gt;&lt;br /&gt;George Tate fallecido tempranamente nunca pudo comprobar la revolución que ocasionaría este producto, aún en constante evolución.&lt;br /&gt;&lt;br /&gt;El éxito obtenido entre los usuarios de micros, principalmente atraídos por su versatilidad y potencia, y los grandes beneficios producidos en su comercialización, hizo que muchas empresas de software se adherieran a la idea de desarrollar nuevos productos análogos, una gama de dialectos que hoy se les agrupa con el sobrenombre de entorno xBase (Clipper, Quicksilver, Foxbase, etc).&lt;br /&gt;&lt;br /&gt;La difusión de estos productos han desbancado a muchos lenguajes de programación, como al Cobol que aunque propicia una fácil lectura de sus fuentes, la programación resulta lenta y laboriosa.&lt;br /&gt;&lt;br /&gt;En los ochenta, en pleno boom informático DBASE sustituye a muchos lenguajes por la potencia de sus órdenes y facilidad de uso. Por entonces, hubo que estar muy despierto a la hora de seleccionar una herramienta de trabajo con futuro.&lt;br /&gt;&lt;br /&gt;CLIPPER es un dialecto creado como otros tantos con la intención de mejorar las prestaciones de DBASE. Su primera versión se creó en 1985 en los laboratorios de Natuncket. CLIPPER está escrito en lenguaje C y Ensamblador y se presentó como un lenguaje atrevido que ha dado muchos quebraderos de cabeza en Ashthon-Tate. En el primer contacto que se tiene con él es dificil encontrar muchas diferencias con respecto a DBASE, ya que CLIPPER es un lenguaje formado por un conjunto de comandos y funciones similares a las usadas con DBASE, incluso la mayoría con igual formato sintáctico.&lt;br /&gt;&lt;br /&gt;Pero no tardaremos demasiado tiempo en percartarnos de las diferencias. La principal de ellas, está en que todos los programas escritos en Clipper pueden compilarse y enlazarse. El resultado obtenido es un fichero ejecutable que puede utilizarse de forma independiente al gestor de base de datos y sin necesidad de incluir módulo runtime. Esto repercute en la velocidad de ejecución de los programas.&lt;br /&gt;&lt;br /&gt;Muchos programadores recordarán que cuando entregaban un proyecto a un cliente desarrollado en DBASE II o III se veían con la fatalidad de entregar los ficheros fuentes, ya que DBASE lo que hacía era interpretarlos. CLIPPER salvaguardó estos intereses. CLIPPER aportó más comandos y funciones y prescindió de muchos de DBASE.&lt;br /&gt;&lt;br /&gt;CLIPPER es ahora sin duda el compilador más utilizado en aplicaciones de gestión de datos para microordenadores. La última versión aparecida en el mercado es la CLIPPER 5.01 versión reparada de la CLIPPER 5.0. Hasta el momento, la versión más utilizada quizás por su largo tiempo de vigencia es la CLIPPER SUMMER '87. Anteriores a ésta eran la CLIPPER AUTUMN '86 y la versión de 1985.&lt;br /&gt;&lt;br /&gt;De todas la versiones detalladas la SUMMER '87 ha sido la más difundida. Muchas aplicaciones se han desarrollado con esta versión, por ello, aún, muchos programadores se resisten al cambio a versiones más actuales.&lt;br /&gt;&lt;br /&gt;Otras prestaciones de CLIPPER SUMMER '87 a destacar son las siguientes:&lt;br /&gt;&lt;br /&gt;   * Provee un conjunto de funciones para el tratamiento de ficheros en redes de area local.&lt;br /&gt;   * Permite manejar ficheros de bajo nivel.&lt;br /&gt;   * Posibilita la creación de funciones de usuarios y agruparlas en librerías.&lt;br /&gt;   * Permite el uso de arrays unidimensionales.&lt;br /&gt;   * Proporciona un depurador avanzado.&lt;br /&gt;&lt;br /&gt;La presente guía está dividida en doce capítulos. Cada capítulo describe comandos y/o funciones de Clipper referentes a temas concretos. El primero de ellos describe aspectos técnicos iniciales que es preciso conocer de este producto.&lt;br /&gt;&lt;br /&gt;I. Características técnicas.&lt;br /&gt;&lt;br /&gt;1. Capacidades.&lt;br /&gt;&lt;br /&gt;Nº. máximo de registros por base de datos, 1000.000.000&lt;br /&gt;Nº. máximo de caracteres por registro, RAM disponible&lt;br /&gt;Nº. máximo de campos por registro, RAM disponible&lt;br /&gt;Nº. máximo de caracteres por campo, 32 kb&lt;br /&gt;Nº. de dígitos de precisión en operaciones de cálculo, 18&lt;br /&gt;Nº. máximo de caracteres en una clave de indexación, 250&lt;br /&gt;Nº. máximo de variables de memoria, 2048&lt;br /&gt;Tamaño máximo de una variable de memoria, 64 kb&lt;br /&gt;Nº. máximo de dígitos en una variable numérica, 19&lt;br /&gt;Nº. máximo de tablas, 2048&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2. Requerimiento hardware.&lt;br /&gt;&lt;br /&gt;Ordenador : IBM PC, XT, AT, 386 o compatible&lt;br /&gt;Memoria RAM : 256 kb&lt;br /&gt;Disco duro : Necesario para funcionamiento óptimo&lt;br /&gt;Coprocesador: Si existe se aprovecha automáticamente&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3. Requerimiento software.&lt;br /&gt;&lt;br /&gt;Sistema Operativo :&lt;br /&gt;DOS 2.0 o superior (monousuario)&lt;br /&gt;DOS 3.1 o superior (multiusuario)&lt;br /&gt;LAN : Bajo DOS. No requiere LAN Pack. Bloqueo manual.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;4. Instalación .&lt;br /&gt;&lt;br /&gt;La instalación de CLIPPER es muy fácil, basta con copiar el contenido de todos los disquetes a un directorio o ejecutar el fichero CLIPCOPY.BAT que se encuentra en el disco de Sistema.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;5. Config.sys.&lt;br /&gt;&lt;br /&gt;Para el funcionamiento óptimo de CLIPPER conviene incluir las siguientes líneas en el fichero de configuración CONFIG.SYS.&lt;br /&gt;&lt;br /&gt;FILES = 20&lt;br /&gt;BUFFERS = 8&lt;br /&gt;&lt;br /&gt;Si se posee DOS 3.3 o superior es posible trabajar hasta con 255 ficheros abiertos simultáneamente. Para ello se debe indicar, en lugar de FILES = 20:&lt;br /&gt;&lt;br /&gt;FILES = 255&lt;br /&gt;&lt;br /&gt;(Es importante ajustar el número de ficheros para aprovechar al máximo la memoria).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;6. Autoexec.bat.&lt;br /&gt;&lt;br /&gt;En el fichero AUTOEXEC.BAT resulta de gran utilidad incluir una línea de PATH. Esto permitirá ejecutar el compilador desde otros directorios de trabajo.&lt;br /&gt;&lt;br /&gt;PATH C:\CLIPPER&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;7. Ficheros.&lt;br /&gt;&lt;br /&gt;A los distintos ficheros que maneja CLIPPER podremos diferenciarlos por su extensión. Si hemos utilizado anteriormente DBASE, la mayoría nos resultarán familiares.&lt;br /&gt;&lt;br /&gt;Bases de datos (.DBF)&lt;br /&gt;Datos memo (.DBT)&lt;br /&gt;Indices (.NTX) en DBASEIII (.NDX)&lt;br /&gt;Etiquetas (.LBL)&lt;br /&gt;Informes (.FRM)&lt;br /&gt;Texto (.TXT)&lt;br /&gt;Variables de memoria (.MEM)&lt;br /&gt;Fuentes (.PRG)&lt;br /&gt;Objetos (.OBJ)&lt;br /&gt;Compilación (.CLP)&lt;br /&gt;Enlace (.LNK)&lt;br /&gt;Overlays (.OVL)&lt;br /&gt;Ejecutables (.EXE)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;8. Compatibilidad con DBASE.&lt;br /&gt;&lt;br /&gt;La posibilidad de compilar DBASE con el compilador de CLIPPER está limitada por un grupo de comandos y funciones de DBASE. A continuación se muestra una relación de estos comandos y funciones:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;¦ APPEND LIST FILES SET CATALOG ¦&lt;br /&gt;¦ ASSIST LIST HISTORY SET COLOR ON/OFF ¦&lt;br /&gt;¦ BROWSE LIST STRUCTURE SET DEBUG ¦&lt;br /&gt;¦ CHANGE LOAD SET DOHISTORY ¦&lt;br /&gt;¦ CLEAR FIELDS LOGOUT SET ECHO ¦&lt;br /&gt;¦ CREATE LABEL MESSAGE() SET ENCRYPTION ¦&lt;br /&gt;¦ CREATE REPORT MODIFY COMMAND SET FIELDS ¦&lt;br /&gt;¦ CREATE QUERY MODIFY LABEL SET HEADING ¦&lt;br /&gt;¦ CREATE SCREEN MODIFY QUERY SET HELP ¦&lt;br /&gt;¦ CREATE VIEW MODIFY REPORT SET HISTORY ¦&lt;br /&gt;¦ DISPLAY FILES MODIFY SCREEN SET MEMOWIDTH ¦&lt;br /&gt;¦ DISPLAY MEMORY MODIFY STRUCTURE SET MENUS ¦&lt;br /&gt;¦ DISPLAY STATUS MODIFY VIEW SET SAFETY ¦&lt;br /&gt;¦ DISPLAY STRUCTUR ON ERROR SET STATUS ¦&lt;br /&gt;¦ DISPLAY USERS ON ESCAPE SET STEP ¦&lt;br /&gt;¦ EDIT ON KEY SET TALK ¦&lt;br /&gt;¦ ERROR() RESUME SET TITLE ¦&lt;br /&gt;¦ EXPORT TO RETRY SET TYPEHEAD ¦&lt;br /&gt;¦ HELP RETURN TO MASTER SET VIEW ¦&lt;br /&gt;¦ IMPORT TO SET ¦&lt;br /&gt;¦ INSERT SET CARRY ¦&lt;br /&gt;Comandos y funciones no compatibles.&lt;br /&gt;&lt;br /&gt;Otras distinciones a considerar son las referentes a las macros (en Clipper no pueden usarse para sustituir a una palabra del sistema) y los ficheros índices (en Clipper están optimizados).&lt;br /&gt;&lt;br /&gt;Clipper proporciona un manejador de bases de datos (DBU), un emulador del punto de petición de orden de Dbase (DOT), un generador de informes y etiquetas (RL) y un generador de ficheros índices. Todo estas opciones son semejantes a las proporcionadas por Dbase.&lt;br /&gt;&lt;br /&gt;II. Entorno de desarrollo.&lt;br /&gt;&lt;br /&gt;1. Entorno.&lt;br /&gt;&lt;br /&gt;Para desarrollar con CLIPPER tendremos que disponer de las siguientes herramientas básicas:&lt;br /&gt;&lt;br /&gt;- Un editor que genere código ASCII standard.&lt;br /&gt;- El compilador CLIPPER.EXE.&lt;br /&gt;- Las librerías CLIPPER.LIB, EXTEND.LIB, OVERLAY.LIB, etc.&lt;br /&gt;- Un enlazador PLINK86.EXE ,LINK.EXE ,TLINK.EXE.&lt;br /&gt;- Un depurador de programas DEBUG.OBJ.&lt;br /&gt;&lt;br /&gt;2. Escritura de programas.&lt;br /&gt;&lt;br /&gt;Los requisitos básicos a cumplir para la correcta escritura de los fuentes son:&lt;br /&gt;&lt;br /&gt;a) Los ficheros fuentes se nombrarán especificando la extensión .PRG.&lt;br /&gt;&lt;br /&gt;b) La longitud de una línea es de 256 caracteres.&lt;br /&gt;&lt;br /&gt;c) Una línea sólo admitirá una instrucción.&lt;br /&gt;&lt;br /&gt;d) Las instrucciones pueden escribirse desde la primera línea en el editor.&lt;br /&gt;&lt;br /&gt;e) Cuando sea necesario escribir líneas de instrucciones muy largas, podemos hacerlo en líneas independiente escribiendo un punto y coma al final de la línea.&lt;br /&gt;&lt;br /&gt;f) Puede escribirse en minúsculas o mayúsculas, indistintamente.&lt;br /&gt;&lt;br /&gt;g) El asterisco '*' se utilizará para hacer comentarios.&lt;br /&gt;&lt;br /&gt;h) El doble '&amp;amp;' se utilizará para comentar líneas con instrucciones.&lt;br /&gt;&lt;br /&gt;3. Compilación.&lt;br /&gt;&lt;br /&gt;La compilación es una traducción del fichero fuente (.PRG) para obtener un fichero objeto (.OBJ). Consiste en transcribir cada instrucción desde el lenguaje simbólico en que está escrito el código (CLIPPER) a código comprensible por el enlazador del sistema operativo (DOS).&lt;br /&gt;&lt;br /&gt;El fichero del compilador que proporciona CLIPPER se llama CLIPPER.EXE.&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;CLIPPER [- {-}]&lt;br /&gt;&lt;br /&gt;Programa fuente que se compila&lt;br /&gt;- Opciones de compilación&lt;br /&gt;&lt;br /&gt;-l El módulo objeto no almacena el nº de línea del fuente.&lt;br /&gt;-m Hace que las llamadas DO o SET PROCEDURE no se compi- len.&lt;br /&gt;-o Especificar el directorio donde se depositará el fichero objeto.&lt;br /&gt;-p La compilación no comienza hasta que no se pulsa una tecla.&lt;br /&gt;-q Suprime la visión en pantalla de los números de líneas.&lt;br /&gt;-s Hace que no se genere módulo objeto. Verifica sólo sintaxis.&lt;br /&gt;-t Especificar la unidad donde se creará el fichero temporal .$$$&lt;br /&gt;&lt;br /&gt;Es imprescindible que haya al menos un espacio en blanco entre y la primera opción así como entre cada una de ellas. Es obligatorio que la opción se exprese en minúsculas.&lt;br /&gt;&lt;br /&gt;Nuestro programa puede contener asimismo diversas llamadas DO a otros módulos .PRG o a procedimientos del mismo programa. Si no le especificamos lo contrario, CLIPPER compila de forma automática los ficheros llamados por DO.&lt;br /&gt;&lt;br /&gt;4. Enlace.&lt;br /&gt;&lt;br /&gt;El fin de un enlazador es el de asociar los módulos objeto obtenidos mediante el compilador con las librerías donde se contienen las traducciones máquina de cada una de las sentencias,llamadas,etc. que aparecen en el módulo objeto.&lt;br /&gt;&lt;br /&gt;a) Enlazadores&lt;br /&gt;&lt;br /&gt;* PLINK86 (Phoenix Tec. Clipper Summer '87)&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;PLINK86 FI {,} [OUTPUT ] LIB&lt;br /&gt;{,} | [@]&lt;br /&gt;&lt;br /&gt;* LINK (Microsoft)&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;LINK {},,, {}&lt;br /&gt;&lt;br /&gt;* TLINK (Borland)&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;TLINK {},,, {}&lt;br /&gt;&lt;br /&gt;* RTLINK (Pocket Soft. Clipper 5)&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;RTLINK [FI [OUTPUT ] [LIB [] []] | [@]&lt;br /&gt;&lt;br /&gt;b) Overlay&lt;br /&gt;&lt;br /&gt;* Ficheros de enlace .LNK&lt;br /&gt;&lt;br /&gt;Todas las claúsulas que deban indicarse al enlazador pueden situarse en un fichero de enlace .LNK. El enlazador usa uno de estos ficheros conforme a la siguiente sintaxis:&lt;br /&gt;&lt;br /&gt;PLINK86 @&lt;br /&gt;&lt;br /&gt;Ejemplo_1: PRUEBA.LNK&lt;br /&gt;&lt;br /&gt;FILE prueba&lt;br /&gt;LIB clipper,extend&lt;br /&gt;; (';'Indica el final del fichero .LNK)&lt;br /&gt;&lt;br /&gt;* Librerias&lt;br /&gt;&lt;br /&gt;CLIPPER.LIB&lt;br /&gt;EXTEND.LIB&lt;br /&gt;OVERLAY.LIB&lt;br /&gt;&lt;br /&gt;* Overlays&lt;br /&gt;&lt;br /&gt;El mayor problema con el que nos podemos encontrar, cuando estamos realizando una aplicación en Clipper, es que ésta no nos quepa físicamente en la memoria de trabajo de nuestro ordenador.&lt;br /&gt;&lt;br /&gt;El único modo que tenemos de solucionar este problema es proceder a lo que denominamos segmentación, programación por capas, solapas u overlays.&lt;br /&gt;&lt;br /&gt;Cuando programamos usando esta técnica, lo que hacemos es dividir la memoria RAM en dos o más áreas de trabajo. En la primera de ellas (área principal) se carga el módulo ejecutable, y en las áreas de solape se cargan y descargan, conforme se van usando, los diferentes módulos overlay que hayamos definido.&lt;br /&gt;&lt;br /&gt;Ejemplo_1: PRUEBA.LNK (2 áreas)&lt;br /&gt;FILE prgprin&lt;br /&gt;LIB clipper,extend&lt;br /&gt;OVERLAY CODE, $CONSTANTS&lt;br /&gt;BEGINAREA&lt;br /&gt;SECTION FILE modulo1&lt;br /&gt;SECTION FILE modulo2&lt;br /&gt;SECTION FILE modulo3&lt;br /&gt;ENDAREA&lt;br /&gt;&lt;br /&gt;Mandatos para compilar y linkar&lt;br /&gt;&lt;br /&gt;CLIPPER prgprin -m&lt;br /&gt;CLIPPER modulo1&lt;br /&gt;CLIPPER modulo2&lt;br /&gt;CLIPPER modulo3&lt;br /&gt;PLINK86 @prueba&lt;br /&gt;&lt;br /&gt;Ejemplo_2: PRUEBA.LNK (3 áreas)&lt;br /&gt;FILE prgprin&lt;br /&gt;LIB clipper,extend&lt;br /&gt;OVERLAY CODE, $CONSTANTS&lt;br /&gt;BEGINAREA&lt;br /&gt;SECTION FILE modulo1&lt;br /&gt;SECTION FILE modulo2&lt;br /&gt;ENDAREA&lt;br /&gt;BEGINAREA&lt;br /&gt;SECTION FILE modulo3&lt;br /&gt;ENDAREA&lt;br /&gt;&lt;br /&gt;Mandatos para compilar y linkar&lt;br /&gt;&lt;br /&gt;CLIPPER prgprin -m&lt;br /&gt;CLIPPER modulo1&lt;br /&gt;CLIPPER modulo2&lt;br /&gt;CLIPPER modulo3&lt;br /&gt;PLINK86 @prueba&lt;br /&gt;&lt;br /&gt;(Para que Clipper produzca un fichero .EXE y tantos ficheros .OVL como módulos para overlays tengamos definidos, sólo hay que cambiar la instrucción:&lt;br /&gt;&lt;br /&gt;SECTION FILE {,}&lt;br /&gt;&lt;br /&gt;por&lt;br /&gt;&lt;br /&gt;SECTION INTO FILE {,}&lt;br /&gt;&lt;br /&gt;(Esto último es útil para trabajar con disquetes)&lt;br /&gt;&lt;br /&gt;* Mandatos del enlazador PLINK86&lt;br /&gt;&lt;br /&gt;#. Sirve para poner un comentario en un fichero de enlace.&lt;br /&gt;&lt;br /&gt;BATCH. Por defecto, cuando PLINK86 no encuentra un fichero .OBJ o .LIB de los especificados, la operación de enlace continúa adelante.&lt;br /&gt;&lt;br /&gt;BEGINAREA. Determina el comienzo de un área.&lt;br /&gt;&lt;br /&gt;ENDAREA. Determina el final de un área.&lt;br /&gt;&lt;br /&gt;DEBUG. Proporciona información adicional para ayudar a la depuración de una aplicación en el caso de overlay.&lt;br /&gt;&lt;br /&gt;FILE. Especificar los módulos objetos separados por coma (,).&lt;br /&gt;&lt;br /&gt;HEIGHT. Nº líneas/página del informe (MAP).&lt;br /&gt;&lt;br /&gt;LIBRARY. Especificar las librerías que serán enlazadas con los .OBJ.&lt;br /&gt;&lt;br /&gt;LOWERCASE. Convierte en minúsculas todos los identificadores y símbolos.&lt;br /&gt;&lt;br /&gt;MAP=. Especificar fichero .MAP.&lt;br /&gt;&lt;br /&gt;NOBELL. Elimina el sonido que aparece con los mensajes del PLINK86.&lt;br /&gt;&lt;br /&gt;OUTPUT. Especificar fichero .EXE.&lt;br /&gt;&lt;br /&gt;SEARCH. Hace una segunda pasada por las librerías si tras terminar el enlace alguno de los símbolos ha quedado sin definir.&lt;br /&gt;&lt;br /&gt;SECTION. Determina que los módulos objeto que se relacionan tras la palabra FILE estarán en el área de overlay abierta, pero no en un fichero independiente en disco.&lt;br /&gt;&lt;br /&gt;SECTION INTO. Igual que el anterior, pero en un fichero en disco.&lt;br /&gt;&lt;br /&gt;UPPERCASE. Convierte a mayúsculas todos los identificadores y símbolos.&lt;br /&gt;&lt;br /&gt;VERBOSE. Nos da información en pantalla de lo que está haciendo PLINK86.&lt;br /&gt;&lt;br /&gt;WIDTH. Determina el ancho en columnas del informe (MAP).&lt;br /&gt;&lt;br /&gt;WORKFILE. Sirve para direccionar el archivo temporal que usa el enlazador.&lt;br /&gt;&lt;br /&gt;III. Bases de datos.&lt;br /&gt;&lt;br /&gt;1. Creación de una base de datos.&lt;br /&gt;&lt;br /&gt;Para crear un fichero de estructura vacia se usará el mandato CREATE. Para definir los distintos campos de la futura base de datos emplearemos APPEND BLANK (para añadir un registro en blanco) y REPLACE (para almacenar el contenido).&lt;br /&gt;&lt;br /&gt;CREATE&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;CREATE clientes&lt;br /&gt;USE clientes&lt;br /&gt;APPEND BLANK&lt;br /&gt;REPLACE FIELD_NAME WITH "CODIGO"&lt;br /&gt;REPLACE FIELD_TYPE WITH "C"&lt;br /&gt;REPLACE FIELD_LEN WITH 5&lt;br /&gt;APPEND BLANK&lt;br /&gt;REPLACE FIELD_NAME WITH "NOMBRE"&lt;br /&gt;REPLACE FIELD_TYPE WITH "C"&lt;br /&gt;REPLACE FIELD_LEN WITH 30&lt;br /&gt;CLOSE&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;Las variables de entorno FIELD_NAME, FIELD_TYPE, FIELD_LEN y FIELD_DECIMALS tomarán el nombre de campo, el tipo de campo, la longitud de campo y las posiciones decimales, respectivamente.&lt;br /&gt;&lt;br /&gt;Una vez creada la estructura pasaremos a generar la base de datos propiamente dicha con CREATE FROM.&lt;br /&gt;&lt;br /&gt;CREATE FROM&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;CREATE CLIENTES FROM CLIENTES&lt;br /&gt;&lt;br /&gt;Tanto en la utilidad DOT porporcionada por Clipper como en el entorno Dbase podemos crear bases de datos sin necesidad de escribir programas.&lt;br /&gt;&lt;br /&gt;2. Tipos y longitud de campos.&lt;br /&gt;&lt;br /&gt;Los distintos tipos de campos que podemos definir en una base de datos son:&lt;br /&gt;&lt;br /&gt;C - Caracter (1-254 caracteres alfanuméricos)&lt;br /&gt;N - Numérico (1-19 dígitos de entero.)&lt;br /&gt;(0-15 dígitos decimal y dos dígitos menor que entero)&lt;br /&gt;D - Fecha (8 dd-mm-aa)&lt;br /&gt;L - Lógico (1 carácter para valores lógicos: T,F,Y,N)&lt;br /&gt;M - Memo (10) Almacena dirección para acceder a fichero .DBT.&lt;br /&gt;&lt;br /&gt;3. Usar una base de datos.&lt;br /&gt;&lt;br /&gt;Para usar una base de datos emplearemos la sentencia USE especificando el fichero de base de datos. Si existe un fichero memo asociado se abrirá, y si se indicó uno o más ficheros .NTX se activarán los índices correspondientes. También proporciona el alias adecuado.&lt;br /&gt;&lt;br /&gt;USE [INDEX {,}] [EXCLUSIVE] [ALIAS]&lt;br /&gt;&lt;br /&gt;El número máximo de ficheros índices asociados es 15. EXCLUSIVE se emplea para redes y posibilita la apertura de ficheros con uso exclusivo a un usuario.&lt;br /&gt;&lt;br /&gt;USE sin más, cierra el fichero del área activa.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;&lt;br /&gt;4. Modificar estructura.&lt;br /&gt;&lt;br /&gt;Para modificar la estructura de una base de datos se recomienda el uso de la sentencia MODIFY STRUCTURE propia de Dbase. Posibilita renombrar, suprimir y añadir campos, así como modificar el tipo y la longitud de los mismos. Con LIST STRUCTURE de Dbase listaremos la estructura de una base de datos.&lt;br /&gt;&lt;br /&gt;MODIFY STRUCTURE&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;MODIFY STRUCTURE&lt;br /&gt;&lt;br /&gt;Hay que tener precaución si existen registros en la base de datos ya que algunas modificaciones pueden vaciarnos el contenido de uno o más campos.&lt;br /&gt;&lt;br /&gt;5. Añadir registros.&lt;br /&gt;&lt;br /&gt;APPEND BLANK añade un registro vacio a nuestro fichero en uso. El puntero de la base de datos se sitúa en el registro añadido. La sentencia REPLACE nos servirá para reemplazar el contenido de los campos.&lt;br /&gt;&lt;br /&gt;REPLACE [] [] WITH {,[] WITH } [FOR ][WHILE ]&lt;br /&gt;&lt;br /&gt;Ejemplo_1: USE CLIENTES&lt;br /&gt;APPEND BLANK&lt;br /&gt;REPLACE CODIGO WITH "00001"&lt;br /&gt;REPLACE NOMBRE WITH "Federico Torres"&lt;br /&gt;&lt;br /&gt;6. Listar registros.&lt;br /&gt;&lt;br /&gt;LIST y DISPLAY sirven para visualizar, imprimir o enviar a un fichero de texto, un registro o conjuto de registros.&lt;br /&gt;&lt;br /&gt;LIST [OFF] [&lt;ámbito&gt;] [] [FOR ] [WHILE ] [TO PRINT/TO FILE ]&lt;br /&gt;&lt;br /&gt;DISPLAY [OFF] [&lt;ámbito&gt;] [] [FOR ] [WHILE ] [TO PRINT/TO FILE ]&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;LIST&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;LIST CODIGO&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DISPLAY FOR CODIGO &gt; "50000" TO PRINT&lt;br /&gt;&lt;br /&gt;7. Puntero de registro.&lt;br /&gt;&lt;br /&gt;Clipper mantiene un puntero que indica el registro activo en cada momento. Tanto en Clipper con en Dbase podemos conocer la posición del puntero con la función RECNO(). En el ejemplo anterior al añadir el registro vacio el puntero se desplaza a la posición que ocupa este registro dentro de la base de datos. Podemos deducir que las sustituciones se efectuarán ahí.&lt;br /&gt;&lt;br /&gt;Existen mandatos que afectan únicamente al registro activo. El puntero se puede desplazar usando la sentencias GO y SKIP en sus distintas modalidades:&lt;br /&gt;&lt;br /&gt;GO (ir al registo indicado)&lt;br /&gt;GO TOP (ir al registro número 1)&lt;br /&gt;GO BOTTOM (ir último registro)&lt;br /&gt;SKIP (ir al siguiente registro)&lt;br /&gt;SKIP -1 (ir al anterior)&lt;br /&gt;etc.&lt;br /&gt;&lt;br /&gt;8. Editar un registro.&lt;br /&gt;&lt;br /&gt;La edición de registros es posible realizarla con varias sentencias. No es posible usar EDIT de Dbase III. En Clipper la edición de un registro puede realizarse con un grupo de GET's, aunque existen otras sentencias más avanzada como DBEDIT, MEMOEDIT, etc.&lt;br /&gt;&lt;br /&gt;@ , [SAY [PICTURE ]] [GET [PICTURE ] [RANGE , ] [VALID ]]&lt;br /&gt;&lt;br /&gt;PICTURE expresa un formato para la entrada/salida de información.&lt;br /&gt;&lt;br /&gt;RANGE sirve para validar datos numéricos entre los dos límites especificados.&lt;br /&gt;&lt;br /&gt;VALID se emplea para expresiones genéricas de validación. será la condición de validación.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;GO 3&lt;br /&gt;@ 1,1 SAY " Modifique codigo: " GET CODIGO&lt;br /&gt;@ 2,1 SAY " Modifique nombre: " GET NOMBRE&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;USE ALUMNOS&lt;br /&gt;GO TOP&lt;br /&gt;@ 1,1 SAY NOMBRE&lt;br /&gt;@ 2,1 SAY " Modifique edad: " GET EDAD RANGE 1,7&lt;br /&gt;@ 3,1 SAY " Modifique sexo: " GET SEXO PICTURE "!"; VALID(SEXO$"VH")&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;READ lee las variables GET's&lt;br /&gt;&lt;br /&gt;9. Marcar un registro.&lt;br /&gt;&lt;br /&gt;Clipper igual que Dbase permite marcar registros para posteriormente, si procede, borrarlos definitivamente. Esto se hará con la sentencia DELETE que marca con un asterisco el registro activo. Puede marcarse más de un registro usando la claúsulas FOR o WHILE.&lt;br /&gt;&lt;br /&gt;DELETE [ámbito] [FOR ] [WHILE ]&lt;br /&gt;&lt;br /&gt;[ámbito] RECORD Marcar el registro especificado.&lt;br /&gt;ALL Marcar todos los registros&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;GO 1&lt;br /&gt;DELETE&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DELETE ALL&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DELETE RECORD 10&lt;br /&gt;&lt;br /&gt;Ejemplo_4:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DELETE FOR NOMBRE = "María"&lt;br /&gt;&lt;br /&gt;10. Borrar registros.&lt;br /&gt;&lt;br /&gt;Una vez marcado un registro es posible borrarlo con PACK&lt;br /&gt;&lt;br /&gt;PACK&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DELETE RECORD 10&lt;br /&gt;PACK&lt;br /&gt;&lt;br /&gt;11. Desmarcar registros.&lt;br /&gt;&lt;br /&gt;La sentencia RECALL suprime las marcas puestas con DELETE&lt;br /&gt;&lt;br /&gt;RECALL [&lt;ámbito&gt;] [FOR ] [WHILE ]&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;RECALL RECORD 10&lt;br /&gt;&lt;br /&gt;12. Borrar todos los registros.&lt;br /&gt;&lt;br /&gt;ZAP borra todos los registros marcados o no de una base de datos manteniendo su estructura.&lt;br /&gt;&lt;br /&gt;13. Localizar registros.&lt;br /&gt;&lt;br /&gt;LOCATE permite localizar uno o más registros. En el momento que encuentra un registro el puntero de registro se coloca en él, esperando a un CONTINUE para continuar con la búsqueda. La búsqueda es secuencial por lo que si el tamaño de la base de datos es considerable puede resultar lento este proceso.&lt;br /&gt;&lt;br /&gt;LOCATE [&lt;ámbito&gt;] [FOR ] [WHILE ]&lt;br /&gt;CONTINUE&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;LOCATE FOR CODIGO &gt; "10000" .AND. NOMBRE = "JOSE"&lt;br /&gt;&lt;br /&gt;14. Operaciones con bases de datos.&lt;br /&gt;&lt;br /&gt;En una base de datos es posible contar registros, y realizar operaciones de suma y media aritmética. COUNT nos servirá para contar, SUM para sumar el contenido de campos numéricos y AVERAGE para calcular la media aritmética.&lt;br /&gt;&lt;br /&gt;COUNT [&lt;ámbito&gt;] [FOR ] [WHILE ] TO&lt;br /&gt;&lt;br /&gt;COUNT cuenta el número de registros que cumplen una determinada condición especificada. Dicha información ha de depositarse obligatoriamente en una variable numérica de memoria.&lt;br /&gt;&lt;br /&gt;&lt;ámbito&gt; es por defecto ALL&lt;br /&gt;&lt;br /&gt;SUM [&lt;ámbito&gt;] TO&lt;br /&gt;&lt;br /&gt;SUM suma uno o más campos depositando el resultado en una variable.&lt;br /&gt;&lt;br /&gt;AVERAGE [&lt;ámbito&gt;} TO&lt;br /&gt;&lt;br /&gt;AVERAGE calcula la media aritmética de uno o más campos.&lt;br /&gt;&lt;br /&gt;15. Exportar.&lt;br /&gt;&lt;br /&gt;COPY TO Copia toda la base de datos en curso o sólo una parte a un nuevo archivo.&lt;br /&gt;&lt;br /&gt;COPY TO [&lt;ámbito&gt; [FIELDS ]&lt;br /&gt;[FOR ] [WHILE ] [SDF/DELIMITED/DELIMITED WITH ]&lt;br /&gt;&lt;br /&gt;- Es el nombre del nuevo archivo.&lt;br /&gt;&lt;br /&gt;&lt;ámbito&gt; - Determina la porción del archivo a copiar, por defecto es ALL (todo).&lt;br /&gt;&lt;br /&gt;FIELDS - Son los campos a copiar a la nueva base de datos.&lt;br /&gt;&lt;br /&gt;FOR/WHILE - Especifican la condición a cumplir.&lt;br /&gt;&lt;br /&gt;SDF - Especifica que el archivo de salida será con formato ASCII, con campos de longitud fija.&lt;br /&gt;&lt;br /&gt;DELIMITED - Formato para el archivo de salida ASCII, con campos de longitud variable y separados por comas. Si se desea pueden separase con espacios (BLANK), o con cualquier otro delimitador.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE HELP&lt;br /&gt;COPY TO HELP.TXT SDF&lt;br /&gt;&lt;br /&gt;16. Importar.&lt;br /&gt;&lt;br /&gt;APPEND FROM añade datos a la base en uso a partir de otro archivo que puede ser que no sea (.DBF). Podemos seleccionar loa datos a añadir mediante cualificadores.&lt;br /&gt;&lt;br /&gt;APPEND [ [FIELDS ] FROM&lt;br /&gt;[FOR ] [WHILE ] [SDF/DELIMITED&lt;br /&gt;[WITH BLANK/]]&lt;br /&gt;&lt;br /&gt;- Registros a agregar por defecto son todos.&lt;br /&gt;&lt;br /&gt;- Lista de campos a agregar.&lt;br /&gt;&lt;br /&gt;- Nombre del archivo origen. Por defecto, (.DBF),&lt;br /&gt;&lt;br /&gt;FOR/WHILE - Indican las condiciones que han de cumplir los registros para ser agregados.&lt;br /&gt;&lt;br /&gt;SDF - Identifca archivos ASCII.&lt;br /&gt;&lt;br /&gt;DELIMITED - Archivos ASCII con separación de campos con comas.&lt;br /&gt;&lt;br /&gt;DELIMITED WITH BLANK - Campos separados por un espacio&lt;br /&gt;&lt;br /&gt;DELIMITED WITH - Podemos especificarlo.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;APPEND FROM VENTAS FOR PEDIDO &gt; 5000&lt;br /&gt;&lt;br /&gt;IV. Indices.&lt;br /&gt;&lt;br /&gt;1. Crear ficheros índices.&lt;br /&gt;&lt;br /&gt;INDEX indexa un fichero de datos por el campo que le indiquemos. Crea en disco un fichero con la extensión .NTX. Pueden usarse también claves múltiples formada por la suma de varios campos, de partes de campos, expresiones y campos, etc, pero recuerde que el máximo número de caracteres de una clave será de 250. Para sumar campos hemos de tener siempre la precaución de convertirlos previamente a cadena. Los ficheros índices no son compatibles con los de Dbase III. Cuando un índice está abierto con su correspondiente base de datos se actualiza de forma automática. Una base de datos puede tener asociados como máximo 15 ficheros índices. Los registros que se encuentran marcados para ser borrados también forman parte del índice.&lt;br /&gt;&lt;br /&gt;INDEX ON {+} TO&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTE&lt;br /&gt;INDEX ON NOMBRE TO NOMCLI&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;USE CLIENTE&lt;br /&gt;INDEX ON NOMBRE+DTOS(FECHA) TO FECCLI&lt;br /&gt;&lt;br /&gt;2. Activar fichero índice.&lt;br /&gt;&lt;br /&gt;Como vimos anteriormente en el capítulo I, la activación de índices se realiza con USE. Se pueden especificar uno o más ficheros índices. Con SET ORDER TO se establecerá el índice activo. Esta sentencia altera el ordenamiento de la declaración inicial de índices hecha con USE...INDEX. Si indicamos SET ORDER TO 0 se desactivan todos los ficheros índices. No obstante, la importancia de este mandato estriba en que no tenemos necesidad de abrirlos de nuevo para activarlos.&lt;br /&gt;&lt;br /&gt;SER ORDER TO&lt;br /&gt;&lt;br /&gt;es el número de índice activo. Puede valer de 0 a 15.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;NOMBRE = SPACE(20)&lt;br /&gt;FECHA = CTOD(SPACE(8))&lt;br /&gt;USE CLIENTES INDEX NOMCLI,FECCLI,DOMCLI&lt;br /&gt;SET ORDER TO 2&lt;br /&gt;LIST NOMBRE,FECHA TO PRINT&lt;br /&gt;&lt;br /&gt;3. Búsqueda por índice.&lt;br /&gt;&lt;br /&gt;SEEK busca una expresión en una clave índice.&lt;br /&gt;&lt;br /&gt;SEEK&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES INDEX NOMCLI&lt;br /&gt;SEEK "LUIS MARIN"&lt;br /&gt;IF FOUND()&lt;br /&gt;@ 4,4 SAY FECHA&lt;br /&gt;@ 5,4 SAY VENTAS&lt;br /&gt;ELSE&lt;br /&gt;@ 10,1 SAY "No existe CLIENTE"&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;4. Area de trabajo.&lt;br /&gt;&lt;br /&gt;SELECT selecciona las diferentes áreas de trabajo en que vamos a situar nuestros ficheros de datos. El último SELECT que enunciemos es aquel que contendrá el fichero activo.&lt;br /&gt;&lt;br /&gt;SELECT &lt;área&gt;/&lt;br /&gt;&lt;br /&gt;&lt;área&gt; es un número comprendido entre 0 y 254.&lt;br /&gt;&lt;br /&gt;es el nombre de un área de trabajo existente si hay un fichero abierto en ese área. Se puede hacer referencia a las 10 primeras áreas de trabajo con las letras A a J.&lt;br /&gt;&lt;br /&gt;En Clipper se pueden utilizar 255 áreas de trabajo. En cada área de trabajo se pueden abrir un fichero de base de datos y 15 ficheros índices como máximo asociados a él.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;SELECT 1&lt;br /&gt;USE CLIENTES&lt;br /&gt;SELECT 2&lt;br /&gt;USE DIARIOVTAS&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;SELECT 1&lt;br /&gt;USE CLIENTES INDEX NOMCLI ALIAS CLI&lt;br /&gt;SELECT 2&lt;br /&gt;USE DIARIOVTAS INDEX TOTALVTAS ALIAS DIA&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;SELECT CLI&lt;br /&gt;SEEK "LUIS PEREZ"&lt;br /&gt;IF FOUND()&lt;br /&gt;CODCLI = CODIGO&lt;br /&gt;SELECT DIA&lt;br /&gt;SEEK CODCLI&lt;br /&gt;IF FOUND()&lt;br /&gt;@ 10,10 SAY PTASVENTAS&lt;br /&gt;ENDIF&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;5. Cierre de ficheros.&lt;br /&gt;&lt;br /&gt;CLOSE cierra el fichero de base de datos abierto en el área activa así como sus índices asociados.&lt;br /&gt;&lt;br /&gt;CLOSE DATABASES cierra todos los ficheros de todas las áreas de trabajo, así como sus correspondientes índices.&lt;br /&gt;&lt;br /&gt;CLOSE INDEX cierra todos los índices del área de trabajo activa.&lt;br /&gt;&lt;br /&gt;CLOSE ALL cierra todos los ficheros abiertos.&lt;br /&gt;&lt;br /&gt;V. Variables de memoria.&lt;br /&gt;&lt;br /&gt;1. Tipos de variables.&lt;br /&gt;&lt;br /&gt;Variable es un nombre asignado a una posición de memoria que se puede utilizar para almacenar un dato concreto. Los tipos de variables por el tipo de dato que contienen son:&lt;br /&gt;&lt;br /&gt;-numéricas&lt;br /&gt;-alfanuméricas&lt;br /&gt;-lógicas&lt;br /&gt;-fechas&lt;br /&gt;&lt;br /&gt;2. Nombrar una variable de memoria.&lt;br /&gt;&lt;br /&gt;Independientemente del tipo a que pertenezca una variable, debe asignársele un nombre, que puede ser de uno a diez caracteres pueden ser una combinación de letras, dígitos o signo de subrayado. El primer carácter de una variable de memoria debe ser una letra. Los siguientes nombres son nombres de variables de memoria permitidos.&lt;br /&gt;&lt;br /&gt;COMPRAS&lt;br /&gt;Precio&lt;br /&gt;I_V_A&lt;br /&gt;MES_1_A_6&lt;br /&gt;&lt;br /&gt;No debe utilizarse el mismo nombre para una variable y para un campo en la misma aplicación.&lt;br /&gt;&lt;br /&gt;3. Introducción de datos en una variable.&lt;br /&gt;&lt;br /&gt;Las instrucciones STORE y el signo igual (=) pueden emplearse indistintamente para la asignación de datos a variables de memoria.&lt;br /&gt;&lt;br /&gt;STORE TO&lt;br /&gt;=&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;PTAS = 0&lt;br /&gt;FECHA_ALTA = CTOD(SPACE(8))&lt;br /&gt;STORE "enero" TO MES&lt;br /&gt;&lt;br /&gt;4. Visualización de variables.&lt;br /&gt;&lt;br /&gt;Para visualizar el contenido de una variable puede usarse la interrogación (?) con los siguientes formatos:&lt;br /&gt;&lt;br /&gt;?&lt;br /&gt;??&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;? MES&lt;br /&gt;?? "HOLA"&lt;br /&gt;&lt;br /&gt;5. Expresiones.&lt;br /&gt;&lt;br /&gt;Además de servir como depósito temporal, las variables de memoria pueden utilizarse en procesosde operaciones. Una variable de memoria puede ser incluida en una expresión para definir un procedimiento, para describir una condición en una instrucción o para servir como elemento de salida (resultado de una operación).&lt;br /&gt;&lt;br /&gt;Pueden utilizarse diferentes tipos de expresión. Una expresión puede incluir un campo de datos, una variable de memoria, una constante o una combinación de todo ello. Sin embargo, todos los elementos de una expresión deben ser del mismo tipo.&lt;br /&gt;&lt;br /&gt;La expresión más corriente es la expresión aritmética, que puede contener un valor, una variable de memoria, un campo numérico y una combinación de éstos unidos por uno o más operadores aritméticos. Las expresiones son útiles para realizar cálculos matemáticos. Puede utilizarse una expresión para asignar un valor a una variable de memoria o para reemplazar el contenido de un campo numérico con un nuevo valor.&lt;br /&gt;&lt;br /&gt;Cuando se incluye más de un operador aritmético en una expresión, ésta se valora de izquierda a derecha de acuerdo con siguiente sistema de prioridades:&lt;br /&gt;&lt;br /&gt;Prioridad máxima : ** ^&lt;br /&gt;Prioridad secundaria: * /&lt;br /&gt;Baja prioridad : + -&lt;br /&gt;&lt;br /&gt;Se pueden utilizar paréntesis en una expresión para definir la secuencia de evaluación y suprimir el sistema normal de prioridades. El material dentro de los paréntesis siempre es evaluado previamente. Cuando haya paréntesis anidados es una expresión aritmética, la expresión del paréntesis interno es evaluado en primer lugar, luego se evalúa el paréntesis externo. Dentro de un paréntesis, los operadores se evalúan según el sistema de prioridades, de izquierda a derecha.&lt;br /&gt;&lt;br /&gt;6. Declaración pública y privada.&lt;br /&gt;&lt;br /&gt;PUBLIC declara variables de memoria como globales o públicas. Estas pueden modificar su valor en cualquier parte del programa.&lt;br /&gt;&lt;br /&gt;PUBLIC&lt;br /&gt;&lt;br /&gt;PRIVATE declara de uso privado la variables de memoria especificadas. Estás pueden modificar su valor en partes de un programa.&lt;br /&gt;&lt;br /&gt;PRIVATE&lt;br /&gt;&lt;br /&gt;7. Salvar y restaurar variables de memoria.&lt;br /&gt;&lt;br /&gt;SAVE TO salva en un fichero variables de memoria.&lt;br /&gt;&lt;br /&gt;SAVE TO [ALL [LIKE /EXCEPT ]]&lt;br /&gt;&lt;br /&gt;es el nombre del fichero donde se almacenarán las variables. Si no se especifica la extensión por defecto es .MEM.&lt;br /&gt;&lt;br /&gt;ALL salva en el fichero todas las variables existentes.&lt;br /&gt;&lt;br /&gt;LIKE salva en el fichero todas las variables cuya estructura sea semejante a la especificada en . Recuerde que puede hacer uso de los símbolos comodines: * y ?.&lt;br /&gt;&lt;br /&gt;EXCEPT salva todas las variables que no tengan una estructura semejante a .&lt;br /&gt;&lt;br /&gt;RESTORE FROM restaura desde disco el fichero de variables de memoria . Si se usa ADDITIVE no se borra el entorno de variables activo al restaurar.&lt;br /&gt;&lt;br /&gt;RESTORE FROM [ADDITIVE]&lt;br /&gt;&lt;br /&gt;Al restaurar las variables de memoria, éstas son privadas, a no ser que se especifiquen como públicas antes de restaurarlas y se utilice la claúsula ADDITIVE.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;conf_cla = "1234"&lt;br /&gt;conf_dis = "A"&lt;br /&gt;conf_dir = "C:\GESTION\"&lt;br /&gt;conf_mar = 20&lt;br /&gt;conf_col = "S"&lt;br /&gt;SAVE TO CONFIG ALL LIKE conf_*&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;RESTORE FROM CONFIG ADDITIVE&lt;br /&gt;clave = SPACE(4)&lt;br /&gt;@ 1,1 SAY "Teclear Clave: " GET clave&lt;br /&gt;READ&lt;br /&gt;IF clave = conf_cla&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;8. Eliminar variables de memoria.&lt;br /&gt;&lt;br /&gt;RELEASE elemina de la vemoria las variables especificadas.&lt;br /&gt;&lt;br /&gt;RELEASE [ {,}] [ALL [LIKE] EXCEPT ]]&lt;br /&gt;&lt;br /&gt;.. es la lista de variables que se desean eliminar.&lt;br /&gt;&lt;br /&gt;ALL indica que sean eliminadas todas las variables existentes.&lt;br /&gt;&lt;br /&gt;ALL LIKE indica que sean eliminadas todas las variables cuya estructura sea semejante a la expresada en . Se pueden usar los comodines: * y ?.&lt;br /&gt;&lt;br /&gt;ALL EXCEPT indica que sean borradas todas las variables que no concuerden con la estructura expresada en .&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;conf_cla = "1234"&lt;br /&gt;conf_dis = "A"&lt;br /&gt;conf_dir = "C:\GESTION\"&lt;br /&gt;conf_mar = 20&lt;br /&gt;conf_col = "S"&lt;br /&gt;RELEASE ALL&lt;br /&gt;&lt;br /&gt;9. Macros.&lt;br /&gt;&lt;br /&gt;Las macros sirven en CLIPPER para forzar la sustitución de una variable por su valor en aquellos puntos de programa donde por si misma la variable no se traduciría. Cuando tras una macro se sigue algún tipo de expresión hemos de indicar al sistema que la macro termina con un punto (.).&lt;br /&gt;&lt;br /&gt;&amp;amp;&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;nombre = "lápiz"&lt;br /&gt;? "Artículo: &amp;amp;nombre"&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;base = "CLIENTES"&lt;br /&gt;USE &amp;amp;base&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;filtro = "EDAD &gt; 18 .AND. SEXO = 'V'"&lt;br /&gt;USE CLIENTES&lt;br /&gt;LIST NOMBRE FOR &amp;amp;filtro&lt;br /&gt;&lt;br /&gt;10. Operadores y valores lógicos.&lt;br /&gt;&lt;br /&gt;a) Operadores lógicos.&lt;br /&gt;&lt;br /&gt;.AND. (Y además)&lt;br /&gt;.OR. (O además)&lt;br /&gt;.NOT. (Negación)&lt;br /&gt;! (Negación)&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF EDAD &gt; 18 .AND. EDAD &lt; pagar =" 10000"&gt;&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;IF !FILE("CLIENTES.DBF")&lt;br /&gt;@ 1,1 SAY " Error no encuentra base de datos "&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;b) Valores lógicos. Representan pares de valores.&lt;br /&gt;&lt;br /&gt;.T. (Verdadero)&lt;br /&gt;.F. (Valso)&lt;br /&gt;&lt;br /&gt;.Y. (Si)&lt;br /&gt;.N. (No)&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;JUBILADO = .Y.&lt;br /&gt;IF JUBILADO&lt;br /&gt;..&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;11. Operadores relacionales.&lt;br /&gt;&lt;br /&gt;= (Igual que)&lt;br /&gt;== (Exactamente igual que)&lt;br /&gt;&gt; (Mayor que)&lt;br /&gt;&lt; (Menor que) &gt;= (Mayor igual que)&lt;br /&gt;&lt;= (Menor igual que) &lt;&gt; # (Distinto)&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DO CASE&lt;br /&gt;CASE MES = 1&lt;br /&gt;....&lt;br /&gt;....&lt;br /&gt;CASE MES &gt;= 2&lt;br /&gt;....&lt;br /&gt;....&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;VI. Operaciones de entrada y salida.&lt;br /&gt;&lt;br /&gt;1. Entrada, máscara, validación y rango.&lt;br /&gt;&lt;br /&gt;@...SAY/GET muestra en las coordenadas reseñadas el contenido de la expresión que sigue a SAY, carga valores a los campos o las variables de memoria que siguen a GET (hasta ser leídos por READ.)&lt;br /&gt;&lt;br /&gt;Las variables usadas han de ser declaradas previamente.&lt;br /&gt;&lt;br /&gt;@ . [SAY [PICTURE ]]&lt;br /&gt;[GET [PICTURE ]&lt;br /&gt;[RANGE ,]&lt;br /&gt;[VALID ]]&lt;br /&gt;&lt;br /&gt;PICTURE expresa un formato para la entrada/salida de información. Este formato puede estar controlado por plantillas o funciones. Las primeras se aplican carácter a carácter y las segundas afectan a toda la claúsula. Las funciones irán precedidas del símbolo @.&lt;br /&gt;&lt;br /&gt;RANGE sirve para validar datos numéricos, indicando un límite inferior y un superior. Entre estos límites deberá estar comprendido en dato numérico para que sea válido.&lt;br /&gt;&lt;br /&gt;VALID se emplea para expresiones genéricas de validación. será la condición de validación.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;nombre = SPACE(20)&lt;br /&gt;@ 2,1 SAY "Teclear nombre: " GET nombre PICTURE "@!"&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;edad = 0&lt;br /&gt;@ 3,3 SAY "Teclear edad: " GET edad PICTURE "999" RANGE 19,125&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;resp = SPACE(1)&lt;br /&gt;@ 5,5 say "¿ GRABAR ? " GET resp "!" VALID(resp$"SN")&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Ejemplo_4:&lt;br /&gt;&lt;br /&gt;importe = 0&lt;br /&gt;@ 5,5 SAY " TECLEAR IMPORTE: " GET importe PICTURE "@E9,999.99"&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Símbolos usados por PICTURE&lt;br /&gt;&lt;br /&gt;A Hace que un GET sólo admita letras&lt;br /&gt;L Idem sólo para valores lógicos.&lt;br /&gt;Y Permite sólo "Y" o "N"&lt;br /&gt;N Idem sólo letras y caracteres&lt;br /&gt;X Idem cualquier carácter&lt;br /&gt;9 Permite que sólo se visualicen dígitos&lt;br /&gt;# Idem sólo letras, espacios y signos&lt;br /&gt;! Idem sólo letras mayúsculas&lt;br /&gt;, Representa los miles en los datos numéricos&lt;br /&gt;$ Hace que se muestren $ para rellenar una cifra por la izquierda.&lt;br /&gt;* Idem con *&lt;br /&gt;&lt;br /&gt;Símbolos utilizados como funciones &lt;"@")  C Indica CR después de un número positivo X Indica DB después de un número negativo ( Encierra con paréntesis números negativos con espacios a la izquierda. ) Idem sin espacios a la izquierda B Justifica los números por la izquierda A Hacen que sólo se puedan captar caracteres alfabéticos ! Hace que sólo se permitan letras mayúsculas R Permite insertar caracteres que aparecerán solamente en pantalla, no almacenándose en la variable E Convierte los números al formato europeo D Visualiza las fechas en el formato especificado con SET DATE K Borra el contenido de la variable si no se pulsa primero un carácter de control del cursor S Hace scroll horizontal con la variable Z Hace que los valores cero en un campo numérico se representen como blancos.  ACCEPT acepta datos alfanuméricos por pantalla y los carga en . No es necesario haber declarado previamente .  ACCEPT [] TO  Ejemplo_1:  ACCEPT "Escribe tu nombre" TO nombre  INPUT Acepta datos por pantalla. Los datos han de ser identificados con sus correspondientes indicadores, así, por ejemplo, una cadena de carácteres deberá escribirse entrecomillada, mientras que esto no será preciso con un número.  INPUT [] TO  Ejemplo_1:  INPUT " Edad " TO edad  WAIT detiene la ejecución del programa y espera la pulsación de una tecla.  WAIT [] [TO ]  es una cadena de caracteres que se visualizarán a modo de información. Si se omite, aparecerá en pantalla: Press any key to continue...  es una variable que contendrá el carácter qu se ha pulsado  2. Pausa.  INKEY() detiene por un tiempo el flujo del programa y devuelve el valor de la tecla que se está pulsando.  INKEY([])  indica el número de segundos de espera. Si es igual a cero detiene el programa y espera que pulsemos una tecla cuyo valor ASCII toma.  Ejemplo_1:  tecla = INKEY(0)  Pulsando [enter],  tecla = 13  3. Conocer la última tecla pulsada.  LASTKEY() devuelve el valor de la última tecla pulsada. Dicho valor es un número que se corresponde con el valor ASCII del carácter.  Ejemplo_1:  INKEY(0) DO CASE CASE LASTKEY() = 27 RETURN CASE CHR(LASTKEY()) = "+" ..... CASE LASTKEY() = 13 ..... ENDCASE  4. Salida.  ?, ??, @ SAY, TEXT/ENDTEXT se emplean generalmente como instrucciones de salida (pantalla/impresora) para expresiones, cadenas, bloques de texto, etc.  Ejemplo_1:  TEXT ********************* ERROR ********************* ENDTEXT  Ejemplo_2:  a=4 b=5 c=3 ? (a*b)**c  5. Borrar pantalla.  CLEAR borra la pantalla, manteniendo los atributos de color vigente, y libera todos los GET pendientes. Asimismo, posiciona el cursor en la posición 0,0 (posiciones verticales 0 a 24 / posiciones horizontales 0 a 79).  @..CLEAR TO borra un área de pantalla.  Ejemplo_1:  @ 3,3 CLEAR TO 9,9  6. Dibujar un marco.  @..TO dibuja un marco de línea sencilla en las coordenadas especificadas. Si se emplea la opción DOUBLE, el marco dibujado será de línea doble.  @ , TO , [DOUBLE]  7. Dibujar una caja.  @..BOX construye una caja entre las coordenadas indicadas y con los códigos ASCII especificados en . El orden de los caracteres es:  1. Esquina superior izquierda 2. Línea horizontal superior 3. Esquina superior derecha 4. Línea vertical derecha 5. Esquina inferior derecha 6. Línea horizontal inferior 7. Esquina inferior izquierda 8. Línea vertical izquierda 9. Carácter de relleno  @ ,,, BOX  Ejemplo_1:  cadena = "+-+¦+-+¦¦" @ 1,1,10,10 BOX cadena  8. Hacer un menú.  @..PROMPT facilita la creación de menús en nuestros programas. Cada opción se muestra con un PROMPT en una posición especifica de la pantalla y se le acompaña opcionalmente de un mensaje aclaratorio.  @ , PROMPT [MESSAGE ]  SET WRAP ON/OFF posibilita la rotación al alcanzar la primera o última opción.  SET MESSAGE determina el número de fila donde aparecerán los mensajes de las distintas opciones.  SET MESSAGE TO [ [CENTER/CENTRE]]  CENTER/CENTRE muestra el mensaje en la fila especificada centrándolo.  MENU TO sirve para leer el valor numérico que representa a la opción seleccionada. Dicho valor se asigna automáticamente y representa el número de orden de cada PROMPT.  Ejemplo_1:  SET WRAP ON SET MESSAGE TO 23 CENTER @ 1,1 PROMPT "ALTA " MESSAGE "Alta de usuarios " @ 2,1 PROMPT "BAJA " MESSAGE "Baja de usuarios " @ 3,1 PROMPT "LISTADO " MESSAGE "Listado DESDE/HASTA" MENU TO opcion DO CASE CASE opcion = 1 ..... CASE opcion = 2 ..... CASE opcion = 3 ..... ENDCASE  9. Salvar/Restaurar pantallas.  SAVE SCREEN salva la pantalla actual así como su estructura de variables leídas y pendientes de leer.  SAVE SCREEN [TO ]  TO indica que la pantalla será almacenada en la variable de memoria . Esta variable será de tipo carácter.  RESTORE SCREEN restaura una pantalla almacenada previamente  RESTORE SCREEN [FROM ]  SAVESCREEN() almacena una parte de la pantalla en una variable de memoria  = SAVESCREEN(,,,)  RESTSCREEN() restaura una área de una pantalla salvada previamente.  RESTSCREEN(,,,,)  Ejemplo_1:  @ 2,3 SAY " -------- " @ 3,3 SAY " CLIENTES " @ 4,3 SAY " -------- " SAVE SCREEN TO panta CLEAR INKEY(0) RESTORE FROM panta RETURN  Ejemplo_2:  @ 2,3 SAY " -------- " @ 3,3 SAY " CLIENTES " @ 4,3 SAY " -------- " panta = SAVESCREEN(3,3,4,12) CLEAR INKEY(0) RESTSCREEN(5,5,6,14,panta)  VII. Bifurcación y bucles.  1. IF (Si cumple condición...).  Bifurca un programa entre una condición y su opuesta. Puede usarse como mandato o como función. En el primer caso, lo que hace es ejecutar alternativamente unas instrucciones u otras y en el segundo devolver alternativamente un valor u otro. La sintáxis de la función puede ser IF() o IIF().  Mandato:  IF  [ELSEIF ]  [ELSE ] ENDIF  Función:  IIF/IF(,&lt;.T.&gt;,&lt;.F.&gt;)&lt;br /&gt;&lt;br /&gt;es la condición que se desea establecer&lt;br /&gt;&lt;br /&gt;ELSEIF reconoce órdenes cuando se cumple la que condición expresada.&lt;br /&gt;&lt;br /&gt;ELSE realiza las distintas órdenes que se indican cuando la condición es falsa.&lt;br /&gt;&lt;br /&gt;&lt;.T.&gt; Indica la expresión a evaluar para el valor verdadero de la condición.&lt;br /&gt;&lt;br /&gt;&lt;.F.&gt; Indica la expresión a evaluar para el valor falso de la condición.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF sexo = "V"&lt;br /&gt;peso = 20&lt;br /&gt;ELSE&lt;br /&gt;peso = 12&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;IF porcentaje &gt; 10&lt;br /&gt;porcentaje = porcentaje - 2&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;salario = salario + IIF(ho&gt;80,80000+1500*(ho-80),80000)&lt;br /&gt;&lt;br /&gt;2. DO CASE (En caso de cumplir condición...).&lt;br /&gt;&lt;br /&gt;Bifurca la ejecución de un programa según las diferentes condiciones especificadas. OTHERWISE representa todos los casos que no cumplen ninguna condición.&lt;br /&gt;&lt;br /&gt;DO CASE&lt;br /&gt;CASE&lt;br /&gt;&lt;br /&gt;CASE&lt;br /&gt;&lt;br /&gt;OTHERWISE&lt;br /&gt;&lt;br /&gt;ENDCASE&lt;br /&gt;&lt;br /&gt;son las diferentes condiciones.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DO CASE&lt;br /&gt;CASE velocidad &gt; 180&lt;br /&gt;consumo = 4&lt;br /&gt;CASE velocidad &gt; 120&lt;br /&gt;consumo = 3&lt;br /&gt;CASE velocidad &gt; 80&lt;br /&gt;consumo = 2&lt;br /&gt;OTHERWISE&lt;br /&gt;consumo = 1&lt;br /&gt;ENDCASE&lt;br /&gt;&lt;br /&gt;3. FOR..NEXT (Desde un valor hasta alcanzar otro).&lt;br /&gt;&lt;br /&gt;Permite la creación de una estructura de bucle que se ejecuta para un rango de valores determinados de forma ascendente o descendente.&lt;br /&gt;&lt;br /&gt;FOR TO [STEP ]&lt;br /&gt;&lt;br /&gt;[EXIT]&lt;br /&gt;&lt;br /&gt;[LOOP]&lt;br /&gt;NEXT&lt;br /&gt;&lt;br /&gt;es el valor inicial. Este valor se asignará a una variable de control.&lt;br /&gt;&lt;br /&gt;es el valor final del bucle.&lt;br /&gt;&lt;br /&gt;STEP indica el incremento o decremento de la variable. Por defecto incrementa en 1.&lt;br /&gt;&lt;br /&gt;EXIT detiene el bucle pasando el control a la sentencia posterior a NEXT.&lt;br /&gt;&lt;br /&gt;LOOP pasa de nuevo el control al comienzo del bucle, sin necesidad de que se llegue a NEXT.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;FOR N=1 TO 10&lt;br /&gt;CUADRADO = N**N&lt;br /&gt;? CUADRADO&lt;br /&gt;NEXT&lt;br /&gt;&lt;br /&gt;4. DO WHILE (Hacer mientras que cumpla condición...).&lt;br /&gt;&lt;br /&gt;DO WHILE realiza una estructura de bucle mientras se cumpla la condición especificada. DO WHILE comienza y continúa el bucle si se cumple la condición. ENDDO devuelve el control al principio.&lt;br /&gt;&lt;br /&gt;DO WHILE&lt;br /&gt;&lt;br /&gt;[EXIT]&lt;br /&gt;[LOOP]&lt;br /&gt;ENDDO&lt;br /&gt;&lt;br /&gt;es la condición que se debe cumplir para que se ejecute el bucle.&lt;br /&gt;&lt;br /&gt;LOOP manda todo el proceso de nuevo al comienzo del bucle, sin necesidad de que se llegue al final, es decir a ENDDO.&lt;br /&gt;&lt;br /&gt;EXIT fuerza a que se pare el proceso y sale del bucle aunque la condición no haya cesado de darse.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DO WHILE .T.&lt;br /&gt;@ 2,2 PROMPT "CLIENTES"&lt;br /&gt;@ 3,2 PROMPT "PROVEEDORES"&lt;br /&gt;MENU TO opcion&lt;br /&gt;DO CASE&lt;br /&gt;CASE opcion = 1&lt;br /&gt;DO CLI&lt;br /&gt;CASE opcion = 2&lt;br /&gt;DO PRO&lt;br /&gt;CASE LASTKEY() = 27&lt;br /&gt;CLEAR&lt;br /&gt;RETURN&lt;br /&gt;ENDCASE&lt;br /&gt;ENDDO&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;C=0&lt;br /&gt;DO WHILE C&lt;100 c="C+1"&gt;&lt;br /&gt;&lt;br /&gt;VIII. Fin.&lt;br /&gt;&lt;br /&gt;1. Retornar.&lt;br /&gt;&lt;br /&gt;RETURN termina un procedimiento, programa, o función, devolviendo el control al procedimiento de llamada o al DOS.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DO BORRAR WITH 2,2,20,20&lt;br /&gt;..&lt;br /&gt;..&lt;br /&gt;..&lt;br /&gt;QUIT&lt;br /&gt;&lt;br /&gt;PROCEDURE BORRAR&lt;br /&gt;PARAMETERS X1,Y1,X2,Y2&lt;br /&gt;@ X1,Y1 CLEAR TO X2,Y2&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;2. Terminar.&lt;br /&gt;&lt;br /&gt;QUIT termina la ejecución de un programa devolviendo el control al DOS.&lt;br /&gt;&lt;br /&gt;Este mandato realiza la misma función que CANCEL o que RETURN en el procedimiento de más alto nivel.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE FICHERO INDEX INDICE&lt;br /&gt;SEEK CLAVE&lt;br /&gt;IF FOUND()&lt;br /&gt;DO PROCESO&lt;br /&gt;ELSE&lt;br /&gt;QUIT&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;3. Cancelar.&lt;br /&gt;&lt;br /&gt;CANCEL cancela la ejecución de un programa o procedimiento, devolviendo el control al sistema operativo.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;clave = SPACE(4)&lt;br /&gt;@ 4,4 SAY "Clave: " GET clave PICTURE "@!"&lt;br /&gt;READ&lt;br /&gt;IF clave # "9876"&lt;br /&gt;CANCEL&lt;br /&gt;ELSE&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;IX. Mantenimiento de ficheros.&lt;br /&gt;&lt;br /&gt;1. Renombrar fichero.&lt;br /&gt;&lt;br /&gt;RENAME renombra ficheros. Es el equivalente al RENAME del DOS ,aunque su sintaxis es algo distinta.&lt;br /&gt;&lt;br /&gt;RENAME TO&lt;br /&gt;&lt;br /&gt;es el nombre inicial del fichero y es el nuevo nuevo. Tanto como deben incluir la extesión del fichero.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;RENAME CLIENTES.DBF TO CLIENTES.DAT&lt;br /&gt;USE CLIENTES.DAT&lt;br /&gt;&lt;br /&gt;2. Copiar ficheros.&lt;br /&gt;&lt;br /&gt;COPY FILE copia el contenido de en . No sirven con este mandato los comodines para copiar varios ficheros en bloque. Salvo esta excepción funciona igual que el COPY del DOS.&lt;br /&gt;&lt;br /&gt;Es importante recordar que siempre hemos de proporcionarle las vías donde buscar los ficheros a copiar y donde queremos copiarlos. Si no se le especifica esta última el fichero se deposita en el directorio de trabajo.&lt;br /&gt;&lt;br /&gt;COPY FILE TO&lt;br /&gt;&lt;br /&gt;es el fichero origen y el fichero destino.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;COPY FILE CLIENTES.DAT TO CLIENTES.DBF&lt;br /&gt;&lt;br /&gt;3. Borrar ficheros.&lt;br /&gt;&lt;br /&gt;DELETE FILE y ERASE borran ficheros. Al especificar el nombre del fichero a borrar debe figurar también su extensión. Antes de usar este comando es necesario cerrar el fichero a borrar con el comando CLOSE.&lt;br /&gt;&lt;br /&gt;ERASE/DELETE FILE&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;..&lt;br /&gt;..&lt;br /&gt;CLOSE DATABASES&lt;br /&gt;DELETE FILE CLIENTES.DBF&lt;br /&gt;&lt;br /&gt;4. LLamada al Dos.&lt;br /&gt;&lt;br /&gt;Además de las órdenes elementales de mantenimiento de ficheros vistas anteriormente, existe la posibilidad de invocar cualquiera del DOS con RUN o !. Por ejemplo, para salir temporalmente de un programa podemos incluir un RUN COMMAND.COM y regresar con EXIT.&lt;br /&gt;&lt;br /&gt;RUN&lt;br /&gt;!&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;RUN CHKDSK &gt; CHEQDIS.TXT&lt;br /&gt;&lt;br /&gt;5. Comprobar la existencia de un fichero.&lt;br /&gt;&lt;br /&gt;Antes de realizar cualquier operación con un fichero podemos comprobar su existencia con la función FILE() que nos retornará un verdadero (.T.) o un falso (.F.).&lt;br /&gt;&lt;br /&gt;FILE()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF FILE("CLIENTES.DBF")&lt;br /&gt;SORT ON NOMBRE TO CLISORT&lt;br /&gt;DELETE FILE CLIENTES.DBF&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;X. Procedimientos y funciones.&lt;br /&gt;&lt;br /&gt;1. Procedimiento.&lt;br /&gt;&lt;br /&gt;PROCEDURE indica el principio de un procedimiento.&lt;br /&gt;&lt;br /&gt;PROCEDURE&lt;br /&gt;&lt;órdenes&gt;&lt;br /&gt;[RETURN]&lt;br /&gt;&lt;br /&gt;- Debe de empezar con una letra y sólo evalúa los 10 primeros caracteres.&lt;br /&gt;&lt;br /&gt;RETURN - Es aconsejable su uso para determinar el fin de un procedimiento, aunque no necesario, ya que detecta el fin al encontrar otro procedure o una marca de fin de archivo.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;CLEAR&lt;br /&gt;DO FONDO&lt;br /&gt;INKEY(0)&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;PROCEDURE Fondo&lt;br /&gt;FOR I=0 TO 24&lt;br /&gt;@ I, 0 SAY REPLICATE("¦", 80 )&lt;br /&gt;NEXT&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;2. LLamada a un procedimiento.&lt;br /&gt;&lt;br /&gt;DO ejecuta un procedimiento escrito en Clipper, C o ensamblador, pasándole parámetros (hasta 128) con WITH.&lt;br /&gt;&lt;br /&gt;DO [WITH ]&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;PROCEDURE LISTACURSOS&lt;br /&gt;IF !ISPRINTER()&lt;br /&gt;DO MSGIMPRESORA&lt;br /&gt;ENDIF&lt;br /&gt;* órdenes ...&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;PROCEDURE MSGIMPRESORA&lt;br /&gt;CLEAR&lt;br /&gt;@ 9, 28 TO 12, 51&lt;br /&gt;@ 10,30 SAY "CONECTE LA IMPRESORA"&lt;br /&gt;@ 11,32 SAY "Y PULSE UNA TECLA"&lt;br /&gt;INKEY(0)&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;3. Creación de un fichero de procedimientos.&lt;br /&gt;&lt;br /&gt;SET PROCEDURE Activa los archivos de procedimientos especificados.&lt;br /&gt;&lt;br /&gt;SET PROCEDURE TO [ ]&lt;br /&gt;&lt;br /&gt;- Si se omite la extensión, se asume que es (.PRG).&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;SET PROCEDURE TO CLIENTES&lt;br /&gt;SET PROCEDURE TO PROVEED&lt;br /&gt;SET PROCEDURE TO MATERIAL&lt;br /&gt;&lt;br /&gt;4. Nombre del procedimiento y número de línea.&lt;br /&gt;&lt;br /&gt;PROCNAME() indica el nombre del procedimiento o programa que estamos ejecutando.&lt;br /&gt;&lt;br /&gt;PROCNAME()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;? "Procedimiento en uso : ", procname()&lt;br /&gt;&lt;br /&gt;PROCLINE() Devuelve el número de la línea del código fuente en curso del programa. Siempre que no le hayamos indicado al compilador que no numere las líneas.&lt;br /&gt;&lt;br /&gt;PROCLINE()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;? procline(), "Linea ", cLinea&lt;br /&gt;&lt;br /&gt;5. Creación de una función.&lt;br /&gt;&lt;br /&gt;FUNCTION Declara una función definida por el usuario escrita en Clipper.&lt;br /&gt;&lt;br /&gt;FUNCTION&lt;br /&gt;&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;- Solo acepta los diez primeros caracteres.&lt;br /&gt;&lt;br /&gt;- Es obligatorio la devolución de un valor.&lt;br /&gt;&lt;br /&gt;Para llamar a una función de usuario, proceda del siguiente modo:&lt;br /&gt;&lt;br /&gt;función( )&lt;br /&gt;&lt;br /&gt;Los parámetros se pasan por valor, exceptuando los arrays, o si el parámetros es precedido por una arroba (@), entonces es pasado por referencia.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;CLEAR&lt;br /&gt;@ 24,0 SAY ISBISIESTO( DATE() )&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;FUNCTION ISBISIESTO&lt;br /&gt;PARAMETERS DFECHA&lt;br /&gt;PRIVATE DANY, CCADENA, LDEVUELVE&lt;br /&gt;DANY = YEAR( DFECHA )&lt;br /&gt;CCADENA = CTOD( "29-02-" + STR(DANY))&lt;br /&gt;IF DOW(CCADENA)=0&lt;br /&gt;LDEVUELVE = .F.&lt;br /&gt;ELSE&lt;br /&gt;LDEVUELVE = .T.&lt;br /&gt;ENDIF&lt;br /&gt;RETURN LDEVUELVE&lt;br /&gt;&lt;br /&gt;6. Conocer el número de parámetros.&lt;br /&gt;&lt;br /&gt;PCOUNT() Determina el número de parámetros pasados a un procedimiento o función definida por el usuario.&lt;br /&gt;&lt;br /&gt;PCOUNT()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;PROCEDURE EDITOR&lt;br /&gt;PARAMETERS CFICHERO&lt;br /&gt;IF PCOUNT() = 0&lt;br /&gt;@ 24,0 SAY "INDIQUE EL FICHERO: " GET CFICHERO&lt;br /&gt;READ&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;XI. Tablas.&lt;br /&gt;&lt;br /&gt;1 Declaración tablas.&lt;br /&gt;&lt;br /&gt;Una tabla es un área de memoria que puede reservarse para contener un grupo de datos. Una tabla consta de un identificativo o nombre y un número definible de posiciones (de 1 a 1024 en Clipper '87). Estas posiciones pueden contener datos numéricos, alfabéticos, fechas, etc. Para acceder a uno de los datos contenido en una tabla se hará indicando el número de posición que ocupa. Existen varias funciones que posibilitan realizar operaciones en una tabla tales como añadir nuevos datos, eliminar datos, rellenar, etc. Este tipo de estructuras de memoria se utilizan como soporte temporal de los datos.&lt;br /&gt;&lt;br /&gt;DECLARE declara una o más áreas de memoria (arrays) con una longitud específica. Antes de poder realizar cualquier operación con una tabla debemos declararla.&lt;br /&gt;&lt;br /&gt;DECLARE []{,[]...}&lt;br /&gt;&lt;br /&gt;es el nombre de la tabla&lt;br /&gt;es la longitud de la tabla (1-1024)&lt;br /&gt;Ejemplo_1: DECLARE PROVINCIA[8]&lt;br /&gt;PROVINCIA[1] = "ALMERIA"&lt;br /&gt;PROVINCIA[2] = "CADIZ "&lt;br /&gt;PROVINCIA[3] = "CORDOBA"&lt;br /&gt;....&lt;br /&gt;&lt;br /&gt;Ejemplo_2: numero = 8&lt;br /&gt;DECLARE PROVINCIA[numero]&lt;br /&gt;Ejemplo_3: tipo = "FICHA"&lt;br /&gt;numero = "01"&lt;br /&gt;tabla = tipo+numero&lt;br /&gt;DECLARE &amp;amp;tabla[4]&lt;br /&gt;&amp;amp;tabla[1] = "ANDALUCIA"&lt;br /&gt;&amp;amp;tabla[2] = 8&lt;br /&gt;&amp;amp;tabla[3] = .T.&lt;br /&gt;&amp;amp;tabla[4] = CTOD("01/01/92")&lt;br /&gt;&lt;br /&gt;2 Longitud.&lt;br /&gt;&lt;br /&gt;LEN es una función que devuelve el número de elementos que tiene una tabla, o lo que es lo mismo la longitud de la tabla indicada.&lt;br /&gt;LEN()&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla&lt;br /&gt;&lt;br /&gt;Ejemplo_1: DECLARE PROVINCIA[8]&lt;br /&gt;? LEN(PROVINCIA)&lt;br /&gt;&lt;br /&gt;3 Insertar.&lt;br /&gt;&lt;br /&gt;La inserción de nuevos elementos en una tabla es posible mediante la función AINS indicándose el nombre de la tabla y la posición donde se desea insertar el nuevo elemento. Automáticamente, el elemento insertado desplazará a los posteriores en una posición y el último se perderá.&lt;br /&gt;AINS(,)&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla&lt;br /&gt;- Posición elemento&lt;br /&gt;&lt;br /&gt;Ejemplo_1: DECLARE NOMBRE[3]&lt;br /&gt;NOMBRE[1] = "LUIS"&lt;br /&gt;NOMBRE[2] = "MARIA"&lt;br /&gt;NOMBRE[3] = "CARLOS"&lt;br /&gt;AINS(NOMBRE,2)&lt;br /&gt;NOMBRE[2] = "MANUEL"&lt;br /&gt;? NOMBRE[1]&lt;br /&gt;? NOMBRE[2]&lt;br /&gt;? NOMBRE[3]&lt;br /&gt;&lt;br /&gt;4 Suprimir.&lt;br /&gt;&lt;br /&gt;ADEL suprime elementos en una tabla redimensionándola.&lt;br /&gt;&lt;br /&gt;ADEL(,)&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla&lt;br /&gt;- Posición elemento&lt;br /&gt;&lt;br /&gt;Ejemplo_1: DECLARE NOMBRE[3]&lt;br /&gt;NOMBRE[1] = "LUIS"&lt;br /&gt;NOMBRE[2] = "MARIA"&lt;br /&gt;NOMBRE[3] = "CARLOS"&lt;br /&gt;ADEL(NOMBRE,2)&lt;br /&gt;? NOMBRE[1]&lt;br /&gt;? NOMBRE[2]&lt;br /&gt;&lt;br /&gt;5 Copiar.&lt;br /&gt;&lt;br /&gt;La copia de un elemento o grupo de elementos de una tabla a otra tabla la realiza la función ACOPY, debiendo indicarse la tabla origen, la tabla destino, la posición inicial de la tabla origen, el nº de elementos a copiar y el elemento de la tabla destino donde ha de comenzarse la copia.&lt;br /&gt;&lt;br /&gt;ACOPY(,[, [,[,]]])&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla origen&lt;br /&gt;- Nombre de la tabla destino&lt;br /&gt;- Posición origen en tabla origen a copiar&lt;br /&gt;- Número de elementos a copiar desde&lt;br /&gt;- Elemento destino a comenzar copia&lt;br /&gt;Ejemplo_1: DECLARE TABLA_A[2],TABLA_B[2]&lt;br /&gt;TABLA_A[1] = "LUIS"&lt;br /&gt;TABLA_A[2] = "MARIA"&lt;br /&gt;ACOPY(TABLA_A,TABLA_B)&lt;br /&gt;? TABLA_B[1]&lt;br /&gt;? TABLA_B[2]&lt;br /&gt;&lt;br /&gt;Ejemplo_2: DECLARE TABLA_A[2],TABLA_B[3]&lt;br /&gt;TABLA_A[1] = "A"&lt;br /&gt;TABLA_A[2] = "B"&lt;br /&gt;ACOPY(TABLA_A,TABLA_B,1,1,3)&lt;br /&gt;? TABLA_B[3]&lt;br /&gt;&lt;br /&gt;6 Rellenar.&lt;br /&gt;&lt;br /&gt;AFILL rellena uno o más elementos con la expresión indicada.&lt;br /&gt;&lt;br /&gt;AFILL(,[,[,]])&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla&lt;br /&gt;- Expresión con la que se rellenar la tabla&lt;br /&gt;- Posición donde comenzar a rellenar&lt;br /&gt;- Número de elementos a rellenar desde&lt;br /&gt;Ejemplo_1: DECLARE TLF[2]&lt;br /&gt;TLF[1] = "433-23-23"&lt;br /&gt;TLF[2] = "433-23-24"&lt;br /&gt;AFILL(TLF,"000-00-00",2,1)&lt;br /&gt;? TLF[1]&lt;br /&gt;? TLF[2]&lt;br /&gt;&lt;br /&gt;7 Directorio.&lt;br /&gt;&lt;br /&gt;ADIR accede al directorio del disco almacenado en tablas información relativa a los ficheros y directorios.&lt;br /&gt;&lt;br /&gt;ADIR( [, [, [, [, [,]]]]])&lt;br /&gt;- máscaras posibles en DOS (*/?) o nombre fichero.&lt;br /&gt;- Es la tabla que se rellenar con los nombres de ficheros reseñados en . Tipo C. - Idem. para tamaño en bytes de fichero. Tipo N.&lt;br /&gt;- Idem. para fechas. Tipo D&lt;br /&gt;- Idem. para horas. Tipo C&lt;br /&gt;- Idem. para atributos. Tipo C&lt;br /&gt;&lt;br /&gt;Atributos: A - Fichero archivo D - Directorio H - Oculto R - Sólo Lectura S - Sistema&lt;br /&gt;&lt;br /&gt;Ejemplo_1: fil_prg = ADIR("*.PRG")&lt;br /&gt;&lt;br /&gt;Ejemplo_2: DECLARE TABLA[ADIR("*.PRG")]&lt;br /&gt;&lt;br /&gt;Ejemplo_3: fil_sec = ADIR("*.sec")&lt;br /&gt;DECLARE&lt;br /&gt;NOMBRE[fil_sec],FECHA[fil_sec] ADIR("*.sec",NOMBRE,"",FECHA)&lt;br /&gt;FOR n=1 TO fil_sec&lt;br /&gt;fil_del = NOMBRE[n]&lt;br /&gt;IF FECHA[n] &lt;&gt;&lt;br /&gt;&lt;br /&gt;8 Estructura.&lt;br /&gt;&lt;br /&gt;La estructura de una base de datos puede conocerse mediante la función AFIELDS. Los nombres de campos, tipo, longitud, etc. pueden almacenarse en tablas para el posterior tratamiento.&lt;br /&gt;&lt;br /&gt;AFIELDS( [, [, [, ]]]])&lt;br /&gt;&lt;br /&gt;- Tabla que contendrá nombre de campos.&lt;br /&gt;- Tabla que contendrá tipo de campos.&lt;br /&gt;- Tabla que contendrá longitud de campos.&lt;br /&gt;- Tabla que contendrá número posiciones decima les.&lt;br /&gt;&lt;br /&gt;Ejemplo_1: USE base&lt;br /&gt;num_cam = FCOUNT()&lt;br /&gt;DECLARE NOMBRE[num_cam],TIPO[num_cam] AFIELDS(NOMBRE,TIPO)&lt;br /&gt;FOR n=1 TO num_cam&lt;br /&gt;? NOMBRE[n]&lt;br /&gt;? TIPO[n]&lt;br /&gt;NEXT&lt;br /&gt;&lt;br /&gt;9 Menú.&lt;br /&gt;&lt;br /&gt;ACHOICE es una función que permite generar un menú de persiana con los elementos de una tabla en las posiciones de pantalla que se indiquen. Devuelve un valor de tipo numérico que se corresponde con el número de posición del elemento seleccionado. Si el valor es 0 no se seleccionó ningún elemento.&lt;br /&gt;&lt;br /&gt;ACHOICE(,,,, [,[,[,[,]]]])&lt;br /&gt;&lt;br /&gt;- Coordenada X1 de pantalla&lt;br /&gt;- Coordenada Y1 de pantalla - Coordenada X2 de pantalla&lt;br /&gt;- Coordenada Y2 de pantalla&lt;br /&gt;- tabla que contendrá elementos&lt;br /&gt;- tabla que contendrá valores lógicos&lt;br /&gt;- Función de usuario.&lt;br /&gt;&lt;br /&gt;Pasa 3 parámetros:&lt;br /&gt;1-modalidad: 0 Período de inactividad 1 Se intenta sobrepasar el principio&lt;br /&gt;2 Se intenta sobrepasar el final 3 Espera de tecla específica 4 No se puede escoger una opción&lt;br /&gt;2-elemento actual de la tabla&lt;br /&gt;&lt;br /&gt;3-posición que ocupa el elemento en la ventana&lt;br /&gt;Valores retorno: 0 Suspende selección&lt;br /&gt;1 Devuelve elemento cursor 2 Contin#a proceso selección&lt;br /&gt;3 Va al elemento cuyo primer&lt;br /&gt;carácter corresponde a la última tecla oprimida.&lt;br /&gt;Ejemplo_1: CLEAR&lt;br /&gt;SET SCOREBOARD OFF&lt;br /&gt;SET COLOR TO W+/N,,,,BG/N&lt;br /&gt;DECLARE MEN[3],LOG[3]&lt;br /&gt;MEN[1] = "ALTA "&lt;br /&gt;MEN[2] = "BAJA "&lt;br /&gt;MEN[3] = "MODIFICACION"&lt;br /&gt;LOG[1] = .T.&lt;br /&gt;LOG[2] = .T.&lt;br /&gt;LOG[3] = .T.&lt;br /&gt;clave = SPACE(2)&lt;br /&gt;@ 1,01 SAY "Clave: " GET clave PICTURE "XX" READ&lt;br /&gt;DO CASE&lt;br /&gt;CASE clave = "11"&lt;br /&gt;LOG[3] = .F.&lt;br /&gt;CASE clave = "22"&lt;br /&gt;LOG[2] = .F.&lt;br /&gt;OTHERWISE&lt;br /&gt;RETURN&lt;br /&gt;ENDCASE&lt;br /&gt;@ 4,1 TO 8,19&lt;br /&gt;opcion = ACHOICE(5,2,7,18,MEN,LOG)&lt;br /&gt;10 Base Datos&lt;br /&gt;&lt;br /&gt;DBEDIT visualiza el contenido de una base de datos en pantalla. Es una potente función que permite la edición de los datos sobre una ventana definida en pantalla.&lt;br /&gt;&lt;br /&gt;DBEDIT([[,,[,[]]]] [,],[,][,] [,][,] [,][,] [,])&lt;br /&gt;&lt;br /&gt;posiciones.&lt;br /&gt;- Tabla de nombres de los campos.&lt;br /&gt;- Función de usuario.&lt;br /&gt;- Tabla de modelos de visualización.&lt;br /&gt;- Tabla de encabezados de columnas.&lt;br /&gt;- Tabla de separación de encabezados. - Tabla de separación de columnas.&lt;br /&gt;- Tabla de separación de pies.&lt;br /&gt;- Tabla de pies.&lt;br /&gt;&lt;br /&gt;Cuando se utiliza una función de usuario, DBEDIT() pasa de forma automática dos parámetros:&lt;br /&gt;1-Estado actual de DBEDIT() dependiendo de la última tecla pulsada antes de llamar a la función. Las diferentes modalidades del estado son:&lt;br /&gt;&lt;br /&gt;0 Inactividad&lt;br /&gt;1 Se ha intentado sobrepasar el primer&lt;br /&gt;2 Se ha intentado sobrepasar el último registro&lt;br /&gt;3 El fichero de datos se encuentra vacio&lt;br /&gt;4 Se ha pulsado una tecla específica&lt;br /&gt;&lt;br /&gt;2-Posición que ocupa en la tabla el campo sobre el que nos encontramos posicionados.&lt;br /&gt;Valores de retorno:&lt;br /&gt;&lt;br /&gt;0 Para salir de DBEDIT()&lt;br /&gt;1 Para continuar la ejecución de DBEDIT() 2 Se vuelven a leer los datos nuevamente y se continúa DBEDIT()&lt;br /&gt;3 Se activa la posibilidad de añadir nuevos registros&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt;TAB1[3],TAB2[3],TAB3[3],TAB4[3],TAB5[3],TAB6[3],TAB7[3]&lt;br /&gt;* Nombre campos&lt;br /&gt;&lt;br /&gt;TAB1[1]= "BAS_LOC"&lt;br /&gt;TAB1[2]= "BAS_PRO"&lt;br /&gt;TAB1[3]= "BAS_HAB"&lt;br /&gt;&lt;br /&gt;* Máscaras de visualización&lt;br /&gt;&lt;br /&gt;TAB2[1]= "XXXXXXX"&lt;br /&gt;TAB2[2]= "XXXXXXX"&lt;br /&gt;TAB2[3]= "999,999,999"&lt;br /&gt;&lt;br /&gt;* Encabezados de columna&lt;br /&gt;&lt;br /&gt;TAB3[1]= "LOCALIDAD"&lt;br /&gt;TAB3[2]= "PROVINCIA"&lt;br /&gt;&lt;br /&gt;TAB3[3]= "HABITANTES"&lt;br /&gt;&lt;br /&gt;* Separadores de encabezados&lt;br /&gt;&lt;br /&gt;TAB4[1]= "D"&lt;br /&gt;TAB4[2]= "D"&lt;br /&gt;TAB4[3]= "D"&lt;br /&gt;&lt;br /&gt;* Separadores de columnas&lt;br /&gt;&lt;br /&gt;TAB5[1]= "3"&lt;br /&gt;TAB5[2]= "3"&lt;br /&gt;TAB5[3]= "3"&lt;br /&gt;&lt;br /&gt;* Separadores de pies de página&lt;br /&gt;&lt;br /&gt;TAB6[1]= "D"&lt;br /&gt;TAB6[2]= "D"&lt;br /&gt;TAB6[3]= "D"&lt;br /&gt;&lt;br /&gt;* Pies de página&lt;br /&gt;&lt;br /&gt;TAB7[1]= "DPIE_1D"&lt;br /&gt;TAB7[2]= "DPIE_2D"&lt;br /&gt;TAB7[3]= "DPIE_3D"&lt;br /&gt;&lt;br /&gt;CLEAR&lt;br /&gt;USE BASE&lt;br /&gt;DBEDIT(1,1,7,40,TAB1,"",TAB2,TAB3,TAB4,TAB5,TAB6,TAB7)&lt;br /&gt;&lt;br /&gt;XII. Impresora.&lt;br /&gt;&lt;br /&gt;1. Salida.&lt;br /&gt;&lt;br /&gt;SET DEVICE redirecciona las salidas por pantalla o por impresora. Por defecto es por pantalla.&lt;br /&gt;&lt;br /&gt;SET DEVICE TO SCREEN/PRINTER&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;PROCEDURE LISTACURSOS&lt;br /&gt;IF !ISPRINTER() &amp;amp;&amp;amp; Impresora no conectada&lt;br /&gt;DO MSGIMPRESORA &amp;amp;&amp;amp; Mensaje que conecte&lt;br /&gt;INKEY(0)&lt;br /&gt;ENDIF&lt;br /&gt;SET DEVICE TO PRINT&lt;br /&gt;USE CURSOS INDEX CURSOS&lt;br /&gt;GO TOP&lt;br /&gt;NPAGINA = 1 &amp;amp;&amp;amp; Contador de páginas&lt;br /&gt;NFILA = 5 &amp;amp;&amp;amp; Contador de filas&lt;br /&gt;@ 0,0 SAY CHR(15) &amp;amp;&amp;amp; Impresión comprimida&lt;br /&gt;&lt;br /&gt;* CABECERA&lt;br /&gt;&lt;br /&gt;@ 1,0 SAY "LISTADO CURSOS"&lt;br /&gt;@ 1,115 SAY "PAGINA.: " + LTRIM(STR(NPAGINA))&lt;br /&gt;@ 2,115 SAY "FECHA..: " + DTOC(DATE())&lt;br /&gt;@ 3,0 SAY "Nº"&lt;br /&gt;@ 3,10 SAY "NOMBRE CURSO"&lt;br /&gt;@ 3,90 SAY "PRECIO"&lt;br /&gt;@ 4,0 SAY REPLICATE(CHR(196),132)&lt;br /&gt;&lt;br /&gt;* FIN CABECERA&lt;br /&gt;&lt;br /&gt;DO WHILE .NOT. EOF()&lt;br /&gt;@ NFILA, 0 SAY RECNO() PICTURE "9999"&lt;br /&gt;@ NFILA,10 SAY NOMCURSO&lt;br /&gt;@ NFILA,90 SAY PRECIO&lt;br /&gt;NFILA = NFILA + 1&lt;br /&gt;IF NFILA = 50&lt;br /&gt;EJECT &amp;amp;&amp;amp; Salto de página&lt;br /&gt;NPAGINA = NPAGINA + 1&lt;br /&gt;&lt;br /&gt;* REPETICION DE LA CABECERA&lt;br /&gt;&lt;br /&gt;NFILA = 5&lt;br /&gt;ENDIF&lt;br /&gt;SKIP &amp;amp;&amp;amp; Incrementamos registro&lt;br /&gt;ENDDO&lt;br /&gt;CLOSE&lt;br /&gt;EJECT&lt;br /&gt;@ 0,0 SAY CHR(18) &amp;amp;&amp;amp; Desactivamos comprimido&lt;br /&gt;SET DEVICE TO SCREEN&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;2. Salto de página.&lt;br /&gt;&lt;br /&gt;EJECT realiza un salto de página en la impresora, y pone a cero los valores de la fila y la columna de la impresora.&lt;br /&gt;&lt;br /&gt;Use SETPRC() si necesita poner a cero los valores internos de fila y columna de la impresora sin enviar un salto de página.&lt;br /&gt;&lt;br /&gt;EJECT&lt;br /&gt;&lt;br /&gt;(Ver SET DEVICE, se incluye un ejemplo completo)&lt;br /&gt;&lt;br /&gt;3. Conocer la situación del cabezal de impresión.&lt;br /&gt;&lt;br /&gt;PCOL() devuelve la columna en que se halla el cabezal de impresión. Retorna un número entero.&lt;br /&gt;&lt;br /&gt;Un EJECT (salto de página) coloca PCOL() a cero.&lt;br /&gt;&lt;br /&gt;PCOL()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;SET DEVICE TO PRINTER&lt;br /&gt;@ 10, PCOL() + 10 SAY "BANCO : " + BANCO&lt;br /&gt;&lt;br /&gt;PROW() Devuelve la fila en que se haya el cabezal de impresión. Un salto de página, EJECT, coloca PROW() a cero.&lt;br /&gt;&lt;br /&gt;PROW()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;SET DEVICE TO PRINTER&lt;br /&gt;@ PROW() + 1, 5 SAY "NOMBRE...: " + NOMBRE&lt;br /&gt;@ PROW() + 2, 5 SAY "DIRECCION: " + DIRECCION&lt;br /&gt;@ PROW() + 3, 5 SAY "POBLACION: " + POBLACION&lt;br /&gt;&lt;br /&gt;4. Conocer si está preparada la impresora.&lt;br /&gt;&lt;br /&gt;ISPRINTER() Comprueba si la impresora esta lista para imprimir. Devuelve un valor lógico.&lt;br /&gt;&lt;br /&gt;ISPRINTER()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF !ISPRINTER() &amp;amp;&amp;amp; impresora no conectada&lt;br /&gt;@ 24,0 SAY "* CONECTE LA IMPRESORA, PULSE TECLA *"&lt;br /&gt;INKEY(0)&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;5. Conocer si tiene papel.&lt;br /&gt;&lt;br /&gt;DOSERROR() determina el error producido por el DOS. Devuelve un valor numérico correspondiente a un error. Para la lista de errores consulte el manual de Nantucket.&lt;br /&gt;&lt;br /&gt;El error que genera la impresora por falta de papel es el número 28.&lt;br /&gt;&lt;br /&gt;DOSERROR()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF DOSERROR() = 28&lt;br /&gt;? "Falta papel"&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;XIII. Funciones predefinidas.&lt;br /&gt;&lt;br /&gt;1. De bases de datos.&lt;br /&gt;&lt;br /&gt;ALIAS() Devuelve el alias del área de trabajo.&lt;br /&gt;&lt;br /&gt;DELETED() Devuelve el estado de borrado del registro actual.&lt;br /&gt;&lt;br /&gt;EOF() Indica si se alcanza el final de un archivo.&lt;br /&gt;&lt;br /&gt;BOF() Indica si se alcanza el principio de un archivo.&lt;br /&gt;&lt;br /&gt;DBFILTER() Determina la expresión del filtro.&lt;br /&gt;&lt;br /&gt;FIELDNAME() Devuelve el nombre del campo especificado.&lt;br /&gt;&lt;br /&gt;HEADER() Determina la longitud de cabecera.&lt;br /&gt;&lt;br /&gt;RECSIZE() Determina la longitud del registro.&lt;br /&gt;&lt;br /&gt;FCOUNT() Devuelve el número de campos de la base.&lt;br /&gt;&lt;br /&gt;USED() Determina la base de datos en uso.&lt;br /&gt;&lt;br /&gt;FOUND() Devuelve verdadero si se encontró registro.&lt;br /&gt;&lt;br /&gt;RECNO() Devuelve el número de registro actual.&lt;br /&gt;&lt;br /&gt;LASTREC() Devuelve el número total de registros.&lt;br /&gt;&lt;br /&gt;2. Numéricas.&lt;br /&gt;&lt;br /&gt;ABS() Devuelve el valor absoluto de una expresión.&lt;br /&gt;&lt;br /&gt;EXP() Calcula la exponencial.&lt;br /&gt;&lt;br /&gt;INT() Convierte cualquier expresión numérica en entero.&lt;br /&gt;&lt;br /&gt;LOG() Devuelve el logaritmo natural de un número.&lt;br /&gt;&lt;br /&gt;MIN() Devuelve el valor mínimo de dos números o dos fechas.&lt;br /&gt;&lt;br /&gt;MAX() Devuelve el valor máximo de dos números o dos fechas.&lt;br /&gt;&lt;br /&gt;SQRT() Devuelve la raiz cuadrada de un número positivo.&lt;br /&gt;&lt;br /&gt;ROUND() Devuelve el número redondeando con la cantidad de decimales especificados.&lt;br /&gt;&lt;br /&gt;VAL() Convierte una tira de caracteres a un valor numérico.&lt;br /&gt;&lt;br /&gt;3. Cadenas.&lt;br /&gt;&lt;br /&gt;ASC() Devuelve el código ASCII del carácter izquierdo.&lt;br /&gt;&lt;br /&gt;AT() Devuelve un número que indica la posición de comienzo de una cadena de caracteres dentro de otra.&lt;br /&gt;&lt;br /&gt;CHR() Devuelve el carácter del código ASCII especificado.&lt;br /&gt;&lt;br /&gt;EMPTY() Devuelve verdad si la expresión está vacia.&lt;br /&gt;&lt;br /&gt;ISALPHA() Devuelve verdadero si el primer carácter es alfabético.&lt;br /&gt;&lt;br /&gt;ISLOWER() Determina si el carácter más a la izquierda de la cadena está en minúsculas.&lt;br /&gt;&lt;br /&gt;ISUPPER() Determina si el carácter más a la derecha de la cadena está en mayúsculas.&lt;br /&gt;&lt;br /&gt;LEN() Devuelve el número de caracteres que hay en una cadena&lt;br /&gt;&lt;br /&gt;LEFT() Devuelve el número de caracteres especificados desde la izquierda&lt;br /&gt;&lt;br /&gt;RIGHT() Devuelve el número de caracteres especificados desde la derecha.&lt;br /&gt;&lt;br /&gt;LTRIM() Elimina los espacios de la izquierda de una cadena.&lt;br /&gt;&lt;br /&gt;REPLICATE() Repite una expresión de caracteres.&lt;br /&gt;&lt;br /&gt;SPACE() Crea una cadena de espacios.&lt;br /&gt;&lt;br /&gt;STR() Convierte un valor numérico en cadena.&lt;br /&gt;&lt;br /&gt;STRTRAN() Busca y reemplaza dentro de una cadena de caracteres.&lt;br /&gt;&lt;br /&gt;SUBSTR() Extrae una parte específica de una cadena.&lt;br /&gt;&lt;br /&gt;TRANSFORM() Devuelve la tira de caracteres con el formato especificado.&lt;br /&gt;&lt;br /&gt;TRIM() Elimina los espacios de una cadena.&lt;br /&gt;&lt;br /&gt;4. Fechas.&lt;br /&gt;&lt;br /&gt;CDOW() Devuelve el nombre de día de la semana de una fecha.&lt;br /&gt;&lt;br /&gt;CMONTH() Devuelve el nombre del mes de una fecha.&lt;br /&gt;&lt;br /&gt;CTOD() Convierte a fecha una cadena&lt;br /&gt;&lt;br /&gt;DATE() Devuelve la fecha del sistema&lt;br /&gt;&lt;br /&gt;DAY() Devuelve el número de día del mes de una fecha.&lt;br /&gt;&lt;br /&gt;DOW() Devuelve el número que representa el día de la semana de un valor fecha.&lt;br /&gt;&lt;br /&gt;DTOC() Convierte una fecha a cadena&lt;br /&gt;&lt;br /&gt;DTOS() Convierte una fecha a cadena tipo índice.&lt;br /&gt;&lt;br /&gt;MONTH() Devuelve un número que representa el mes.&lt;br /&gt;&lt;br /&gt;YEAR() Devuelve el valor número completo del año dada una fecha.&lt;br /&gt;&lt;br /&gt;5. Hora.&lt;br /&gt;&lt;br /&gt;SECONDS() Devuelve la hora del sistema como segundos y centésimas.&lt;br /&gt;&lt;br /&gt;TIME() Devuelve la hora del sistema.&lt;br /&gt;&lt;br /&gt;SECS() Devuelve hora como segundos y centésimas.&lt;br /&gt;&lt;br /&gt;TSTGRING() Dada una cantidad de segundos nos devuelve dicha cantidad en formato hora.&lt;br /&gt;&lt;br /&gt;6. Otras funciones de interés.&lt;br /&gt;&lt;br /&gt;FILE() Devuelve verdadero si existe el fichero especificado.&lt;br /&gt;&lt;br /&gt;GETE() Recupera el contenido de una variable de entorno DOS.&lt;br /&gt;&lt;br /&gt;TYPE() Devuelve el tipo de dato de la variable, expresión o campo.&lt;br /&gt;&lt;br /&gt;COL() Devuelve la columna actual del cursor.&lt;br /&gt;&lt;br /&gt;ROW() Devuelve la fila actual del cursor.&lt;br /&gt;&lt;br /&gt;CURDIR() Determina el directorio actual.&lt;br /&gt;&lt;br /&gt;DISKSPACE() Determina el número de bytes disponible en una unidad.&lt;br /&gt;&lt;br /&gt;MEMORY() Devuelve el espacio de memoria libre.&lt;br /&gt;&lt;br /&gt;READVAR() Devuelve el nombre de la variable de un GET/MENU.&lt;br /&gt;&lt;br /&gt;XIV. Ordenes SET.&lt;br /&gt;&lt;br /&gt;1. Ordenes SET TO...&lt;br /&gt;&lt;br /&gt;SET ALTERNATE TO Crea un fichero de protocolo.&lt;br /&gt;&lt;br /&gt;SET COLOR TO Fija los colores de pantalla.&lt;br /&gt;&lt;br /&gt;SET DECIMALS TO Fija el número de los decimales a mostrar en los resultados de las funciones numéricas y cálculos.&lt;br /&gt;&lt;br /&gt;SET DEFAULT TO [:] Especifica la unidad y directorio por defecto para la creación de ficheros.&lt;br /&gt;&lt;br /&gt;SET DATE AMERICAN/ANSI/BRITISH/ITALIAN/FRENCH/GERMAN Fija el formato de los campos de fecha.&lt;br /&gt;&lt;br /&gt;AMERICAN mm/dd/aa&lt;br /&gt;ANSI aa.mm.dd&lt;br /&gt;BRITISH dd/mm/aa&lt;br /&gt;ITALIAN dd-mm-aa&lt;br /&gt;FREMCH dd/mm/aa&lt;br /&gt;GERMAN dd.mm.aa.&lt;br /&gt;&lt;br /&gt;SET DELIMITERS TO Especifica los caracteres empleados como delimitadores.&lt;br /&gt;&lt;br /&gt;SET DEVICE TO SCREEN/PRINTER Dirige el resultado de la instrucción @ al dispositivo elegido.&lt;br /&gt;&lt;br /&gt;SET FILTER TO Hace que la base de datos se vea como si sólo contuviese los registros que cumplen la condición.&lt;br /&gt;&lt;br /&gt;SET INDEX TO Abre el índice indicado y cierra los anteriores abiertos con la misma base de datos.&lt;br /&gt;&lt;br /&gt;SET KEY TO Asigna a una tecla un procedimiento.&lt;br /&gt;&lt;br /&gt;SET MARGIN TO Fija el margen izquierdo de la impresora.&lt;br /&gt;&lt;br /&gt;SET MESSAGE TO /CENTER Establece la línea donde se muestran los mensajes asociados a PROMPT.&lt;br /&gt;&lt;br /&gt;SET ORDER TO [] Establece que fichero índice es el principal.&lt;br /&gt;&lt;br /&gt;SET PATH TO [] Especifica la ruta de búsqueda que Clippersigue en el acceso a ficheros.&lt;br /&gt;&lt;br /&gt;SET PRINTER TO [/] Determina la salida de la impresora.&lt;br /&gt;&lt;br /&gt;SET PROCEDURE TO [] Activa fichero de procedimientos.&lt;br /&gt;&lt;br /&gt;2. Ordenes SET ON/OFF.&lt;br /&gt;&lt;br /&gt;SET ALTERNATE on/OFF Determina cuando la salida se envía al fichero.&lt;br /&gt;&lt;br /&gt;SET BELL on/OFF Determina cuándo suena la alarma durante la entradade datos.&lt;br /&gt;&lt;br /&gt;SET CENTURY on/OFF Determina si una fecha debe mostrar los dígitos del siglo o no.&lt;br /&gt;&lt;br /&gt;SET CONFIRM on/OFF Determina si se requiere pulsar return para cada GET.&lt;br /&gt;&lt;br /&gt;SET CONSOLE on/off Determina si la ejecución de los comandos utilizarán la pantalla como salida..&lt;br /&gt;&lt;br /&gt;SET CURSOR on/off Muestra u oculta el cursor en la pantalla.&lt;br /&gt;&lt;br /&gt;SET DELETED on/OFF Oculta/procesa los registros marcados para borrar.&lt;br /&gt;&lt;br /&gt;SET DELIMITERS on/OFF Determina si se muestran los delimitadores&lt;br /&gt;&lt;br /&gt;SET ESCAPE ON/off Activa/Desactiva el desvío producido al pulsar la tecla ESC.&lt;br /&gt;&lt;br /&gt;SET INTENSITY ON/off Muestra los campos de entrada durante los GETs en color o en vídeo inverso.&lt;br /&gt;&lt;br /&gt;SET PRINT on/OFF Determina si la salida de los comandos @...SAY se mandarán a la impresora.&lt;br /&gt;&lt;br /&gt;SET SCOREBOARD ON/off Determinan si los mensajes de clipper aparecen en la línea 0.&lt;br /&gt;&lt;br /&gt;SET SOFTSEEK on/OFF Permite acceder al registro más próximo si el buscado no se encuentra.&lt;br /&gt;&lt;br /&gt;SET UNIQUE on/OFF Determina si sólo los registros con clave no repetida aparecerán en el índice.&lt;br /&gt;&lt;br /&gt;SET WRAP on/OFF Permite el movimiento circular entre opciones de menús.&lt;br /&gt;&lt;br /&gt;XV. Redes locales.&lt;br /&gt;&lt;br /&gt;1. Bloqueo de registro.&lt;br /&gt;&lt;br /&gt;RLOCK() Bloquea/desbloquea el registro actual del área de trabajo en curso. Para utilizar en redes locales.&lt;br /&gt;&lt;br /&gt;RLOCK() / LOCK()&lt;br /&gt;&lt;br /&gt;2. Bloqueo de ficheros.&lt;br /&gt;&lt;br /&gt;FLOCK() Bloquea/desbloquea un archivo abierto de base de datos dependiendo de su estado anterior. Sólo se utiliza en redes locales.&lt;br /&gt;&lt;br /&gt;FLOCK()&lt;br /&gt;&lt;br /&gt;3. Desbloqueo.&lt;br /&gt;&lt;br /&gt;UNLOCK Desactiva el bloqueo de los archivos o registros bloqueados por el #ltimo usuario.&lt;br /&gt;&lt;br /&gt;UNLOCK [ALL]&lt;br /&gt;&lt;br /&gt;ALL - Quita todos los bloqueos en curso de todas las áreas de trabajo.&lt;br /&gt;&lt;br /&gt;4. Uso exclusivo de ficheros.&lt;br /&gt;&lt;br /&gt;SET EXCLUSIVE Permite el uso exclusivo o no de archivos de base de datos, índices y campos memos, en redes locales. Por defecto esta en ON.&lt;br /&gt;&lt;br /&gt;SET EXCLUSIVE ON/off&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5539267871087722416-4564279430818223895?l=wifi4.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/4564279430818223895'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/4564279430818223895'/><link rel='alternate' type='text/html' href='http://wifi4.blogspot.com/2009/11/programacion-en-clipper_29.html' title='PROGRAMACION EN CLIPPER'/><author><name>Wifi4.0</name><uri>http://www.blogger.com/profile/16699432101663038724</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5539267871087722416.post-6863605924168843492</id><published>2009-11-29T02:23:00.000-08:00</published><updated>2009-11-29T02:25:17.093-08:00</updated><title type='text'>Breve introduccion a c++</title><content type='html'>&lt;span style=";font-family:times new roman;font-size:130%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_IlKwMMnZNV8/SwGuLWlWt-I/AAAAAAAAAAc/tyTmnvzk6ko/s1600/Origen%2520lenguajes%25202.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5404792537885358050" style="margin: 0px auto 10px; display: block; width: 306px; height: 156px; text-align: center;" alt="" src="http://3.bp.blogspot.com/_IlKwMMnZNV8/SwGuLWlWt-I/AAAAAAAAAAc/tyTmnvzk6ko/s320/Origen%2520lenguajes%25202.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style=";font-family:times new roman;font-size:130%;"  &gt;• Paradigma:multiparadigma: orientado a objetos, imperativo, programación genérica.&lt;br /&gt;• Apareció en:1983&lt;br /&gt;• Diseñado por:Bjarne Stroustrup&lt;br /&gt;• Tipo de dato:fuerte, estático&lt;br /&gt;• Implementaciones:GNU Compiler Collection, Microsoft Visual C++, Borland C++ Builder, Dev-C++, C-Free.&lt;br /&gt;• Dialectos:ISO C++, ANSI C++ 1998, ANSI C++ 2003&lt;br /&gt;• Influido por:C, Simula&lt;br /&gt;• Ha influido a:Ada, C#, Java, PHP, D, Perl&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Hoy en día, los ordenadores con capaces de llevar a cabo tareas muy diferentes; desde operaciones matemáticas simples hasta sofisticadas representaciones graficas. Estas tareas no las hace el ordenador por si mismo, si no que son efectuadas siguiendo una serie de instrucciones predefinidas que componen lo que nosotros llamamos un programa.Para que el ordenador interprete correctamente el programa, este debe estar escrito en lenguaje maquina (secuencias de 0 y 1). Obviamente, seria muy complicado para nosotros programar de esta manera, por lo que nacen los llamados lenguajes de alto nivel. Estos son mucho más sencillos y parecidos al nuestro. Entre ellos esta C++.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Breve historia de C++&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;C++ esta basado en el lenguaje de programación C, el cual esta a su vez basado en dos lenguajes muy primitivos (BCPL y B).Sus fundadores fueron Martin Richards (1976) y Ken Thompson (1970) respectivamente. Dos ańos mas tarde de la creación de B Dennis Ritchie implementó el diseńo de BCPL y B y creó C, que se dio a conocer por ser el lenguaje de programación de desarrollo de UNIX.&lt;br /&gt;A principios de los ańos 80, Bjarne Stroustrup (de los laboratorios Bell) empezó a desarrollar C++, que recibiría formalmente su nombre a finales de 1983. En octubre de 1985, apareció la primera divulgación comercial del lenguaje y la primera edición del libro "The C++ Programming Language", escrito por el propio creador de C++.&lt;br /&gt;Desde ese momento, C y C++ se han usado para la creación de sistemas operativos, por lo que su popularidad ha ido creciendo a lo largo de los ańos, y ahora mismo es uno de los lenguajes de programación mas usados por los programadores.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ventajas:&lt;br /&gt;• Lenguaje de programación orientado a objetos.&lt;br /&gt;• Lenguaje muy didáctico, gracias a este lenguaje puedes aprender muchos otros lenguajes con gran facilidad, como C#, Java, Visual Basic, Javascript, PHP, entre otros.&lt;br /&gt;• Es muy potente en lo que se refiere a creación de sistemas complejos, un lenguaje muy robusto.&lt;br /&gt;• Permite elaborar aplicaciones sencillas como un "Hello World!" hasta sistemas operativos y mucho más, todo eso dependiendo del manejo del lenguaje.&lt;br /&gt;Actualmente, puede compilar y ejecutar código de C, ya viene con librerías para realizar esta labor.&lt;br /&gt;• Es un lenguaje muy empleado, existen muchos tutoriales en línea, libros, códigos fuentes abiertos... hay material de sobra y basta para aprender lo necesario y mucho más con este lenguaje.&lt;br /&gt;• Existen muchos algoritmos cuyo pseudocódigo se encuentra ya desarrollado en C++, de manera que puedes tomarlo y amoldarlo a tu solución (porque el que veas un fragmento de código no asegura que sea correcto al 100%).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Desventajas:&lt;br /&gt;• Uso de DLLs (librerías dinámicas) muy complejo. Java y .Net han evolucionado estos conceptos manipulando las DLLs mediante los frameworks que proveen. En cambio, en C++ el desarrollador debe encargarse de cargar y liberar de memoria estas librerías, y correr los riesgos por el manejo de esta memoria.&lt;br /&gt;• Elaborar un sistema en C++ es como construir un rascacielos: tiene buen soporte y es robusto, pero si existen errores en los pisos inferiores toda la parte superior se viene abajo terriblemente.&lt;br /&gt;• Manejo de punteros y memoria respecto a ello. Claro, esta también es una gran ventaja porque permite un mejor control de la memoria y una buena administración de recursos de computadora, pero la inexperiencia de los desarrolladores o la pérdida de costumbre con este tipo de variables (sobre todo cuando son dobles o triples punteros, inclusive de mayor orden) los lleva al desastre.&lt;br /&gt;• No es recomendable para desarrollo de páginas Web.&lt;br /&gt;• Existen muchos entornos de programación para C++. No existen estándares para ello. De manera que puedes encontrar C++ para Unix/Linux, C++ para Windows, C++ para MacOS, y asi indistintamente. Además, en cada SO encuentras diferentes IDEs de desarrollo, y también encuentras IDEs para desarrollo de aplicaciones gráficas como Anjuta, Qt para Unix/Linux, Borland C++ Builder y Visual Studio C++ para Windows...&lt;br /&gt;Características de C++&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;C++ tiene varias características que otros lenguajes de programación no tienen. Las más destacadas son:&lt;br /&gt;&lt;br /&gt;Programación orientada a objetos:&lt;br /&gt;La posibilidad de orientar la programación a objetos permite al programador diseńar aplicaciones desde un punto de vista más cercano a la vida real. Además, permite la reutilización del código de una manera más lógica y productiva.&lt;br /&gt;Portabilidad:&lt;br /&gt;Un código escrito en C++ puede ser compilado en casi todo tipo de ordenadores y sistemas operativos sin hacer apenas cambios.&lt;br /&gt;Brevedad:&lt;br /&gt;El código escrito en C++ es muy corto en comparación con otros lenguajes, sobretodo porque en este lenguaje es preferible el uso de caracteres especiales que las "palabras clave".&lt;br /&gt;programación modular:&lt;br /&gt;Un cuerpo de aplicación en C++ puede estar hecho con varios ficheros de código fuente que son compilados por separado y después unidos. Además, esta característica permite unir código en C++ con código producido en otros lenguajes de programación como Ensamblador o el propio C&lt;br /&gt;Velocidad:&lt;br /&gt;El código resultante de una compilación en C++ es muy eficiente, gracias a su capacidad de actuar como lenguaje de alto y bajo nivel y a la reducida medida del lenguaje.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Aplicaciones (conocidas) estan hechas en C++&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;• Windows (sistema operativo)&lt;br /&gt;• Linux (sistema operativo)&lt;br /&gt;• Mac (sistema operativo)&lt;br /&gt;• Photoshop (programa de diceño grafico)&lt;br /&gt;• Winamp (reproductor de musica)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Nota: la lista no pretende ser completa, solo se presentan algunas de las aplicaciones más conocidas, relevantes y/o utilizadas por gran número de personas.&lt;br /&gt;Conclusión&lt;br /&gt;C es un lenguaje de programación de nivel medio ya que combina los elementos del lenguaje de alto nivel con la funcionalidad del ensamblador.Su característica principal es ser portable,es decir,es posible adaptar los programas escritos para un tipo de computadora en otra.Otra de sus características principales es el ser esctructurado, es decir, el programa se divide en módulos (funciones) independientes entre sí.&lt;br /&gt;El lenguaje C inicialmente fué creado para la programación de:&lt;br /&gt;-Sistemas operativos&lt;br /&gt;-Intérpretes&lt;br /&gt;-Editores&lt;br /&gt;-Ensambladores&lt;br /&gt;-Compiladores&lt;br /&gt;-Administradores de bases de datos.&lt;br /&gt;Actualmente, debido a sus caraterísticas, puede ser utilizado paratodo tipo de programas.&lt;br /&gt;Una de las mejores maneras de aprender es tomando una clase de programación. Vea si usted puede tomar una clase en su escuela, o tomar una clase en la escuela nocturna. Programación en C es una habilidad que puede terminar ahorrándole su trabajo o ayudarle a conseguir un mejor trabajo&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5539267871087722416-6863605924168843492?l=wifi4.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/6863605924168843492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/6863605924168843492'/><link rel='alternate' type='text/html' href='http://wifi4.blogspot.com/2009/11/breve-introduccion-c_29.html' title='Breve introduccion a c++'/><author><name>Wifi4.0</name><uri>http://www.blogger.com/profile/16699432101663038724</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_IlKwMMnZNV8/SwGuLWlWt-I/AAAAAAAAAAc/tyTmnvzk6ko/s72-c/Origen%2520lenguajes%25202.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-5539267871087722416.post-316233384819763246</id><published>2009-11-29T02:20:00.000-08:00</published><updated>2009-11-29T02:21:24.475-08:00</updated><title type='text'>PROGRAMACION EN C++</title><content type='html'>&lt;span style="font-family: times new roman;font-size:130%;" &gt;Introducción.&lt;br /&gt;&lt;br /&gt;A principio de los años ochenta, DBASE II hizo su aparición de la mano de George Tate (1943-1984) y su empresa Ashton-Tate. Esta nueva herramienta se presentaba en el emergente mundo de los microordenadores con la intención de facilitar la gestión de las bases de datos.&lt;br /&gt;&lt;br /&gt;Evidentemente, los sistemas de gestión de bases de datos existían desde mucho antes, sobre todo, desarrollados para grandes sistemas, pero la cuestión estaba en cubrir una carencia que más tarde o más temprano debía ser atendida por los ingenieros de software y que era esperada ansiosamente por el creciente número de usuarios de los ordenadores personales.&lt;br /&gt;&lt;br /&gt;El sistema de gestión de bases de datos había que diseñarse no exclusivamente como un entorno de programación, semejante a otros entornos o lenguajes con capacidad de tratamiento de grandes masas de datos. Este debía posibilitar la ejecución interactiva de instrucciones, ser amigable, accesible por usuarios no programadores, y debía estar formado por unas instrucciones potentes y fáciles de memorizar.&lt;br /&gt;&lt;br /&gt;(LA PRIMERA DE LAS VERSIONES DE DBASE II SE UTILIZÓ CON EL SISTEMA OPERATIVO CP/M, SIGUIÉNDOLE OTRAS COMO LA 2.4 DE SEPTIEMBRE DE 1983 BAJO DOS 1.1 Y 2.0).&lt;br /&gt;&lt;br /&gt;También, a principio de los ochenta se comienza a utilizar entre los usuarios de micros una nueva terminología informática de bases de datos, ésta era más familiar en otros ambientes informáticos y definía con precisión los conceptos más básicos:&lt;br /&gt;&lt;br /&gt;   * Una base de datos puede definirse como la agrupación útil y organizada de información.&lt;br /&gt;   * Bases de datos relacionales. Este tipo de estructura define relaciones entre los datos en una base de datos. Un modelo simple organiza la base de datos de igual forma que podemos definir una tabla de dos dimensiones (filas y columnas). Los datos de una fila (registro) se subdividen en columnas (campos). A cada fila se la asigna un número (nº de registro) que representa el orden en que será almacenado el registro en la base de datos. A las distintas columnas se le asignará un nombre de campo. Con esta estructura básica de base de datos era fácil manipular y actualizar gran cantidad de información.&lt;br /&gt;&lt;br /&gt;Es fácil distinguir los componentes básicos de una base de datos:&lt;br /&gt;&lt;br /&gt;Su estructura es descrita por un conjunto de nombres de campos, estos campos pueden ser de varios tipos en función del dato a almacenar (números, fechas, etc) y de longitud definible.&lt;br /&gt;&lt;br /&gt;Otro componente son los datos propiamente dichos.&lt;br /&gt;&lt;br /&gt;   * Los gestores de bases de datos permiten la organizacíón y el tratamiento eficaz de grandes masas de datos proporcionándonos gran variedad de herramientas.&lt;br /&gt;&lt;br /&gt;DBASE II proporciona un gestor de base de datos de tipo relacional con capacidad para gestionar las bases de datos, interpretar interactivamente instrucciones y ejecutar bloques de sentencias (programas).&lt;br /&gt;&lt;br /&gt;DBASE II también contribuyó a la filosofía de la programación estructurada, mejoró sus prestaciones y evolucionó en varias versiones (DBASE III, DBASE III+ y DBASE IV).&lt;br /&gt;&lt;br /&gt;George Tate fallecido tempranamente nunca pudo comprobar la revolución que ocasionaría este producto, aún en constante evolución.&lt;br /&gt;&lt;br /&gt;El éxito obtenido entre los usuarios de micros, principalmente atraídos por su versatilidad y potencia, y los grandes beneficios producidos en su comercialización, hizo que muchas empresas de software se adherieran a la idea de desarrollar nuevos productos análogos, una gama de dialectos que hoy se les agrupa con el sobrenombre de entorno xBase (Clipper, Quicksilver, Foxbase, etc).&lt;br /&gt;&lt;br /&gt;La difusión de estos productos han desbancado a muchos lenguajes de programación, como al Cobol que aunque propicia una fácil lectura de sus fuentes, la programación resulta lenta y laboriosa.&lt;br /&gt;&lt;br /&gt;En los ochenta, en pleno boom informático DBASE sustituye a muchos lenguajes por la potencia de sus órdenes y facilidad de uso. Por entonces, hubo que estar muy despierto a la hora de seleccionar una herramienta de trabajo con futuro.&lt;br /&gt;&lt;br /&gt;CLIPPER es un dialecto creado como otros tantos con la intención de mejorar las prestaciones de DBASE. Su primera versión se creó en 1985 en los laboratorios de Natuncket. CLIPPER está escrito en lenguaje C y Ensamblador y se presentó como un lenguaje atrevido que ha dado muchos quebraderos de cabeza en Ashthon-Tate. En el primer contacto que se tiene con él es dificil encontrar muchas diferencias con respecto a DBASE, ya que CLIPPER es un lenguaje formado por un conjunto de comandos y funciones similares a las usadas con DBASE, incluso la mayoría con igual formato sintáctico.&lt;br /&gt;&lt;br /&gt;Pero no tardaremos demasiado tiempo en percartarnos de las diferencias. La principal de ellas, está en que todos los programas escritos en Clipper pueden compilarse y enlazarse. El resultado obtenido es un fichero ejecutable que puede utilizarse de forma independiente al gestor de base de datos y sin necesidad de incluir módulo runtime. Esto repercute en la velocidad de ejecución de los programas.&lt;br /&gt;&lt;br /&gt;Muchos programadores recordarán que cuando entregaban un proyecto a un cliente desarrollado en DBASE II o III se veían con la fatalidad de entregar los ficheros fuentes, ya que DBASE lo que hacía era interpretarlos. CLIPPER salvaguardó estos intereses. CLIPPER aportó más comandos y funciones y prescindió de muchos de DBASE.&lt;br /&gt;&lt;br /&gt;CLIPPER es ahora sin duda el compilador más utilizado en aplicaciones de gestión de datos para microordenadores. La última versión aparecida en el mercado es la CLIPPER 5.01 versión reparada de la CLIPPER 5.0. Hasta el momento, la versión más utilizada quizás por su largo tiempo de vigencia es la CLIPPER SUMMER '87. Anteriores a ésta eran la CLIPPER AUTUMN '86 y la versión de 1985.&lt;br /&gt;&lt;br /&gt;De todas la versiones detalladas la SUMMER '87 ha sido la más difundida. Muchas aplicaciones se han desarrollado con esta versión, por ello, aún, muchos programadores se resisten al cambio a versiones más actuales.&lt;br /&gt;&lt;br /&gt;Otras prestaciones de CLIPPER SUMMER '87 a destacar son las siguientes:&lt;br /&gt;&lt;br /&gt;   * Provee un conjunto de funciones para el tratamiento de ficheros en redes de area local.&lt;br /&gt;   * Permite manejar ficheros de bajo nivel.&lt;br /&gt;   * Posibilita la creación de funciones de usuarios y agruparlas en librerías.&lt;br /&gt;   * Permite el uso de arrays unidimensionales.&lt;br /&gt;   * Proporciona un depurador avanzado.&lt;br /&gt;&lt;br /&gt;La presente guía está dividida en doce capítulos. Cada capítulo describe comandos y/o funciones de Clipper referentes a temas concretos. El primero de ellos describe aspectos técnicos iniciales que es preciso conocer de este producto.&lt;br /&gt;&lt;br /&gt;I. Características técnicas.&lt;br /&gt;&lt;br /&gt;1. Capacidades.&lt;br /&gt;&lt;br /&gt;Nº. máximo de registros por base de datos, 1000.000.000&lt;br /&gt;Nº. máximo de caracteres por registro, RAM disponible&lt;br /&gt;Nº. máximo de campos por registro, RAM disponible&lt;br /&gt;Nº. máximo de caracteres por campo, 32 kb&lt;br /&gt;Nº. de dígitos de precisión en operaciones de cálculo, 18&lt;br /&gt;Nº. máximo de caracteres en una clave de indexación, 250&lt;br /&gt;Nº. máximo de variables de memoria, 2048&lt;br /&gt;Tamaño máximo de una variable de memoria, 64 kb&lt;br /&gt;Nº. máximo de dígitos en una variable numérica, 19&lt;br /&gt;Nº. máximo de tablas, 2048&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2. Requerimiento hardware.&lt;br /&gt;&lt;br /&gt;Ordenador : IBM PC, XT, AT, 386 o compatible&lt;br /&gt;Memoria RAM : 256 kb&lt;br /&gt;Disco duro : Necesario para funcionamiento óptimo&lt;br /&gt;Coprocesador: Si existe se aprovecha automáticamente&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3. Requerimiento software.&lt;br /&gt;&lt;br /&gt;Sistema Operativo :&lt;br /&gt;DOS 2.0 o superior (monousuario)&lt;br /&gt;DOS 3.1 o superior (multiusuario)&lt;br /&gt;LAN : Bajo DOS. No requiere LAN Pack. Bloqueo manual.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;4. Instalación .&lt;br /&gt;&lt;br /&gt;La instalación de CLIPPER es muy fácil, basta con copiar el contenido de todos los disquetes a un directorio o ejecutar el fichero CLIPCOPY.BAT que se encuentra en el disco de Sistema.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;5. Config.sys.&lt;br /&gt;&lt;br /&gt;Para el funcionamiento óptimo de CLIPPER conviene incluir las siguientes líneas en el fichero de configuración CONFIG.SYS.&lt;br /&gt;&lt;br /&gt;FILES = 20&lt;br /&gt;BUFFERS = 8&lt;br /&gt;&lt;br /&gt;Si se posee DOS 3.3 o superior es posible trabajar hasta con 255 ficheros abiertos simultáneamente. Para ello se debe indicar, en lugar de FILES = 20:&lt;br /&gt;&lt;br /&gt;FILES = 255&lt;br /&gt;&lt;br /&gt;(Es importante ajustar el número de ficheros para aprovechar al máximo la memoria).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;6. Autoexec.bat.&lt;br /&gt;&lt;br /&gt;En el fichero AUTOEXEC.BAT resulta de gran utilidad incluir una línea de PATH. Esto permitirá ejecutar el compilador desde otros directorios de trabajo.&lt;br /&gt;&lt;br /&gt;PATH C:\CLIPPER&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;7. Ficheros.&lt;br /&gt;&lt;br /&gt;A los distintos ficheros que maneja CLIPPER podremos diferenciarlos por su extensión. Si hemos utilizado anteriormente DBASE, la mayoría nos resultarán familiares.&lt;br /&gt;&lt;br /&gt;Bases de datos (.DBF)&lt;br /&gt;Datos memo (.DBT)&lt;br /&gt;Indices (.NTX) en DBASEIII (.NDX)&lt;br /&gt;Etiquetas (.LBL)&lt;br /&gt;Informes (.FRM)&lt;br /&gt;Texto (.TXT)&lt;br /&gt;Variables de memoria (.MEM)&lt;br /&gt;Fuentes (.PRG)&lt;br /&gt;Objetos (.OBJ)&lt;br /&gt;Compilación (.CLP)&lt;br /&gt;Enlace (.LNK)&lt;br /&gt;Overlays (.OVL)&lt;br /&gt;Ejecutables (.EXE)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;8. Compatibilidad con DBASE.&lt;br /&gt;&lt;br /&gt;La posibilidad de compilar DBASE con el compilador de CLIPPER está limitada por un grupo de comandos y funciones de DBASE. A continuación se muestra una relación de estos comandos y funciones:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;¦ APPEND LIST FILES SET CATALOG ¦&lt;br /&gt;¦ ASSIST LIST HISTORY SET COLOR ON/OFF ¦&lt;br /&gt;¦ BROWSE LIST STRUCTURE SET DEBUG ¦&lt;br /&gt;¦ CHANGE LOAD SET DOHISTORY ¦&lt;br /&gt;¦ CLEAR FIELDS LOGOUT SET ECHO ¦&lt;br /&gt;¦ CREATE LABEL MESSAGE() SET ENCRYPTION ¦&lt;br /&gt;¦ CREATE REPORT MODIFY COMMAND SET FIELDS ¦&lt;br /&gt;¦ CREATE QUERY MODIFY LABEL SET HEADING ¦&lt;br /&gt;¦ CREATE SCREEN MODIFY QUERY SET HELP ¦&lt;br /&gt;¦ CREATE VIEW MODIFY REPORT SET HISTORY ¦&lt;br /&gt;¦ DISPLAY FILES MODIFY SCREEN SET MEMOWIDTH ¦&lt;br /&gt;¦ DISPLAY MEMORY MODIFY STRUCTURE SET MENUS ¦&lt;br /&gt;¦ DISPLAY STATUS MODIFY VIEW SET SAFETY ¦&lt;br /&gt;¦ DISPLAY STRUCTUR ON ERROR SET STATUS ¦&lt;br /&gt;¦ DISPLAY USERS ON ESCAPE SET STEP ¦&lt;br /&gt;¦ EDIT ON KEY SET TALK ¦&lt;br /&gt;¦ ERROR() RESUME SET TITLE ¦&lt;br /&gt;¦ EXPORT TO RETRY SET TYPEHEAD ¦&lt;br /&gt;¦ HELP RETURN TO MASTER SET VIEW ¦&lt;br /&gt;¦ IMPORT TO SET ¦&lt;br /&gt;¦ INSERT SET CARRY ¦&lt;br /&gt;Comandos y funciones no compatibles.&lt;br /&gt;&lt;br /&gt;Otras distinciones a considerar son las referentes a las macros (en Clipper no pueden usarse para sustituir a una palabra del sistema) y los ficheros índices (en Clipper están optimizados).&lt;br /&gt;&lt;br /&gt;Clipper proporciona un manejador de bases de datos (DBU), un emulador del punto de petición de orden de Dbase (DOT), un generador de informes y etiquetas (RL) y un generador de ficheros índices. Todo estas opciones son semejantes a las proporcionadas por Dbase.&lt;br /&gt;&lt;br /&gt;II. Entorno de desarrollo.&lt;br /&gt;&lt;br /&gt;1. Entorno.&lt;br /&gt;&lt;br /&gt;Para desarrollar con CLIPPER tendremos que disponer de las siguientes herramientas básicas:&lt;br /&gt;&lt;br /&gt;- Un editor que genere código ASCII standard.&lt;br /&gt;- El compilador CLIPPER.EXE.&lt;br /&gt;- Las librerías CLIPPER.LIB, EXTEND.LIB, OVERLAY.LIB, etc.&lt;br /&gt;- Un enlazador PLINK86.EXE ,LINK.EXE ,TLINK.EXE.&lt;br /&gt;- Un depurador de programas DEBUG.OBJ.&lt;br /&gt;&lt;br /&gt;2. Escritura de programas.&lt;br /&gt;&lt;br /&gt;Los requisitos básicos a cumplir para la correcta escritura de los fuentes son:&lt;br /&gt;&lt;br /&gt;a) Los ficheros fuentes se nombrarán especificando la extensión .PRG.&lt;br /&gt;&lt;br /&gt;b) La longitud de una línea es de 256 caracteres.&lt;br /&gt;&lt;br /&gt;c) Una línea sólo admitirá una instrucción.&lt;br /&gt;&lt;br /&gt;d) Las instrucciones pueden escribirse desde la primera línea en el editor.&lt;br /&gt;&lt;br /&gt;e) Cuando sea necesario escribir líneas de instrucciones muy largas, podemos hacerlo en líneas independiente escribiendo un punto y coma al final de la línea.&lt;br /&gt;&lt;br /&gt;f) Puede escribirse en minúsculas o mayúsculas, indistintamente.&lt;br /&gt;&lt;br /&gt;g) El asterisco '*' se utilizará para hacer comentarios.&lt;br /&gt;&lt;br /&gt;h) El doble '&amp;amp;' se utilizará para comentar líneas con instrucciones.&lt;br /&gt;&lt;br /&gt;3. Compilación.&lt;br /&gt;&lt;br /&gt;La compilación es una traducción del fichero fuente (.PRG) para obtener un fichero objeto (.OBJ). Consiste en transcribir cada instrucción desde el lenguaje simbólico en que está escrito el código (CLIPPER) a código comprensible por el enlazador del sistema operativo (DOS).&lt;br /&gt;&lt;br /&gt;El fichero del compilador que proporciona CLIPPER se llama CLIPPER.EXE.&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;CLIPPER [- {-}]&lt;br /&gt;&lt;br /&gt;Programa fuente que se compila&lt;br /&gt;- Opciones de compilación&lt;br /&gt;&lt;br /&gt;-l El módulo objeto no almacena el nº de línea del fuente.&lt;br /&gt;-m Hace que las llamadas DO o SET PROCEDURE no se compi- len.&lt;br /&gt;-o Especificar el directorio donde se depositará el fichero objeto.&lt;br /&gt;-p La compilación no comienza hasta que no se pulsa una tecla.&lt;br /&gt;-q Suprime la visión en pantalla de los números de líneas.&lt;br /&gt;-s Hace que no se genere módulo objeto. Verifica sólo sintaxis.&lt;br /&gt;-t Especificar la unidad donde se creará el fichero temporal .$$$&lt;br /&gt;&lt;br /&gt;Es imprescindible que haya al menos un espacio en blanco entre y la primera opción así como entre cada una de ellas. Es obligatorio que la opción se exprese en minúsculas.&lt;br /&gt;&lt;br /&gt;Nuestro programa puede contener asimismo diversas llamadas DO a otros módulos .PRG o a procedimientos del mismo programa. Si no le especificamos lo contrario, CLIPPER compila de forma automática los ficheros llamados por DO.&lt;br /&gt;&lt;br /&gt;4. Enlace.&lt;br /&gt;&lt;br /&gt;El fin de un enlazador es el de asociar los módulos objeto obtenidos mediante el compilador con las librerías donde se contienen las traducciones máquina de cada una de las sentencias,llamadas,etc. que aparecen en el módulo objeto.&lt;br /&gt;&lt;br /&gt;a) Enlazadores&lt;br /&gt;&lt;br /&gt;* PLINK86 (Phoenix Tec. Clipper Summer '87)&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;PLINK86 FI {,} [OUTPUT ] LIB&lt;br /&gt;{,} | [@]&lt;br /&gt;&lt;br /&gt;* LINK (Microsoft)&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;LINK {},,, {}&lt;br /&gt;&lt;br /&gt;* TLINK (Borland)&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;TLINK {},,, {}&lt;br /&gt;&lt;br /&gt;* RTLINK (Pocket Soft. Clipper 5)&lt;br /&gt;&lt;br /&gt;Sintaxis:&lt;br /&gt;&lt;br /&gt;RTLINK [FI [OUTPUT ] [LIB [] []] | [@]&lt;br /&gt;&lt;br /&gt;b) Overlay&lt;br /&gt;&lt;br /&gt;* Ficheros de enlace .LNK&lt;br /&gt;&lt;br /&gt;Todas las claúsulas que deban indicarse al enlazador pueden situarse en un fichero de enlace .LNK. El enlazador usa uno de estos ficheros conforme a la siguiente sintaxis:&lt;br /&gt;&lt;br /&gt;PLINK86 @&lt;br /&gt;&lt;br /&gt;Ejemplo_1: PRUEBA.LNK&lt;br /&gt;&lt;br /&gt;FILE prueba&lt;br /&gt;LIB clipper,extend&lt;br /&gt;; (';'Indica el final del fichero .LNK)&lt;br /&gt;&lt;br /&gt;* Librerias&lt;br /&gt;&lt;br /&gt;CLIPPER.LIB&lt;br /&gt;EXTEND.LIB&lt;br /&gt;OVERLAY.LIB&lt;br /&gt;&lt;br /&gt;* Overlays&lt;br /&gt;&lt;br /&gt;El mayor problema con el que nos podemos encontrar, cuando estamos realizando una aplicación en Clipper, es que ésta no nos quepa físicamente en la memoria de trabajo de nuestro ordenador.&lt;br /&gt;&lt;br /&gt;El único modo que tenemos de solucionar este problema es proceder a lo que denominamos segmentación, programación por capas, solapas u overlays.&lt;br /&gt;&lt;br /&gt;Cuando programamos usando esta técnica, lo que hacemos es dividir la memoria RAM en dos o más áreas de trabajo. En la primera de ellas (área principal) se carga el módulo ejecutable, y en las áreas de solape se cargan y descargan, conforme se van usando, los diferentes módulos overlay que hayamos definido.&lt;br /&gt;&lt;br /&gt;Ejemplo_1: PRUEBA.LNK (2 áreas)&lt;br /&gt;FILE prgprin&lt;br /&gt;LIB clipper,extend&lt;br /&gt;OVERLAY CODE, $CONSTANTS&lt;br /&gt;BEGINAREA&lt;br /&gt;SECTION FILE modulo1&lt;br /&gt;SECTION FILE modulo2&lt;br /&gt;SECTION FILE modulo3&lt;br /&gt;ENDAREA&lt;br /&gt;&lt;br /&gt;Mandatos para compilar y linkar&lt;br /&gt;&lt;br /&gt;CLIPPER prgprin -m&lt;br /&gt;CLIPPER modulo1&lt;br /&gt;CLIPPER modulo2&lt;br /&gt;CLIPPER modulo3&lt;br /&gt;PLINK86 @prueba&lt;br /&gt;&lt;br /&gt;Ejemplo_2: PRUEBA.LNK (3 áreas)&lt;br /&gt;FILE prgprin&lt;br /&gt;LIB clipper,extend&lt;br /&gt;OVERLAY CODE, $CONSTANTS&lt;br /&gt;BEGINAREA&lt;br /&gt;SECTION FILE modulo1&lt;br /&gt;SECTION FILE modulo2&lt;br /&gt;ENDAREA&lt;br /&gt;BEGINAREA&lt;br /&gt;SECTION FILE modulo3&lt;br /&gt;ENDAREA&lt;br /&gt;&lt;br /&gt;Mandatos para compilar y linkar&lt;br /&gt;&lt;br /&gt;CLIPPER prgprin -m&lt;br /&gt;CLIPPER modulo1&lt;br /&gt;CLIPPER modulo2&lt;br /&gt;CLIPPER modulo3&lt;br /&gt;PLINK86 @prueba&lt;br /&gt;&lt;br /&gt;(Para que Clipper produzca un fichero .EXE y tantos ficheros .OVL como módulos para overlays tengamos definidos, sólo hay que cambiar la instrucción:&lt;br /&gt;&lt;br /&gt;SECTION FILE {,}&lt;br /&gt;&lt;br /&gt;por&lt;br /&gt;&lt;br /&gt;SECTION INTO FILE {,}&lt;br /&gt;&lt;br /&gt;(Esto último es útil para trabajar con disquetes)&lt;br /&gt;&lt;br /&gt;* Mandatos del enlazador PLINK86&lt;br /&gt;&lt;br /&gt;#. Sirve para poner un comentario en un fichero de enlace.&lt;br /&gt;&lt;br /&gt;BATCH. Por defecto, cuando PLINK86 no encuentra un fichero .OBJ o .LIB de los especificados, la operación de enlace continúa adelante.&lt;br /&gt;&lt;br /&gt;BEGINAREA. Determina el comienzo de un área.&lt;br /&gt;&lt;br /&gt;ENDAREA. Determina el final de un área.&lt;br /&gt;&lt;br /&gt;DEBUG. Proporciona información adicional para ayudar a la depuración de una aplicación en el caso de overlay.&lt;br /&gt;&lt;br /&gt;FILE. Especificar los módulos objetos separados por coma (,).&lt;br /&gt;&lt;br /&gt;HEIGHT. Nº líneas/página del informe (MAP).&lt;br /&gt;&lt;br /&gt;LIBRARY. Especificar las librerías que serán enlazadas con los .OBJ.&lt;br /&gt;&lt;br /&gt;LOWERCASE. Convierte en minúsculas todos los identificadores y símbolos.&lt;br /&gt;&lt;br /&gt;MAP=. Especificar fichero .MAP.&lt;br /&gt;&lt;br /&gt;NOBELL. Elimina el sonido que aparece con los mensajes del PLINK86.&lt;br /&gt;&lt;br /&gt;OUTPUT. Especificar fichero .EXE.&lt;br /&gt;&lt;br /&gt;SEARCH. Hace una segunda pasada por las librerías si tras terminar el enlace alguno de los símbolos ha quedado sin definir.&lt;br /&gt;&lt;br /&gt;SECTION. Determina que los módulos objeto que se relacionan tras la palabra FILE estarán en el área de overlay abierta, pero no en un fichero independiente en disco.&lt;br /&gt;&lt;br /&gt;SECTION INTO. Igual que el anterior, pero en un fichero en disco.&lt;br /&gt;&lt;br /&gt;UPPERCASE. Convierte a mayúsculas todos los identificadores y símbolos.&lt;br /&gt;&lt;br /&gt;VERBOSE. Nos da información en pantalla de lo que está haciendo PLINK86.&lt;br /&gt;&lt;br /&gt;WIDTH. Determina el ancho en columnas del informe (MAP).&lt;br /&gt;&lt;br /&gt;WORKFILE. Sirve para direccionar el archivo temporal que usa el enlazador.&lt;br /&gt;&lt;br /&gt;III. Bases de datos.&lt;br /&gt;&lt;br /&gt;1. Creación de una base de datos.&lt;br /&gt;&lt;br /&gt;Para crear un fichero de estructura vacia se usará el mandato CREATE. Para definir los distintos campos de la futura base de datos emplearemos APPEND BLANK (para añadir un registro en blanco) y REPLACE (para almacenar el contenido).&lt;br /&gt;&lt;br /&gt;CREATE&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;CREATE clientes&lt;br /&gt;USE clientes&lt;br /&gt;APPEND BLANK&lt;br /&gt;REPLACE FIELD_NAME WITH "CODIGO"&lt;br /&gt;REPLACE FIELD_TYPE WITH "C"&lt;br /&gt;REPLACE FIELD_LEN WITH 5&lt;br /&gt;APPEND BLANK&lt;br /&gt;REPLACE FIELD_NAME WITH "NOMBRE"&lt;br /&gt;REPLACE FIELD_TYPE WITH "C"&lt;br /&gt;REPLACE FIELD_LEN WITH 30&lt;br /&gt;CLOSE&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;Las variables de entorno FIELD_NAME, FIELD_TYPE, FIELD_LEN y FIELD_DECIMALS tomarán el nombre de campo, el tipo de campo, la longitud de campo y las posiciones decimales, respectivamente.&lt;br /&gt;&lt;br /&gt;Una vez creada la estructura pasaremos a generar la base de datos propiamente dicha con CREATE FROM.&lt;br /&gt;&lt;br /&gt;CREATE FROM&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;CREATE CLIENTES FROM CLIENTES&lt;br /&gt;&lt;br /&gt;Tanto en la utilidad DOT porporcionada por Clipper como en el entorno Dbase podemos crear bases de datos sin necesidad de escribir programas.&lt;br /&gt;&lt;br /&gt;2. Tipos y longitud de campos.&lt;br /&gt;&lt;br /&gt;Los distintos tipos de campos que podemos definir en una base de datos son:&lt;br /&gt;&lt;br /&gt;C - Caracter (1-254 caracteres alfanuméricos)&lt;br /&gt;N - Numérico (1-19 dígitos de entero.)&lt;br /&gt;(0-15 dígitos decimal y dos dígitos menor que entero)&lt;br /&gt;D - Fecha (8 dd-mm-aa)&lt;br /&gt;L - Lógico (1 carácter para valores lógicos: T,F,Y,N)&lt;br /&gt;M - Memo (10) Almacena dirección para acceder a fichero .DBT.&lt;br /&gt;&lt;br /&gt;3. Usar una base de datos.&lt;br /&gt;&lt;br /&gt;Para usar una base de datos emplearemos la sentencia USE especificando el fichero de base de datos. Si existe un fichero memo asociado se abrirá, y si se indicó uno o más ficheros .NTX se activarán los índices correspondientes. También proporciona el alias adecuado.&lt;br /&gt;&lt;br /&gt;USE [INDEX {,}] [EXCLUSIVE] [ALIAS]&lt;br /&gt;&lt;br /&gt;El número máximo de ficheros índices asociados es 15. EXCLUSIVE se emplea para redes y posibilita la apertura de ficheros con uso exclusivo a un usuario.&lt;br /&gt;&lt;br /&gt;USE sin más, cierra el fichero del área activa.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;&lt;br /&gt;4. Modificar estructura.&lt;br /&gt;&lt;br /&gt;Para modificar la estructura de una base de datos se recomienda el uso de la sentencia MODIFY STRUCTURE propia de Dbase. Posibilita renombrar, suprimir y añadir campos, así como modificar el tipo y la longitud de los mismos. Con LIST STRUCTURE de Dbase listaremos la estructura de una base de datos.&lt;br /&gt;&lt;br /&gt;MODIFY STRUCTURE&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;MODIFY STRUCTURE&lt;br /&gt;&lt;br /&gt;Hay que tener precaución si existen registros en la base de datos ya que algunas modificaciones pueden vaciarnos el contenido de uno o más campos.&lt;br /&gt;&lt;br /&gt;5. Añadir registros.&lt;br /&gt;&lt;br /&gt;APPEND BLANK añade un registro vacio a nuestro fichero en uso. El puntero de la base de datos se sitúa en el registro añadido. La sentencia REPLACE nos servirá para reemplazar el contenido de los campos.&lt;br /&gt;&lt;br /&gt;REPLACE [] [] WITH {,[] WITH } [FOR ][WHILE ]&lt;br /&gt;&lt;br /&gt;Ejemplo_1: USE CLIENTES&lt;br /&gt;APPEND BLANK&lt;br /&gt;REPLACE CODIGO WITH "00001"&lt;br /&gt;REPLACE NOMBRE WITH "Federico Torres"&lt;br /&gt;&lt;br /&gt;6. Listar registros.&lt;br /&gt;&lt;br /&gt;LIST y DISPLAY sirven para visualizar, imprimir o enviar a un fichero de texto, un registro o conjuto de registros.&lt;br /&gt;&lt;br /&gt;LIST [OFF] [&lt;ámbito&gt;] [] [FOR ] [WHILE ] [TO PRINT/TO FILE ]&lt;br /&gt;&lt;br /&gt;DISPLAY [OFF] [&lt;ámbito&gt;] [] [FOR ] [WHILE ] [TO PRINT/TO FILE ]&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;LIST&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;LIST CODIGO&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DISPLAY FOR CODIGO &gt; "50000" TO PRINT&lt;br /&gt;&lt;br /&gt;7. Puntero de registro.&lt;br /&gt;&lt;br /&gt;Clipper mantiene un puntero que indica el registro activo en cada momento. Tanto en Clipper con en Dbase podemos conocer la posición del puntero con la función RECNO(). En el ejemplo anterior al añadir el registro vacio el puntero se desplaza a la posición que ocupa este registro dentro de la base de datos. Podemos deducir que las sustituciones se efectuarán ahí.&lt;br /&gt;&lt;br /&gt;Existen mandatos que afectan únicamente al registro activo. El puntero se puede desplazar usando la sentencias GO y SKIP en sus distintas modalidades:&lt;br /&gt;&lt;br /&gt;GO (ir al registo indicado)&lt;br /&gt;GO TOP (ir al registro número 1)&lt;br /&gt;GO BOTTOM (ir último registro)&lt;br /&gt;SKIP (ir al siguiente registro)&lt;br /&gt;SKIP -1 (ir al anterior)&lt;br /&gt;etc.&lt;br /&gt;&lt;br /&gt;8. Editar un registro.&lt;br /&gt;&lt;br /&gt;La edición de registros es posible realizarla con varias sentencias. No es posible usar EDIT de Dbase III. En Clipper la edición de un registro puede realizarse con un grupo de GET's, aunque existen otras sentencias más avanzada como DBEDIT, MEMOEDIT, etc.&lt;br /&gt;&lt;br /&gt;@ , [SAY [PICTURE ]] [GET [PICTURE ] [RANGE , ] [VALID ]]&lt;br /&gt;&lt;br /&gt;PICTURE expresa un formato para la entrada/salida de información.&lt;br /&gt;&lt;br /&gt;RANGE sirve para validar datos numéricos entre los dos límites especificados.&lt;br /&gt;&lt;br /&gt;VALID se emplea para expresiones genéricas de validación. será la condición de validación.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;GO 3&lt;br /&gt;@ 1,1 SAY " Modifique codigo: " GET CODIGO&lt;br /&gt;@ 2,1 SAY " Modifique nombre: " GET NOMBRE&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;USE ALUMNOS&lt;br /&gt;GO TOP&lt;br /&gt;@ 1,1 SAY NOMBRE&lt;br /&gt;@ 2,1 SAY " Modifique edad: " GET EDAD RANGE 1,7&lt;br /&gt;@ 3,1 SAY " Modifique sexo: " GET SEXO PICTURE "!"; VALID(SEXO$"VH")&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;READ lee las variables GET's&lt;br /&gt;&lt;br /&gt;9. Marcar un registro.&lt;br /&gt;&lt;br /&gt;Clipper igual que Dbase permite marcar registros para posteriormente, si procede, borrarlos definitivamente. Esto se hará con la sentencia DELETE que marca con un asterisco el registro activo. Puede marcarse más de un registro usando la claúsulas FOR o WHILE.&lt;br /&gt;&lt;br /&gt;DELETE [ámbito] [FOR ] [WHILE ]&lt;br /&gt;&lt;br /&gt;[ámbito] RECORD Marcar el registro especificado.&lt;br /&gt;ALL Marcar todos los registros&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;GO 1&lt;br /&gt;DELETE&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DELETE ALL&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DELETE RECORD 10&lt;br /&gt;&lt;br /&gt;Ejemplo_4:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DELETE FOR NOMBRE = "María"&lt;br /&gt;&lt;br /&gt;10. Borrar registros.&lt;br /&gt;&lt;br /&gt;Una vez marcado un registro es posible borrarlo con PACK&lt;br /&gt;&lt;br /&gt;PACK&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;DELETE RECORD 10&lt;br /&gt;PACK&lt;br /&gt;&lt;br /&gt;11. Desmarcar registros.&lt;br /&gt;&lt;br /&gt;La sentencia RECALL suprime las marcas puestas con DELETE&lt;br /&gt;&lt;br /&gt;RECALL [&lt;ámbito&gt;] [FOR ] [WHILE ]&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;RECALL RECORD 10&lt;br /&gt;&lt;br /&gt;12. Borrar todos los registros.&lt;br /&gt;&lt;br /&gt;ZAP borra todos los registros marcados o no de una base de datos manteniendo su estructura.&lt;br /&gt;&lt;br /&gt;13. Localizar registros.&lt;br /&gt;&lt;br /&gt;LOCATE permite localizar uno o más registros. En el momento que encuentra un registro el puntero de registro se coloca en él, esperando a un CONTINUE para continuar con la búsqueda. La búsqueda es secuencial por lo que si el tamaño de la base de datos es considerable puede resultar lento este proceso.&lt;br /&gt;&lt;br /&gt;LOCATE [&lt;ámbito&gt;] [FOR ] [WHILE ]&lt;br /&gt;CONTINUE&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;LOCATE FOR CODIGO &gt; "10000" .AND. NOMBRE = "JOSE"&lt;br /&gt;&lt;br /&gt;14. Operaciones con bases de datos.&lt;br /&gt;&lt;br /&gt;En una base de datos es posible contar registros, y realizar operaciones de suma y media aritmética. COUNT nos servirá para contar, SUM para sumar el contenido de campos numéricos y AVERAGE para calcular la media aritmética.&lt;br /&gt;&lt;br /&gt;COUNT [&lt;ámbito&gt;] [FOR ] [WHILE ] TO&lt;br /&gt;&lt;br /&gt;COUNT cuenta el número de registros que cumplen una determinada condición especificada. Dicha información ha de depositarse obligatoriamente en una variable numérica de memoria.&lt;br /&gt;&lt;br /&gt;&lt;ámbito&gt; es por defecto ALL&lt;br /&gt;&lt;br /&gt;SUM [&lt;ámbito&gt;] TO&lt;br /&gt;&lt;br /&gt;SUM suma uno o más campos depositando el resultado en una variable.&lt;br /&gt;&lt;br /&gt;AVERAGE [&lt;ámbito&gt;} TO&lt;br /&gt;&lt;br /&gt;AVERAGE calcula la media aritmética de uno o más campos.&lt;br /&gt;&lt;br /&gt;15. Exportar.&lt;br /&gt;&lt;br /&gt;COPY TO Copia toda la base de datos en curso o sólo una parte a un nuevo archivo.&lt;br /&gt;&lt;br /&gt;COPY TO [&lt;ámbito&gt; [FIELDS ]&lt;br /&gt;[FOR ] [WHILE ] [SDF/DELIMITED/DELIMITED WITH ]&lt;br /&gt;&lt;br /&gt;- Es el nombre del nuevo archivo.&lt;br /&gt;&lt;br /&gt;&lt;ámbito&gt; - Determina la porción del archivo a copiar, por defecto es ALL (todo).&lt;br /&gt;&lt;br /&gt;FIELDS - Son los campos a copiar a la nueva base de datos.&lt;br /&gt;&lt;br /&gt;FOR/WHILE - Especifican la condición a cumplir.&lt;br /&gt;&lt;br /&gt;SDF - Especifica que el archivo de salida será con formato ASCII, con campos de longitud fija.&lt;br /&gt;&lt;br /&gt;DELIMITED - Formato para el archivo de salida ASCII, con campos de longitud variable y separados por comas. Si se desea pueden separase con espacios (BLANK), o con cualquier otro delimitador.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE HELP&lt;br /&gt;COPY TO HELP.TXT SDF&lt;br /&gt;&lt;br /&gt;16. Importar.&lt;br /&gt;&lt;br /&gt;APPEND FROM añade datos a la base en uso a partir de otro archivo que puede ser que no sea (.DBF). Podemos seleccionar loa datos a añadir mediante cualificadores.&lt;br /&gt;&lt;br /&gt;APPEND [ [FIELDS ] FROM&lt;br /&gt;[FOR ] [WHILE ] [SDF/DELIMITED&lt;br /&gt;[WITH BLANK/]]&lt;br /&gt;&lt;br /&gt;- Registros a agregar por defecto son todos.&lt;br /&gt;&lt;br /&gt;- Lista de campos a agregar.&lt;br /&gt;&lt;br /&gt;- Nombre del archivo origen. Por defecto, (.DBF),&lt;br /&gt;&lt;br /&gt;FOR/WHILE - Indican las condiciones que han de cumplir los registros para ser agregados.&lt;br /&gt;&lt;br /&gt;SDF - Identifca archivos ASCII.&lt;br /&gt;&lt;br /&gt;DELIMITED - Archivos ASCII con separación de campos con comas.&lt;br /&gt;&lt;br /&gt;DELIMITED WITH BLANK - Campos separados por un espacio&lt;br /&gt;&lt;br /&gt;DELIMITED WITH - Podemos especificarlo.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;APPEND FROM VENTAS FOR PEDIDO &gt; 5000&lt;br /&gt;&lt;br /&gt;IV. Indices.&lt;br /&gt;&lt;br /&gt;1. Crear ficheros índices.&lt;br /&gt;&lt;br /&gt;INDEX indexa un fichero de datos por el campo que le indiquemos. Crea en disco un fichero con la extensión .NTX. Pueden usarse también claves múltiples formada por la suma de varios campos, de partes de campos, expresiones y campos, etc, pero recuerde que el máximo número de caracteres de una clave será de 250. Para sumar campos hemos de tener siempre la precaución de convertirlos previamente a cadena. Los ficheros índices no son compatibles con los de Dbase III. Cuando un índice está abierto con su correspondiente base de datos se actualiza de forma automática. Una base de datos puede tener asociados como máximo 15 ficheros índices. Los registros que se encuentran marcados para ser borrados también forman parte del índice.&lt;br /&gt;&lt;br /&gt;INDEX ON {+} TO&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTE&lt;br /&gt;INDEX ON NOMBRE TO NOMCLI&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;USE CLIENTE&lt;br /&gt;INDEX ON NOMBRE+DTOS(FECHA) TO FECCLI&lt;br /&gt;&lt;br /&gt;2. Activar fichero índice.&lt;br /&gt;&lt;br /&gt;Como vimos anteriormente en el capítulo I, la activación de índices se realiza con USE. Se pueden especificar uno o más ficheros índices. Con SET ORDER TO se establecerá el índice activo. Esta sentencia altera el ordenamiento de la declaración inicial de índices hecha con USE...INDEX. Si indicamos SET ORDER TO 0 se desactivan todos los ficheros índices. No obstante, la importancia de este mandato estriba en que no tenemos necesidad de abrirlos de nuevo para activarlos.&lt;br /&gt;&lt;br /&gt;SER ORDER TO&lt;br /&gt;&lt;br /&gt;es el número de índice activo. Puede valer de 0 a 15.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;NOMBRE = SPACE(20)&lt;br /&gt;FECHA = CTOD(SPACE(8))&lt;br /&gt;USE CLIENTES INDEX NOMCLI,FECCLI,DOMCLI&lt;br /&gt;SET ORDER TO 2&lt;br /&gt;LIST NOMBRE,FECHA TO PRINT&lt;br /&gt;&lt;br /&gt;3. Búsqueda por índice.&lt;br /&gt;&lt;br /&gt;SEEK busca una expresión en una clave índice.&lt;br /&gt;&lt;br /&gt;SEEK&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES INDEX NOMCLI&lt;br /&gt;SEEK "LUIS MARIN"&lt;br /&gt;IF FOUND()&lt;br /&gt;@ 4,4 SAY FECHA&lt;br /&gt;@ 5,4 SAY VENTAS&lt;br /&gt;ELSE&lt;br /&gt;@ 10,1 SAY "No existe CLIENTE"&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;4. Area de trabajo.&lt;br /&gt;&lt;br /&gt;SELECT selecciona las diferentes áreas de trabajo en que vamos a situar nuestros ficheros de datos. El último SELECT que enunciemos es aquel que contendrá el fichero activo.&lt;br /&gt;&lt;br /&gt;SELECT &lt;área&gt;/&lt;br /&gt;&lt;br /&gt;&lt;área&gt; es un número comprendido entre 0 y 254.&lt;br /&gt;&lt;br /&gt;es el nombre de un área de trabajo existente si hay un fichero abierto en ese área. Se puede hacer referencia a las 10 primeras áreas de trabajo con las letras A a J.&lt;br /&gt;&lt;br /&gt;En Clipper se pueden utilizar 255 áreas de trabajo. En cada área de trabajo se pueden abrir un fichero de base de datos y 15 ficheros índices como máximo asociados a él.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;SELECT 1&lt;br /&gt;USE CLIENTES&lt;br /&gt;SELECT 2&lt;br /&gt;USE DIARIOVTAS&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;SELECT 1&lt;br /&gt;USE CLIENTES INDEX NOMCLI ALIAS CLI&lt;br /&gt;SELECT 2&lt;br /&gt;USE DIARIOVTAS INDEX TOTALVTAS ALIAS DIA&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;SELECT CLI&lt;br /&gt;SEEK "LUIS PEREZ"&lt;br /&gt;IF FOUND()&lt;br /&gt;CODCLI = CODIGO&lt;br /&gt;SELECT DIA&lt;br /&gt;SEEK CODCLI&lt;br /&gt;IF FOUND()&lt;br /&gt;@ 10,10 SAY PTASVENTAS&lt;br /&gt;ENDIF&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;5. Cierre de ficheros.&lt;br /&gt;&lt;br /&gt;CLOSE cierra el fichero de base de datos abierto en el área activa así como sus índices asociados.&lt;br /&gt;&lt;br /&gt;CLOSE DATABASES cierra todos los ficheros de todas las áreas de trabajo, así como sus correspondientes índices.&lt;br /&gt;&lt;br /&gt;CLOSE INDEX cierra todos los índices del área de trabajo activa.&lt;br /&gt;&lt;br /&gt;CLOSE ALL cierra todos los ficheros abiertos.&lt;br /&gt;&lt;br /&gt;V. Variables de memoria.&lt;br /&gt;&lt;br /&gt;1. Tipos de variables.&lt;br /&gt;&lt;br /&gt;Variable es un nombre asignado a una posición de memoria que se puede utilizar para almacenar un dato concreto. Los tipos de variables por el tipo de dato que contienen son:&lt;br /&gt;&lt;br /&gt;-numéricas&lt;br /&gt;-alfanuméricas&lt;br /&gt;-lógicas&lt;br /&gt;-fechas&lt;br /&gt;&lt;br /&gt;2. Nombrar una variable de memoria.&lt;br /&gt;&lt;br /&gt;Independientemente del tipo a que pertenezca una variable, debe asignársele un nombre, que puede ser de uno a diez caracteres pueden ser una combinación de letras, dígitos o signo de subrayado. El primer carácter de una variable de memoria debe ser una letra. Los siguientes nombres son nombres de variables de memoria permitidos.&lt;br /&gt;&lt;br /&gt;COMPRAS&lt;br /&gt;Precio&lt;br /&gt;I_V_A&lt;br /&gt;MES_1_A_6&lt;br /&gt;&lt;br /&gt;No debe utilizarse el mismo nombre para una variable y para un campo en la misma aplicación.&lt;br /&gt;&lt;br /&gt;3. Introducción de datos en una variable.&lt;br /&gt;&lt;br /&gt;Las instrucciones STORE y el signo igual (=) pueden emplearse indistintamente para la asignación de datos a variables de memoria.&lt;br /&gt;&lt;br /&gt;STORE TO&lt;br /&gt;=&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;PTAS = 0&lt;br /&gt;FECHA_ALTA = CTOD(SPACE(8))&lt;br /&gt;STORE "enero" TO MES&lt;br /&gt;&lt;br /&gt;4. Visualización de variables.&lt;br /&gt;&lt;br /&gt;Para visualizar el contenido de una variable puede usarse la interrogación (?) con los siguientes formatos:&lt;br /&gt;&lt;br /&gt;?&lt;br /&gt;??&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;? MES&lt;br /&gt;?? "HOLA"&lt;br /&gt;&lt;br /&gt;5. Expresiones.&lt;br /&gt;&lt;br /&gt;Además de servir como depósito temporal, las variables de memoria pueden utilizarse en procesosde operaciones. Una variable de memoria puede ser incluida en una expresión para definir un procedimiento, para describir una condición en una instrucción o para servir como elemento de salida (resultado de una operación).&lt;br /&gt;&lt;br /&gt;Pueden utilizarse diferentes tipos de expresión. Una expresión puede incluir un campo de datos, una variable de memoria, una constante o una combinación de todo ello. Sin embargo, todos los elementos de una expresión deben ser del mismo tipo.&lt;br /&gt;&lt;br /&gt;La expresión más corriente es la expresión aritmética, que puede contener un valor, una variable de memoria, un campo numérico y una combinación de éstos unidos por uno o más operadores aritméticos. Las expresiones son útiles para realizar cálculos matemáticos. Puede utilizarse una expresión para asignar un valor a una variable de memoria o para reemplazar el contenido de un campo numérico con un nuevo valor.&lt;br /&gt;&lt;br /&gt;Cuando se incluye más de un operador aritmético en una expresión, ésta se valora de izquierda a derecha de acuerdo con siguiente sistema de prioridades:&lt;br /&gt;&lt;br /&gt;Prioridad máxima : ** ^&lt;br /&gt;Prioridad secundaria: * /&lt;br /&gt;Baja prioridad : + -&lt;br /&gt;&lt;br /&gt;Se pueden utilizar paréntesis en una expresión para definir la secuencia de evaluación y suprimir el sistema normal de prioridades. El material dentro de los paréntesis siempre es evaluado previamente. Cuando haya paréntesis anidados es una expresión aritmética, la expresión del paréntesis interno es evaluado en primer lugar, luego se evalúa el paréntesis externo. Dentro de un paréntesis, los operadores se evalúan según el sistema de prioridades, de izquierda a derecha.&lt;br /&gt;&lt;br /&gt;6. Declaración pública y privada.&lt;br /&gt;&lt;br /&gt;PUBLIC declara variables de memoria como globales o públicas. Estas pueden modificar su valor en cualquier parte del programa.&lt;br /&gt;&lt;br /&gt;PUBLIC&lt;br /&gt;&lt;br /&gt;PRIVATE declara de uso privado la variables de memoria especificadas. Estás pueden modificar su valor en partes de un programa.&lt;br /&gt;&lt;br /&gt;PRIVATE&lt;br /&gt;&lt;br /&gt;7. Salvar y restaurar variables de memoria.&lt;br /&gt;&lt;br /&gt;SAVE TO salva en un fichero variables de memoria.&lt;br /&gt;&lt;br /&gt;SAVE TO [ALL [LIKE /EXCEPT ]]&lt;br /&gt;&lt;br /&gt;es el nombre del fichero donde se almacenarán las variables. Si no se especifica la extensión por defecto es .MEM.&lt;br /&gt;&lt;br /&gt;ALL salva en el fichero todas las variables existentes.&lt;br /&gt;&lt;br /&gt;LIKE salva en el fichero todas las variables cuya estructura sea semejante a la especificada en . Recuerde que puede hacer uso de los símbolos comodines: * y ?.&lt;br /&gt;&lt;br /&gt;EXCEPT salva todas las variables que no tengan una estructura semejante a .&lt;br /&gt;&lt;br /&gt;RESTORE FROM restaura desde disco el fichero de variables de memoria . Si se usa ADDITIVE no se borra el entorno de variables activo al restaurar.&lt;br /&gt;&lt;br /&gt;RESTORE FROM [ADDITIVE]&lt;br /&gt;&lt;br /&gt;Al restaurar las variables de memoria, éstas son privadas, a no ser que se especifiquen como públicas antes de restaurarlas y se utilice la claúsula ADDITIVE.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;conf_cla = "1234"&lt;br /&gt;conf_dis = "A"&lt;br /&gt;conf_dir = "C:\GESTION\"&lt;br /&gt;conf_mar = 20&lt;br /&gt;conf_col = "S"&lt;br /&gt;SAVE TO CONFIG ALL LIKE conf_*&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;RESTORE FROM CONFIG ADDITIVE&lt;br /&gt;clave = SPACE(4)&lt;br /&gt;@ 1,1 SAY "Teclear Clave: " GET clave&lt;br /&gt;READ&lt;br /&gt;IF clave = conf_cla&lt;br /&gt;...&lt;br /&gt;...&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;8. Eliminar variables de memoria.&lt;br /&gt;&lt;br /&gt;RELEASE elemina de la vemoria las variables especificadas.&lt;br /&gt;&lt;br /&gt;RELEASE [ {,}] [ALL [LIKE] EXCEPT ]]&lt;br /&gt;&lt;br /&gt;.. es la lista de variables que se desean eliminar.&lt;br /&gt;&lt;br /&gt;ALL indica que sean eliminadas todas las variables existentes.&lt;br /&gt;&lt;br /&gt;ALL LIKE indica que sean eliminadas todas las variables cuya estructura sea semejante a la expresada en . Se pueden usar los comodines: * y ?.&lt;br /&gt;&lt;br /&gt;ALL EXCEPT indica que sean borradas todas las variables que no concuerden con la estructura expresada en .&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;conf_cla = "1234"&lt;br /&gt;conf_dis = "A"&lt;br /&gt;conf_dir = "C:\GESTION\"&lt;br /&gt;conf_mar = 20&lt;br /&gt;conf_col = "S"&lt;br /&gt;RELEASE ALL&lt;br /&gt;&lt;br /&gt;9. Macros.&lt;br /&gt;&lt;br /&gt;Las macros sirven en CLIPPER para forzar la sustitución de una variable por su valor en aquellos puntos de programa donde por si misma la variable no se traduciría. Cuando tras una macro se sigue algún tipo de expresión hemos de indicar al sistema que la macro termina con un punto (.).&lt;br /&gt;&lt;br /&gt;&amp;amp;&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;nombre = "lápiz"&lt;br /&gt;? "Artículo: &amp;amp;nombre"&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;base = "CLIENTES"&lt;br /&gt;USE &amp;amp;base&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;filtro = "EDAD &gt; 18 .AND. SEXO = 'V'"&lt;br /&gt;USE CLIENTES&lt;br /&gt;LIST NOMBRE FOR &amp;amp;filtro&lt;br /&gt;&lt;br /&gt;10. Operadores y valores lógicos.&lt;br /&gt;&lt;br /&gt;a) Operadores lógicos.&lt;br /&gt;&lt;br /&gt;.AND. (Y además)&lt;br /&gt;.OR. (O además)&lt;br /&gt;.NOT. (Negación)&lt;br /&gt;! (Negación)&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF EDAD &gt; 18 .AND. EDAD &lt; pagar =" 10000"&gt;&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;IF !FILE("CLIENTES.DBF")&lt;br /&gt;@ 1,1 SAY " Error no encuentra base de datos "&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;b) Valores lógicos. Representan pares de valores.&lt;br /&gt;&lt;br /&gt;.T. (Verdadero)&lt;br /&gt;.F. (Valso)&lt;br /&gt;&lt;br /&gt;.Y. (Si)&lt;br /&gt;.N. (No)&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;JUBILADO = .Y.&lt;br /&gt;IF JUBILADO&lt;br /&gt;..&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;11. Operadores relacionales.&lt;br /&gt;&lt;br /&gt;= (Igual que)&lt;br /&gt;== (Exactamente igual que)&lt;br /&gt;&gt; (Mayor que)&lt;br /&gt;&lt; (Menor que) &gt;= (Mayor igual que)&lt;br /&gt;&lt;= (Menor igual que) &lt;&gt; # (Distinto)&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DO CASE&lt;br /&gt;CASE MES = 1&lt;br /&gt;....&lt;br /&gt;....&lt;br /&gt;CASE MES &gt;= 2&lt;br /&gt;....&lt;br /&gt;....&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;VI. Operaciones de entrada y salida.&lt;br /&gt;&lt;br /&gt;1. Entrada, máscara, validación y rango.&lt;br /&gt;&lt;br /&gt;@...SAY/GET muestra en las coordenadas reseñadas el contenido de la expresión que sigue a SAY, carga valores a los campos o las variables de memoria que siguen a GET (hasta ser leídos por READ.)&lt;br /&gt;&lt;br /&gt;Las variables usadas han de ser declaradas previamente.&lt;br /&gt;&lt;br /&gt;@ . [SAY [PICTURE ]]&lt;br /&gt;[GET [PICTURE ]&lt;br /&gt;[RANGE ,]&lt;br /&gt;[VALID ]]&lt;br /&gt;&lt;br /&gt;PICTURE expresa un formato para la entrada/salida de información. Este formato puede estar controlado por plantillas o funciones. Las primeras se aplican carácter a carácter y las segundas afectan a toda la claúsula. Las funciones irán precedidas del símbolo @.&lt;br /&gt;&lt;br /&gt;RANGE sirve para validar datos numéricos, indicando un límite inferior y un superior. Entre estos límites deberá estar comprendido en dato numérico para que sea válido.&lt;br /&gt;&lt;br /&gt;VALID se emplea para expresiones genéricas de validación. será la condición de validación.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;nombre = SPACE(20)&lt;br /&gt;@ 2,1 SAY "Teclear nombre: " GET nombre PICTURE "@!"&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;edad = 0&lt;br /&gt;@ 3,3 SAY "Teclear edad: " GET edad PICTURE "999" RANGE 19,125&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;resp = SPACE(1)&lt;br /&gt;@ 5,5 say "¿ GRABAR ? " GET resp "!" VALID(resp$"SN")&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Ejemplo_4:&lt;br /&gt;&lt;br /&gt;importe = 0&lt;br /&gt;@ 5,5 SAY " TECLEAR IMPORTE: " GET importe PICTURE "@E9,999.99"&lt;br /&gt;READ&lt;br /&gt;&lt;br /&gt;Símbolos usados por PICTURE&lt;br /&gt;&lt;br /&gt;A Hace que un GET sólo admita letras&lt;br /&gt;L Idem sólo para valores lógicos.&lt;br /&gt;Y Permite sólo "Y" o "N"&lt;br /&gt;N Idem sólo letras y caracteres&lt;br /&gt;X Idem cualquier carácter&lt;br /&gt;9 Permite que sólo se visualicen dígitos&lt;br /&gt;# Idem sólo letras, espacios y signos&lt;br /&gt;! Idem sólo letras mayúsculas&lt;br /&gt;, Representa los miles en los datos numéricos&lt;br /&gt;$ Hace que se muestren $ para rellenar una cifra por la izquierda.&lt;br /&gt;* Idem con *&lt;br /&gt;&lt;br /&gt;Símbolos utilizados como funciones &lt;"@")  C Indica CR después de un número positivo X Indica DB después de un número negativo ( Encierra con paréntesis números negativos con espacios a la izquierda. ) Idem sin espacios a la izquierda B Justifica los números por la izquierda A Hacen que sólo se puedan captar caracteres alfabéticos ! Hace que sólo se permitan letras mayúsculas R Permite insertar caracteres que aparecerán solamente en pantalla, no almacenándose en la variable E Convierte los números al formato europeo D Visualiza las fechas en el formato especificado con SET DATE K Borra el contenido de la variable si no se pulsa primero un carácter de control del cursor S Hace scroll horizontal con la variable Z Hace que los valores cero en un campo numérico se representen como blancos.  ACCEPT acepta datos alfanuméricos por pantalla y los carga en . No es necesario haber declarado previamente .  ACCEPT [] TO  Ejemplo_1:  ACCEPT "Escribe tu nombre" TO nombre  INPUT Acepta datos por pantalla. Los datos han de ser identificados con sus correspondientes indicadores, así, por ejemplo, una cadena de carácteres deberá escribirse entrecomillada, mientras que esto no será preciso con un número.  INPUT [] TO  Ejemplo_1:  INPUT " Edad " TO edad  WAIT detiene la ejecución del programa y espera la pulsación de una tecla.  WAIT [] [TO ]  es una cadena de caracteres que se visualizarán a modo de información. Si se omite, aparecerá en pantalla: Press any key to continue...  es una variable que contendrá el carácter qu se ha pulsado  2. Pausa.  INKEY() detiene por un tiempo el flujo del programa y devuelve el valor de la tecla que se está pulsando.  INKEY([])  indica el número de segundos de espera. Si es igual a cero detiene el programa y espera que pulsemos una tecla cuyo valor ASCII toma.  Ejemplo_1:  tecla = INKEY(0)  Pulsando [enter],  tecla = 13  3. Conocer la última tecla pulsada.  LASTKEY() devuelve el valor de la última tecla pulsada. Dicho valor es un número que se corresponde con el valor ASCII del carácter.  Ejemplo_1:  INKEY(0) DO CASE CASE LASTKEY() = 27 RETURN CASE CHR(LASTKEY()) = "+" ..... CASE LASTKEY() = 13 ..... ENDCASE  4. Salida.  ?, ??, @ SAY, TEXT/ENDTEXT se emplean generalmente como instrucciones de salida (pantalla/impresora) para expresiones, cadenas, bloques de texto, etc.  Ejemplo_1:  TEXT ********************* ERROR ********************* ENDTEXT  Ejemplo_2:  a=4 b=5 c=3 ? (a*b)**c  5. Borrar pantalla.  CLEAR borra la pantalla, manteniendo los atributos de color vigente, y libera todos los GET pendientes. Asimismo, posiciona el cursor en la posición 0,0 (posiciones verticales 0 a 24 / posiciones horizontales 0 a 79).  @..CLEAR TO borra un área de pantalla.  Ejemplo_1:  @ 3,3 CLEAR TO 9,9  6. Dibujar un marco.  @..TO dibuja un marco de línea sencilla en las coordenadas especificadas. Si se emplea la opción DOUBLE, el marco dibujado será de línea doble.  @ , TO , [DOUBLE]  7. Dibujar una caja.  @..BOX construye una caja entre las coordenadas indicadas y con los códigos ASCII especificados en . El orden de los caracteres es:  1. Esquina superior izquierda 2. Línea horizontal superior 3. Esquina superior derecha 4. Línea vertical derecha 5. Esquina inferior derecha 6. Línea horizontal inferior 7. Esquina inferior izquierda 8. Línea vertical izquierda 9. Carácter de relleno  @ ,,, BOX  Ejemplo_1:  cadena = "+-+¦+-+¦¦" @ 1,1,10,10 BOX cadena  8. Hacer un menú.  @..PROMPT facilita la creación de menús en nuestros programas. Cada opción se muestra con un PROMPT en una posición especifica de la pantalla y se le acompaña opcionalmente de un mensaje aclaratorio.  @ , PROMPT [MESSAGE ]  SET WRAP ON/OFF posibilita la rotación al alcanzar la primera o última opción.  SET MESSAGE determina el número de fila donde aparecerán los mensajes de las distintas opciones.  SET MESSAGE TO [ [CENTER/CENTRE]]  CENTER/CENTRE muestra el mensaje en la fila especificada centrándolo.  MENU TO sirve para leer el valor numérico que representa a la opción seleccionada. Dicho valor se asigna automáticamente y representa el número de orden de cada PROMPT.  Ejemplo_1:  SET WRAP ON SET MESSAGE TO 23 CENTER @ 1,1 PROMPT "ALTA " MESSAGE "Alta de usuarios " @ 2,1 PROMPT "BAJA " MESSAGE "Baja de usuarios " @ 3,1 PROMPT "LISTADO " MESSAGE "Listado DESDE/HASTA" MENU TO opcion DO CASE CASE opcion = 1 ..... CASE opcion = 2 ..... CASE opcion = 3 ..... ENDCASE  9. Salvar/Restaurar pantallas.  SAVE SCREEN salva la pantalla actual así como su estructura de variables leídas y pendientes de leer.  SAVE SCREEN [TO ]  TO indica que la pantalla será almacenada en la variable de memoria . Esta variable será de tipo carácter.  RESTORE SCREEN restaura una pantalla almacenada previamente  RESTORE SCREEN [FROM ]  SAVESCREEN() almacena una parte de la pantalla en una variable de memoria  = SAVESCREEN(,,,)  RESTSCREEN() restaura una área de una pantalla salvada previamente.  RESTSCREEN(,,,,)  Ejemplo_1:  @ 2,3 SAY " -------- " @ 3,3 SAY " CLIENTES " @ 4,3 SAY " -------- " SAVE SCREEN TO panta CLEAR INKEY(0) RESTORE FROM panta RETURN  Ejemplo_2:  @ 2,3 SAY " -------- " @ 3,3 SAY " CLIENTES " @ 4,3 SAY " -------- " panta = SAVESCREEN(3,3,4,12) CLEAR INKEY(0) RESTSCREEN(5,5,6,14,panta)  VII. Bifurcación y bucles.  1. IF (Si cumple condición...).  Bifurca un programa entre una condición y su opuesta. Puede usarse como mandato o como función. En el primer caso, lo que hace es ejecutar alternativamente unas instrucciones u otras y en el segundo devolver alternativamente un valor u otro. La sintáxis de la función puede ser IF() o IIF().  Mandato:  IF  [ELSEIF ]  [ELSE ] ENDIF  Función:  IIF/IF(,&lt;.T.&gt;,&lt;.F.&gt;)&lt;br /&gt;&lt;br /&gt;es la condición que se desea establecer&lt;br /&gt;&lt;br /&gt;ELSEIF reconoce órdenes cuando se cumple la que condición expresada.&lt;br /&gt;&lt;br /&gt;ELSE realiza las distintas órdenes que se indican cuando la condición es falsa.&lt;br /&gt;&lt;br /&gt;&lt;.T.&gt; Indica la expresión a evaluar para el valor verdadero de la condición.&lt;br /&gt;&lt;br /&gt;&lt;.F.&gt; Indica la expresión a evaluar para el valor falso de la condición.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF sexo = "V"&lt;br /&gt;peso = 20&lt;br /&gt;ELSE&lt;br /&gt;peso = 12&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;IF porcentaje &gt; 10&lt;br /&gt;porcentaje = porcentaje - 2&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;Ejemplo_3:&lt;br /&gt;&lt;br /&gt;salario = salario + IIF(ho&gt;80,80000+1500*(ho-80),80000)&lt;br /&gt;&lt;br /&gt;2. DO CASE (En caso de cumplir condición...).&lt;br /&gt;&lt;br /&gt;Bifurca la ejecución de un programa según las diferentes condiciones especificadas. OTHERWISE representa todos los casos que no cumplen ninguna condición.&lt;br /&gt;&lt;br /&gt;DO CASE&lt;br /&gt;CASE&lt;br /&gt;&lt;br /&gt;CASE&lt;br /&gt;&lt;br /&gt;OTHERWISE&lt;br /&gt;&lt;br /&gt;ENDCASE&lt;br /&gt;&lt;br /&gt;son las diferentes condiciones.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DO CASE&lt;br /&gt;CASE velocidad &gt; 180&lt;br /&gt;consumo = 4&lt;br /&gt;CASE velocidad &gt; 120&lt;br /&gt;consumo = 3&lt;br /&gt;CASE velocidad &gt; 80&lt;br /&gt;consumo = 2&lt;br /&gt;OTHERWISE&lt;br /&gt;consumo = 1&lt;br /&gt;ENDCASE&lt;br /&gt;&lt;br /&gt;3. FOR..NEXT (Desde un valor hasta alcanzar otro).&lt;br /&gt;&lt;br /&gt;Permite la creación de una estructura de bucle que se ejecuta para un rango de valores determinados de forma ascendente o descendente.&lt;br /&gt;&lt;br /&gt;FOR TO [STEP ]&lt;br /&gt;&lt;br /&gt;[EXIT]&lt;br /&gt;&lt;br /&gt;[LOOP]&lt;br /&gt;NEXT&lt;br /&gt;&lt;br /&gt;es el valor inicial. Este valor se asignará a una variable de control.&lt;br /&gt;&lt;br /&gt;es el valor final del bucle.&lt;br /&gt;&lt;br /&gt;STEP indica el incremento o decremento de la variable. Por defecto incrementa en 1.&lt;br /&gt;&lt;br /&gt;EXIT detiene el bucle pasando el control a la sentencia posterior a NEXT.&lt;br /&gt;&lt;br /&gt;LOOP pasa de nuevo el control al comienzo del bucle, sin necesidad de que se llegue a NEXT.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;FOR N=1 TO 10&lt;br /&gt;CUADRADO = N**N&lt;br /&gt;? CUADRADO&lt;br /&gt;NEXT&lt;br /&gt;&lt;br /&gt;4. DO WHILE (Hacer mientras que cumpla condición...).&lt;br /&gt;&lt;br /&gt;DO WHILE realiza una estructura de bucle mientras se cumpla la condición especificada. DO WHILE comienza y continúa el bucle si se cumple la condición. ENDDO devuelve el control al principio.&lt;br /&gt;&lt;br /&gt;DO WHILE&lt;br /&gt;&lt;br /&gt;[EXIT]&lt;br /&gt;[LOOP]&lt;br /&gt;ENDDO&lt;br /&gt;&lt;br /&gt;es la condición que se debe cumplir para que se ejecute el bucle.&lt;br /&gt;&lt;br /&gt;LOOP manda todo el proceso de nuevo al comienzo del bucle, sin necesidad de que se llegue al final, es decir a ENDDO.&lt;br /&gt;&lt;br /&gt;EXIT fuerza a que se pare el proceso y sale del bucle aunque la condición no haya cesado de darse.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DO WHILE .T.&lt;br /&gt;@ 2,2 PROMPT "CLIENTES"&lt;br /&gt;@ 3,2 PROMPT "PROVEEDORES"&lt;br /&gt;MENU TO opcion&lt;br /&gt;DO CASE&lt;br /&gt;CASE opcion = 1&lt;br /&gt;DO CLI&lt;br /&gt;CASE opcion = 2&lt;br /&gt;DO PRO&lt;br /&gt;CASE LASTKEY() = 27&lt;br /&gt;CLEAR&lt;br /&gt;RETURN&lt;br /&gt;ENDCASE&lt;br /&gt;ENDDO&lt;br /&gt;&lt;br /&gt;Ejemplo_2:&lt;br /&gt;&lt;br /&gt;C=0&lt;br /&gt;DO WHILE C&lt;100 c="C+1"&gt;&lt;br /&gt;&lt;br /&gt;VIII. Fin.&lt;br /&gt;&lt;br /&gt;1. Retornar.&lt;br /&gt;&lt;br /&gt;RETURN termina un procedimiento, programa, o función, devolviendo el control al procedimiento de llamada o al DOS.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DO BORRAR WITH 2,2,20,20&lt;br /&gt;..&lt;br /&gt;..&lt;br /&gt;..&lt;br /&gt;QUIT&lt;br /&gt;&lt;br /&gt;PROCEDURE BORRAR&lt;br /&gt;PARAMETERS X1,Y1,X2,Y2&lt;br /&gt;@ X1,Y1 CLEAR TO X2,Y2&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;2. Terminar.&lt;br /&gt;&lt;br /&gt;QUIT termina la ejecución de un programa devolviendo el control al DOS.&lt;br /&gt;&lt;br /&gt;Este mandato realiza la misma función que CANCEL o que RETURN en el procedimiento de más alto nivel.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE FICHERO INDEX INDICE&lt;br /&gt;SEEK CLAVE&lt;br /&gt;IF FOUND()&lt;br /&gt;DO PROCESO&lt;br /&gt;ELSE&lt;br /&gt;QUIT&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;3. Cancelar.&lt;br /&gt;&lt;br /&gt;CANCEL cancela la ejecución de un programa o procedimiento, devolviendo el control al sistema operativo.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;clave = SPACE(4)&lt;br /&gt;@ 4,4 SAY "Clave: " GET clave PICTURE "@!"&lt;br /&gt;READ&lt;br /&gt;IF clave # "9876"&lt;br /&gt;CANCEL&lt;br /&gt;ELSE&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;IX. Mantenimiento de ficheros.&lt;br /&gt;&lt;br /&gt;1. Renombrar fichero.&lt;br /&gt;&lt;br /&gt;RENAME renombra ficheros. Es el equivalente al RENAME del DOS ,aunque su sintaxis es algo distinta.&lt;br /&gt;&lt;br /&gt;RENAME TO&lt;br /&gt;&lt;br /&gt;es el nombre inicial del fichero y es el nuevo nuevo. Tanto como deben incluir la extesión del fichero.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;RENAME CLIENTES.DBF TO CLIENTES.DAT&lt;br /&gt;USE CLIENTES.DAT&lt;br /&gt;&lt;br /&gt;2. Copiar ficheros.&lt;br /&gt;&lt;br /&gt;COPY FILE copia el contenido de en . No sirven con este mandato los comodines para copiar varios ficheros en bloque. Salvo esta excepción funciona igual que el COPY del DOS.&lt;br /&gt;&lt;br /&gt;Es importante recordar que siempre hemos de proporcionarle las vías donde buscar los ficheros a copiar y donde queremos copiarlos. Si no se le especifica esta última el fichero se deposita en el directorio de trabajo.&lt;br /&gt;&lt;br /&gt;COPY FILE TO&lt;br /&gt;&lt;br /&gt;es el fichero origen y el fichero destino.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;COPY FILE CLIENTES.DAT TO CLIENTES.DBF&lt;br /&gt;&lt;br /&gt;3. Borrar ficheros.&lt;br /&gt;&lt;br /&gt;DELETE FILE y ERASE borran ficheros. Al especificar el nombre del fichero a borrar debe figurar también su extensión. Antes de usar este comando es necesario cerrar el fichero a borrar con el comando CLOSE.&lt;br /&gt;&lt;br /&gt;ERASE/DELETE FILE&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;USE CLIENTES&lt;br /&gt;..&lt;br /&gt;..&lt;br /&gt;CLOSE DATABASES&lt;br /&gt;DELETE FILE CLIENTES.DBF&lt;br /&gt;&lt;br /&gt;4. LLamada al Dos.&lt;br /&gt;&lt;br /&gt;Además de las órdenes elementales de mantenimiento de ficheros vistas anteriormente, existe la posibilidad de invocar cualquiera del DOS con RUN o !. Por ejemplo, para salir temporalmente de un programa podemos incluir un RUN COMMAND.COM y regresar con EXIT.&lt;br /&gt;&lt;br /&gt;RUN&lt;br /&gt;!&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;RUN CHKDSK &gt; CHEQDIS.TXT&lt;br /&gt;&lt;br /&gt;5. Comprobar la existencia de un fichero.&lt;br /&gt;&lt;br /&gt;Antes de realizar cualquier operación con un fichero podemos comprobar su existencia con la función FILE() que nos retornará un verdadero (.T.) o un falso (.F.).&lt;br /&gt;&lt;br /&gt;FILE()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF FILE("CLIENTES.DBF")&lt;br /&gt;SORT ON NOMBRE TO CLISORT&lt;br /&gt;DELETE FILE CLIENTES.DBF&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;X. Procedimientos y funciones.&lt;br /&gt;&lt;br /&gt;1. Procedimiento.&lt;br /&gt;&lt;br /&gt;PROCEDURE indica el principio de un procedimiento.&lt;br /&gt;&lt;br /&gt;PROCEDURE&lt;br /&gt;&lt;órdenes&gt;&lt;br /&gt;[RETURN]&lt;br /&gt;&lt;br /&gt;- Debe de empezar con una letra y sólo evalúa los 10 primeros caracteres.&lt;br /&gt;&lt;br /&gt;RETURN - Es aconsejable su uso para determinar el fin de un procedimiento, aunque no necesario, ya que detecta el fin al encontrar otro procedure o una marca de fin de archivo.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;CLEAR&lt;br /&gt;DO FONDO&lt;br /&gt;INKEY(0)&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;PROCEDURE Fondo&lt;br /&gt;FOR I=0 TO 24&lt;br /&gt;@ I, 0 SAY REPLICATE("¦", 80 )&lt;br /&gt;NEXT&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;2. LLamada a un procedimiento.&lt;br /&gt;&lt;br /&gt;DO ejecuta un procedimiento escrito en Clipper, C o ensamblador, pasándole parámetros (hasta 128) con WITH.&lt;br /&gt;&lt;br /&gt;DO [WITH ]&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;PROCEDURE LISTACURSOS&lt;br /&gt;IF !ISPRINTER()&lt;br /&gt;DO MSGIMPRESORA&lt;br /&gt;ENDIF&lt;br /&gt;* órdenes ...&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;PROCEDURE MSGIMPRESORA&lt;br /&gt;CLEAR&lt;br /&gt;@ 9, 28 TO 12, 51&lt;br /&gt;@ 10,30 SAY "CONECTE LA IMPRESORA"&lt;br /&gt;@ 11,32 SAY "Y PULSE UNA TECLA"&lt;br /&gt;INKEY(0)&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;3. Creación de un fichero de procedimientos.&lt;br /&gt;&lt;br /&gt;SET PROCEDURE Activa los archivos de procedimientos especificados.&lt;br /&gt;&lt;br /&gt;SET PROCEDURE TO [ ]&lt;br /&gt;&lt;br /&gt;- Si se omite la extensión, se asume que es (.PRG).&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;SET PROCEDURE TO CLIENTES&lt;br /&gt;SET PROCEDURE TO PROVEED&lt;br /&gt;SET PROCEDURE TO MATERIAL&lt;br /&gt;&lt;br /&gt;4. Nombre del procedimiento y número de línea.&lt;br /&gt;&lt;br /&gt;PROCNAME() indica el nombre del procedimiento o programa que estamos ejecutando.&lt;br /&gt;&lt;br /&gt;PROCNAME()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;? "Procedimiento en uso : ", procname()&lt;br /&gt;&lt;br /&gt;PROCLINE() Devuelve el número de la línea del código fuente en curso del programa. Siempre que no le hayamos indicado al compilador que no numere las líneas.&lt;br /&gt;&lt;br /&gt;PROCLINE()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;? procline(), "Linea ", cLinea&lt;br /&gt;&lt;br /&gt;5. Creación de una función.&lt;br /&gt;&lt;br /&gt;FUNCTION Declara una función definida por el usuario escrita en Clipper.&lt;br /&gt;&lt;br /&gt;FUNCTION&lt;br /&gt;&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;- Solo acepta los diez primeros caracteres.&lt;br /&gt;&lt;br /&gt;- Es obligatorio la devolución de un valor.&lt;br /&gt;&lt;br /&gt;Para llamar a una función de usuario, proceda del siguiente modo:&lt;br /&gt;&lt;br /&gt;función( )&lt;br /&gt;&lt;br /&gt;Los parámetros se pasan por valor, exceptuando los arrays, o si el parámetros es precedido por una arroba (@), entonces es pasado por referencia.&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;CLEAR&lt;br /&gt;@ 24,0 SAY ISBISIESTO( DATE() )&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;FUNCTION ISBISIESTO&lt;br /&gt;PARAMETERS DFECHA&lt;br /&gt;PRIVATE DANY, CCADENA, LDEVUELVE&lt;br /&gt;DANY = YEAR( DFECHA )&lt;br /&gt;CCADENA = CTOD( "29-02-" + STR(DANY))&lt;br /&gt;IF DOW(CCADENA)=0&lt;br /&gt;LDEVUELVE = .F.&lt;br /&gt;ELSE&lt;br /&gt;LDEVUELVE = .T.&lt;br /&gt;ENDIF&lt;br /&gt;RETURN LDEVUELVE&lt;br /&gt;&lt;br /&gt;6. Conocer el número de parámetros.&lt;br /&gt;&lt;br /&gt;PCOUNT() Determina el número de parámetros pasados a un procedimiento o función definida por el usuario.&lt;br /&gt;&lt;br /&gt;PCOUNT()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;PROCEDURE EDITOR&lt;br /&gt;PARAMETERS CFICHERO&lt;br /&gt;IF PCOUNT() = 0&lt;br /&gt;@ 24,0 SAY "INDIQUE EL FICHERO: " GET CFICHERO&lt;br /&gt;READ&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;XI. Tablas.&lt;br /&gt;&lt;br /&gt;1 Declaración tablas.&lt;br /&gt;&lt;br /&gt;Una tabla es un área de memoria que puede reservarse para contener un grupo de datos. Una tabla consta de un identificativo o nombre y un número definible de posiciones (de 1 a 1024 en Clipper '87). Estas posiciones pueden contener datos numéricos, alfabéticos, fechas, etc. Para acceder a uno de los datos contenido en una tabla se hará indicando el número de posición que ocupa. Existen varias funciones que posibilitan realizar operaciones en una tabla tales como añadir nuevos datos, eliminar datos, rellenar, etc. Este tipo de estructuras de memoria se utilizan como soporte temporal de los datos.&lt;br /&gt;&lt;br /&gt;DECLARE declara una o más áreas de memoria (arrays) con una longitud específica. Antes de poder realizar cualquier operación con una tabla debemos declararla.&lt;br /&gt;&lt;br /&gt;DECLARE []{,[]...}&lt;br /&gt;&lt;br /&gt;es el nombre de la tabla&lt;br /&gt;es la longitud de la tabla (1-1024)&lt;br /&gt;Ejemplo_1: DECLARE PROVINCIA[8]&lt;br /&gt;PROVINCIA[1] = "ALMERIA"&lt;br /&gt;PROVINCIA[2] = "CADIZ "&lt;br /&gt;PROVINCIA[3] = "CORDOBA"&lt;br /&gt;....&lt;br /&gt;&lt;br /&gt;Ejemplo_2: numero = 8&lt;br /&gt;DECLARE PROVINCIA[numero]&lt;br /&gt;Ejemplo_3: tipo = "FICHA"&lt;br /&gt;numero = "01"&lt;br /&gt;tabla = tipo+numero&lt;br /&gt;DECLARE &amp;amp;tabla[4]&lt;br /&gt;&amp;amp;tabla[1] = "ANDALUCIA"&lt;br /&gt;&amp;amp;tabla[2] = 8&lt;br /&gt;&amp;amp;tabla[3] = .T.&lt;br /&gt;&amp;amp;tabla[4] = CTOD("01/01/92")&lt;br /&gt;&lt;br /&gt;2 Longitud.&lt;br /&gt;&lt;br /&gt;LEN es una función que devuelve el número de elementos que tiene una tabla, o lo que es lo mismo la longitud de la tabla indicada.&lt;br /&gt;LEN()&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla&lt;br /&gt;&lt;br /&gt;Ejemplo_1: DECLARE PROVINCIA[8]&lt;br /&gt;? LEN(PROVINCIA)&lt;br /&gt;&lt;br /&gt;3 Insertar.&lt;br /&gt;&lt;br /&gt;La inserción de nuevos elementos en una tabla es posible mediante la función AINS indicándose el nombre de la tabla y la posición donde se desea insertar el nuevo elemento. Automáticamente, el elemento insertado desplazará a los posteriores en una posición y el último se perderá.&lt;br /&gt;AINS(,)&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla&lt;br /&gt;- Posición elemento&lt;br /&gt;&lt;br /&gt;Ejemplo_1: DECLARE NOMBRE[3]&lt;br /&gt;NOMBRE[1] = "LUIS"&lt;br /&gt;NOMBRE[2] = "MARIA"&lt;br /&gt;NOMBRE[3] = "CARLOS"&lt;br /&gt;AINS(NOMBRE,2)&lt;br /&gt;NOMBRE[2] = "MANUEL"&lt;br /&gt;? NOMBRE[1]&lt;br /&gt;? NOMBRE[2]&lt;br /&gt;? NOMBRE[3]&lt;br /&gt;&lt;br /&gt;4 Suprimir.&lt;br /&gt;&lt;br /&gt;ADEL suprime elementos en una tabla redimensionándola.&lt;br /&gt;&lt;br /&gt;ADEL(,)&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla&lt;br /&gt;- Posición elemento&lt;br /&gt;&lt;br /&gt;Ejemplo_1: DECLARE NOMBRE[3]&lt;br /&gt;NOMBRE[1] = "LUIS"&lt;br /&gt;NOMBRE[2] = "MARIA"&lt;br /&gt;NOMBRE[3] = "CARLOS"&lt;br /&gt;ADEL(NOMBRE,2)&lt;br /&gt;? NOMBRE[1]&lt;br /&gt;? NOMBRE[2]&lt;br /&gt;&lt;br /&gt;5 Copiar.&lt;br /&gt;&lt;br /&gt;La copia de un elemento o grupo de elementos de una tabla a otra tabla la realiza la función ACOPY, debiendo indicarse la tabla origen, la tabla destino, la posición inicial de la tabla origen, el nº de elementos a copiar y el elemento de la tabla destino donde ha de comenzarse la copia.&lt;br /&gt;&lt;br /&gt;ACOPY(,[, [,[,]]])&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla origen&lt;br /&gt;- Nombre de la tabla destino&lt;br /&gt;- Posición origen en tabla origen a copiar&lt;br /&gt;- Número de elementos a copiar desde&lt;br /&gt;- Elemento destino a comenzar copia&lt;br /&gt;Ejemplo_1: DECLARE TABLA_A[2],TABLA_B[2]&lt;br /&gt;TABLA_A[1] = "LUIS"&lt;br /&gt;TABLA_A[2] = "MARIA"&lt;br /&gt;ACOPY(TABLA_A,TABLA_B)&lt;br /&gt;? TABLA_B[1]&lt;br /&gt;? TABLA_B[2]&lt;br /&gt;&lt;br /&gt;Ejemplo_2: DECLARE TABLA_A[2],TABLA_B[3]&lt;br /&gt;TABLA_A[1] = "A"&lt;br /&gt;TABLA_A[2] = "B"&lt;br /&gt;ACOPY(TABLA_A,TABLA_B,1,1,3)&lt;br /&gt;? TABLA_B[3]&lt;br /&gt;&lt;br /&gt;6 Rellenar.&lt;br /&gt;&lt;br /&gt;AFILL rellena uno o más elementos con la expresión indicada.&lt;br /&gt;&lt;br /&gt;AFILL(,[,[,]])&lt;br /&gt;&lt;br /&gt;- Nombre de la tabla&lt;br /&gt;- Expresión con la que se rellenar la tabla&lt;br /&gt;- Posición donde comenzar a rellenar&lt;br /&gt;- Número de elementos a rellenar desde&lt;br /&gt;Ejemplo_1: DECLARE TLF[2]&lt;br /&gt;TLF[1] = "433-23-23"&lt;br /&gt;TLF[2] = "433-23-24"&lt;br /&gt;AFILL(TLF,"000-00-00",2,1)&lt;br /&gt;? TLF[1]&lt;br /&gt;? TLF[2]&lt;br /&gt;&lt;br /&gt;7 Directorio.&lt;br /&gt;&lt;br /&gt;ADIR accede al directorio del disco almacenado en tablas información relativa a los ficheros y directorios.&lt;br /&gt;&lt;br /&gt;ADIR( [, [, [, [, [,]]]]])&lt;br /&gt;- máscaras posibles en DOS (*/?) o nombre fichero.&lt;br /&gt;- Es la tabla que se rellenar con los nombres de ficheros reseñados en . Tipo C. - Idem. para tamaño en bytes de fichero. Tipo N.&lt;br /&gt;- Idem. para fechas. Tipo D&lt;br /&gt;- Idem. para horas. Tipo C&lt;br /&gt;- Idem. para atributos. Tipo C&lt;br /&gt;&lt;br /&gt;Atributos: A - Fichero archivo D - Directorio H - Oculto R - Sólo Lectura S - Sistema&lt;br /&gt;&lt;br /&gt;Ejemplo_1: fil_prg = ADIR("*.PRG")&lt;br /&gt;&lt;br /&gt;Ejemplo_2: DECLARE TABLA[ADIR("*.PRG")]&lt;br /&gt;&lt;br /&gt;Ejemplo_3: fil_sec = ADIR("*.sec")&lt;br /&gt;DECLARE&lt;br /&gt;NOMBRE[fil_sec],FECHA[fil_sec] ADIR("*.sec",NOMBRE,"",FECHA)&lt;br /&gt;FOR n=1 TO fil_sec&lt;br /&gt;fil_del = NOMBRE[n]&lt;br /&gt;IF FECHA[n] &lt;&gt;&lt;br /&gt;&lt;br /&gt;8 Estructura.&lt;br /&gt;&lt;br /&gt;La estructura de una base de datos puede conocerse mediante la función AFIELDS. Los nombres de campos, tipo, longitud, etc. pueden almacenarse en tablas para el posterior tratamiento.&lt;br /&gt;&lt;br /&gt;AFIELDS( [, [, [, ]]]])&lt;br /&gt;&lt;br /&gt;- Tabla que contendrá nombre de campos.&lt;br /&gt;- Tabla que contendrá tipo de campos.&lt;br /&gt;- Tabla que contendrá longitud de campos.&lt;br /&gt;- Tabla que contendrá número posiciones decima les.&lt;br /&gt;&lt;br /&gt;Ejemplo_1: USE base&lt;br /&gt;num_cam = FCOUNT()&lt;br /&gt;DECLARE NOMBRE[num_cam],TIPO[num_cam] AFIELDS(NOMBRE,TIPO)&lt;br /&gt;FOR n=1 TO num_cam&lt;br /&gt;? NOMBRE[n]&lt;br /&gt;? TIPO[n]&lt;br /&gt;NEXT&lt;br /&gt;&lt;br /&gt;9 Menú.&lt;br /&gt;&lt;br /&gt;ACHOICE es una función que permite generar un menú de persiana con los elementos de una tabla en las posiciones de pantalla que se indiquen. Devuelve un valor de tipo numérico que se corresponde con el número de posición del elemento seleccionado. Si el valor es 0 no se seleccionó ningún elemento.&lt;br /&gt;&lt;br /&gt;ACHOICE(,,,, [,[,[,[,]]]])&lt;br /&gt;&lt;br /&gt;- Coordenada X1 de pantalla&lt;br /&gt;- Coordenada Y1 de pantalla - Coordenada X2 de pantalla&lt;br /&gt;- Coordenada Y2 de pantalla&lt;br /&gt;- tabla que contendrá elementos&lt;br /&gt;- tabla que contendrá valores lógicos&lt;br /&gt;- Función de usuario.&lt;br /&gt;&lt;br /&gt;Pasa 3 parámetros:&lt;br /&gt;1-modalidad: 0 Período de inactividad 1 Se intenta sobrepasar el principio&lt;br /&gt;2 Se intenta sobrepasar el final 3 Espera de tecla específica 4 No se puede escoger una opción&lt;br /&gt;2-elemento actual de la tabla&lt;br /&gt;&lt;br /&gt;3-posición que ocupa el elemento en la ventana&lt;br /&gt;Valores retorno: 0 Suspende selección&lt;br /&gt;1 Devuelve elemento cursor 2 Contin#a proceso selección&lt;br /&gt;3 Va al elemento cuyo primer&lt;br /&gt;carácter corresponde a la última tecla oprimida.&lt;br /&gt;Ejemplo_1: CLEAR&lt;br /&gt;SET SCOREBOARD OFF&lt;br /&gt;SET COLOR TO W+/N,,,,BG/N&lt;br /&gt;DECLARE MEN[3],LOG[3]&lt;br /&gt;MEN[1] = "ALTA "&lt;br /&gt;MEN[2] = "BAJA "&lt;br /&gt;MEN[3] = "MODIFICACION"&lt;br /&gt;LOG[1] = .T.&lt;br /&gt;LOG[2] = .T.&lt;br /&gt;LOG[3] = .T.&lt;br /&gt;clave = SPACE(2)&lt;br /&gt;@ 1,01 SAY "Clave: " GET clave PICTURE "XX" READ&lt;br /&gt;DO CASE&lt;br /&gt;CASE clave = "11"&lt;br /&gt;LOG[3] = .F.&lt;br /&gt;CASE clave = "22"&lt;br /&gt;LOG[2] = .F.&lt;br /&gt;OTHERWISE&lt;br /&gt;RETURN&lt;br /&gt;ENDCASE&lt;br /&gt;@ 4,1 TO 8,19&lt;br /&gt;opcion = ACHOICE(5,2,7,18,MEN,LOG)&lt;br /&gt;10 Base Datos&lt;br /&gt;&lt;br /&gt;DBEDIT visualiza el contenido de una base de datos en pantalla. Es una potente función que permite la edición de los datos sobre una ventana definida en pantalla.&lt;br /&gt;&lt;br /&gt;DBEDIT([[,,[,[]]]] [,],[,][,] [,][,] [,][,] [,])&lt;br /&gt;&lt;br /&gt;posiciones.&lt;br /&gt;- Tabla de nombres de los campos.&lt;br /&gt;- Función de usuario.&lt;br /&gt;- Tabla de modelos de visualización.&lt;br /&gt;- Tabla de encabezados de columnas.&lt;br /&gt;- Tabla de separación de encabezados. - Tabla de separación de columnas.&lt;br /&gt;- Tabla de separación de pies.&lt;br /&gt;- Tabla de pies.&lt;br /&gt;&lt;br /&gt;Cuando se utiliza una función de usuario, DBEDIT() pasa de forma automática dos parámetros:&lt;br /&gt;1-Estado actual de DBEDIT() dependiendo de la última tecla pulsada antes de llamar a la función. Las diferentes modalidades del estado son:&lt;br /&gt;&lt;br /&gt;0 Inactividad&lt;br /&gt;1 Se ha intentado sobrepasar el primer&lt;br /&gt;2 Se ha intentado sobrepasar el último registro&lt;br /&gt;3 El fichero de datos se encuentra vacio&lt;br /&gt;4 Se ha pulsado una tecla específica&lt;br /&gt;&lt;br /&gt;2-Posición que ocupa en la tabla el campo sobre el que nos encontramos posicionados.&lt;br /&gt;Valores de retorno:&lt;br /&gt;&lt;br /&gt;0 Para salir de DBEDIT()&lt;br /&gt;1 Para continuar la ejecución de DBEDIT() 2 Se vuelven a leer los datos nuevamente y se continúa DBEDIT()&lt;br /&gt;3 Se activa la posibilidad de añadir nuevos registros&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt;TAB1[3],TAB2[3],TAB3[3],TAB4[3],TAB5[3],TAB6[3],TAB7[3]&lt;br /&gt;* Nombre campos&lt;br /&gt;&lt;br /&gt;TAB1[1]= "BAS_LOC"&lt;br /&gt;TAB1[2]= "BAS_PRO"&lt;br /&gt;TAB1[3]= "BAS_HAB"&lt;br /&gt;&lt;br /&gt;* Máscaras de visualización&lt;br /&gt;&lt;br /&gt;TAB2[1]= "XXXXXXX"&lt;br /&gt;TAB2[2]= "XXXXXXX"&lt;br /&gt;TAB2[3]= "999,999,999"&lt;br /&gt;&lt;br /&gt;* Encabezados de columna&lt;br /&gt;&lt;br /&gt;TAB3[1]= "LOCALIDAD"&lt;br /&gt;TAB3[2]= "PROVINCIA"&lt;br /&gt;&lt;br /&gt;TAB3[3]= "HABITANTES"&lt;br /&gt;&lt;br /&gt;* Separadores de encabezados&lt;br /&gt;&lt;br /&gt;TAB4[1]= "D"&lt;br /&gt;TAB4[2]= "D"&lt;br /&gt;TAB4[3]= "D"&lt;br /&gt;&lt;br /&gt;* Separadores de columnas&lt;br /&gt;&lt;br /&gt;TAB5[1]= "3"&lt;br /&gt;TAB5[2]= "3"&lt;br /&gt;TAB5[3]= "3"&lt;br /&gt;&lt;br /&gt;* Separadores de pies de página&lt;br /&gt;&lt;br /&gt;TAB6[1]= "D"&lt;br /&gt;TAB6[2]= "D"&lt;br /&gt;TAB6[3]= "D"&lt;br /&gt;&lt;br /&gt;* Pies de página&lt;br /&gt;&lt;br /&gt;TAB7[1]= "DPIE_1D"&lt;br /&gt;TAB7[2]= "DPIE_2D"&lt;br /&gt;TAB7[3]= "DPIE_3D"&lt;br /&gt;&lt;br /&gt;CLEAR&lt;br /&gt;USE BASE&lt;br /&gt;DBEDIT(1,1,7,40,TAB1,"",TAB2,TAB3,TAB4,TAB5,TAB6,TAB7)&lt;br /&gt;&lt;br /&gt;XII. Impresora.&lt;br /&gt;&lt;br /&gt;1. Salida.&lt;br /&gt;&lt;br /&gt;SET DEVICE redirecciona las salidas por pantalla o por impresora. Por defecto es por pantalla.&lt;br /&gt;&lt;br /&gt;SET DEVICE TO SCREEN/PRINTER&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;PROCEDURE LISTACURSOS&lt;br /&gt;IF !ISPRINTER() &amp;amp;&amp;amp; Impresora no conectada&lt;br /&gt;DO MSGIMPRESORA &amp;amp;&amp;amp; Mensaje que conecte&lt;br /&gt;INKEY(0)&lt;br /&gt;ENDIF&lt;br /&gt;SET DEVICE TO PRINT&lt;br /&gt;USE CURSOS INDEX CURSOS&lt;br /&gt;GO TOP&lt;br /&gt;NPAGINA = 1 &amp;amp;&amp;amp; Contador de páginas&lt;br /&gt;NFILA = 5 &amp;amp;&amp;amp; Contador de filas&lt;br /&gt;@ 0,0 SAY CHR(15) &amp;amp;&amp;amp; Impresión comprimida&lt;br /&gt;&lt;br /&gt;* CABECERA&lt;br /&gt;&lt;br /&gt;@ 1,0 SAY "LISTADO CURSOS"&lt;br /&gt;@ 1,115 SAY "PAGINA.: " + LTRIM(STR(NPAGINA))&lt;br /&gt;@ 2,115 SAY "FECHA..: " + DTOC(DATE())&lt;br /&gt;@ 3,0 SAY "Nº"&lt;br /&gt;@ 3,10 SAY "NOMBRE CURSO"&lt;br /&gt;@ 3,90 SAY "PRECIO"&lt;br /&gt;@ 4,0 SAY REPLICATE(CHR(196),132)&lt;br /&gt;&lt;br /&gt;* FIN CABECERA&lt;br /&gt;&lt;br /&gt;DO WHILE .NOT. EOF()&lt;br /&gt;@ NFILA, 0 SAY RECNO() PICTURE "9999"&lt;br /&gt;@ NFILA,10 SAY NOMCURSO&lt;br /&gt;@ NFILA,90 SAY PRECIO&lt;br /&gt;NFILA = NFILA + 1&lt;br /&gt;IF NFILA = 50&lt;br /&gt;EJECT &amp;amp;&amp;amp; Salto de página&lt;br /&gt;NPAGINA = NPAGINA + 1&lt;br /&gt;&lt;br /&gt;* REPETICION DE LA CABECERA&lt;br /&gt;&lt;br /&gt;NFILA = 5&lt;br /&gt;ENDIF&lt;br /&gt;SKIP &amp;amp;&amp;amp; Incrementamos registro&lt;br /&gt;ENDDO&lt;br /&gt;CLOSE&lt;br /&gt;EJECT&lt;br /&gt;@ 0,0 SAY CHR(18) &amp;amp;&amp;amp; Desactivamos comprimido&lt;br /&gt;SET DEVICE TO SCREEN&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;2. Salto de página.&lt;br /&gt;&lt;br /&gt;EJECT realiza un salto de página en la impresora, y pone a cero los valores de la fila y la columna de la impresora.&lt;br /&gt;&lt;br /&gt;Use SETPRC() si necesita poner a cero los valores internos de fila y columna de la impresora sin enviar un salto de página.&lt;br /&gt;&lt;br /&gt;EJECT&lt;br /&gt;&lt;br /&gt;(Ver SET DEVICE, se incluye un ejemplo completo)&lt;br /&gt;&lt;br /&gt;3. Conocer la situación del cabezal de impresión.&lt;br /&gt;&lt;br /&gt;PCOL() devuelve la columna en que se halla el cabezal de impresión. Retorna un número entero.&lt;br /&gt;&lt;br /&gt;Un EJECT (salto de página) coloca PCOL() a cero.&lt;br /&gt;&lt;br /&gt;PCOL()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;SET DEVICE TO PRINTER&lt;br /&gt;@ 10, PCOL() + 10 SAY "BANCO : " + BANCO&lt;br /&gt;&lt;br /&gt;PROW() Devuelve la fila en que se haya el cabezal de impresión. Un salto de página, EJECT, coloca PROW() a cero.&lt;br /&gt;&lt;br /&gt;PROW()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;SET DEVICE TO PRINTER&lt;br /&gt;@ PROW() + 1, 5 SAY "NOMBRE...: " + NOMBRE&lt;br /&gt;@ PROW() + 2, 5 SAY "DIRECCION: " + DIRECCION&lt;br /&gt;@ PROW() + 3, 5 SAY "POBLACION: " + POBLACION&lt;br /&gt;&lt;br /&gt;4. Conocer si está preparada la impresora.&lt;br /&gt;&lt;br /&gt;ISPRINTER() Comprueba si la impresora esta lista para imprimir. Devuelve un valor lógico.&lt;br /&gt;&lt;br /&gt;ISPRINTER()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF !ISPRINTER() &amp;amp;&amp;amp; impresora no conectada&lt;br /&gt;@ 24,0 SAY "* CONECTE LA IMPRESORA, PULSE TECLA *"&lt;br /&gt;INKEY(0)&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;5. Conocer si tiene papel.&lt;br /&gt;&lt;br /&gt;DOSERROR() determina el error producido por el DOS. Devuelve un valor numérico correspondiente a un error. Para la lista de errores consulte el manual de Nantucket.&lt;br /&gt;&lt;br /&gt;El error que genera la impresora por falta de papel es el número 28.&lt;br /&gt;&lt;br /&gt;DOSERROR()&lt;br /&gt;&lt;br /&gt;Ejemplo_1:&lt;br /&gt;&lt;br /&gt;IF DOSERROR() = 28&lt;br /&gt;? "Falta papel"&lt;br /&gt;ENDIF&lt;br /&gt;&lt;br /&gt;XIII. Funciones predefinidas.&lt;br /&gt;&lt;br /&gt;1. De bases de datos.&lt;br /&gt;&lt;br /&gt;ALIAS() Devuelve el alias del área de trabajo.&lt;br /&gt;&lt;br /&gt;DELETED() Devuelve el estado de borrado del registro actual.&lt;br /&gt;&lt;br /&gt;EOF() Indica si se alcanza el final de un archivo.&lt;br /&gt;&lt;br /&gt;BOF() Indica si se alcanza el principio de un archivo.&lt;br /&gt;&lt;br /&gt;DBFILTER() Determina la expresión del filtro.&lt;br /&gt;&lt;br /&gt;FIELDNAME() Devuelve el nombre del campo especificado.&lt;br /&gt;&lt;br /&gt;HEADER() Determina la longitud de cabecera.&lt;br /&gt;&lt;br /&gt;RECSIZE() Determina la longitud del registro.&lt;br /&gt;&lt;br /&gt;FCOUNT() Devuelve el número de campos de la base.&lt;br /&gt;&lt;br /&gt;USED() Determina la base de datos en uso.&lt;br /&gt;&lt;br /&gt;FOUND() Devuelve verdadero si se encontró registro.&lt;br /&gt;&lt;br /&gt;RECNO() Devuelve el número de registro actual.&lt;br /&gt;&lt;br /&gt;LASTREC() Devuelve el número total de registros.&lt;br /&gt;&lt;br /&gt;2. Numéricas.&lt;br /&gt;&lt;br /&gt;ABS() Devuelve el valor absoluto de una expresión.&lt;br /&gt;&lt;br /&gt;EXP() Calcula la exponencial.&lt;br /&gt;&lt;br /&gt;INT() Convierte cualquier expresión numérica en entero.&lt;br /&gt;&lt;br /&gt;LOG() Devuelve el logaritmo natural de un número.&lt;br /&gt;&lt;br /&gt;MIN() Devuelve el valor mínimo de dos números o dos fechas.&lt;br /&gt;&lt;br /&gt;MAX() Devuelve el valor máximo de dos números o dos fechas.&lt;br /&gt;&lt;br /&gt;SQRT() Devuelve la raiz cuadrada de un número positivo.&lt;br /&gt;&lt;br /&gt;ROUND() Devuelve el número redondeando con la cantidad de decimales especificados.&lt;br /&gt;&lt;br /&gt;VAL() Convierte una tira de caracteres a un valor numérico.&lt;br /&gt;&lt;br /&gt;3. Cadenas.&lt;br /&gt;&lt;br /&gt;ASC() Devuelve el código ASCII del carácter izquierdo.&lt;br /&gt;&lt;br /&gt;AT() Devuelve un número que indica la posición de comienzo de una cadena de caracteres dentro de otra.&lt;br /&gt;&lt;br /&gt;CHR() Devuelve el carácter del código ASCII especificado.&lt;br /&gt;&lt;br /&gt;EMPTY() Devuelve verdad si la expresión está vacia.&lt;br /&gt;&lt;br /&gt;ISALPHA() Devuelve verdadero si el primer carácter es alfabético.&lt;br /&gt;&lt;br /&gt;ISLOWER() Determina si el carácter más a la izquierda de la cadena está en minúsculas.&lt;br /&gt;&lt;br /&gt;ISUPPER() Determina si el carácter más a la derecha de la cadena está en mayúsculas.&lt;br /&gt;&lt;br /&gt;LEN() Devuelve el número de caracteres que hay en una cadena&lt;br /&gt;&lt;br /&gt;LEFT() Devuelve el número de caracteres especificados desde la izquierda&lt;br /&gt;&lt;br /&gt;RIGHT() Devuelve el número de caracteres especificados desde la derecha.&lt;br /&gt;&lt;br /&gt;LTRIM() Elimina los espacios de la izquierda de una cadena.&lt;br /&gt;&lt;br /&gt;REPLICATE() Repite una expresión de caracteres.&lt;br /&gt;&lt;br /&gt;SPACE() Crea una cadena de espacios.&lt;br /&gt;&lt;br /&gt;STR() Convierte un valor numérico en cadena.&lt;br /&gt;&lt;br /&gt;STRTRAN() Busca y reemplaza dentro de una cadena de caracteres.&lt;br /&gt;&lt;br /&gt;SUBSTR() Extrae una parte específica de una cadena.&lt;br /&gt;&lt;br /&gt;TRANSFORM() Devuelve la tira de caracteres con el formato especificado.&lt;br /&gt;&lt;br /&gt;TRIM() Elimina los espacios de una cadena.&lt;br /&gt;&lt;br /&gt;4. Fechas.&lt;br /&gt;&lt;br /&gt;CDOW() Devuelve el nombre de día de la semana de una fecha.&lt;br /&gt;&lt;br /&gt;CMONTH() Devuelve el nombre del mes de una fecha.&lt;br /&gt;&lt;br /&gt;CTOD() Convierte a fecha una cadena&lt;br /&gt;&lt;br /&gt;DATE() Devuelve la fecha del sistema&lt;br /&gt;&lt;br /&gt;DAY() Devuelve el número de día del mes de una fecha.&lt;br /&gt;&lt;br /&gt;DOW() Devuelve el número que representa el día de la semana de un valor fecha.&lt;br /&gt;&lt;br /&gt;DTOC() Convierte una fecha a cadena&lt;br /&gt;&lt;br /&gt;DTOS() Convierte una fecha a cadena tipo índice.&lt;br /&gt;&lt;br /&gt;MONTH() Devuelve un número que representa el mes.&lt;br /&gt;&lt;br /&gt;YEAR() Devuelve el valor número completo del año dada una fecha.&lt;br /&gt;&lt;br /&gt;5. Hora.&lt;br /&gt;&lt;br /&gt;SECONDS() Devuelve la hora del sistema como segundos y centésimas.&lt;br /&gt;&lt;br /&gt;TIME() Devuelve la hora del sistema.&lt;br /&gt;&lt;br /&gt;SECS() Devuelve hora como segundos y centésimas.&lt;br /&gt;&lt;br /&gt;TSTGRING() Dada una cantidad de segundos nos devuelve dicha cantidad en formato hora.&lt;br /&gt;&lt;br /&gt;6. Otras funciones de interés.&lt;br /&gt;&lt;br /&gt;FILE() Devuelve verdadero si existe el fichero especificado.&lt;br /&gt;&lt;br /&gt;GETE() Recupera el contenido de una variable de entorno DOS.&lt;br /&gt;&lt;br /&gt;TYPE() Devuelve el tipo de dato de la variable, expresión o campo.&lt;br /&gt;&lt;br /&gt;COL() Devuelve la columna actual del cursor.&lt;br /&gt;&lt;br /&gt;ROW() Devuelve la fila actual del cursor.&lt;br /&gt;&lt;br /&gt;CURDIR() Determina el directorio actual.&lt;br /&gt;&lt;br /&gt;DISKSPACE() Determina el número de bytes disponible en una unidad.&lt;br /&gt;&lt;br /&gt;MEMORY() Devuelve el espacio de memoria libre.&lt;br /&gt;&lt;br /&gt;READVAR() Devuelve el nombre de la variable de un GET/MENU.&lt;br /&gt;&lt;br /&gt;XIV. Ordenes SET.&lt;br /&gt;&lt;br /&gt;1. Ordenes SET TO...&lt;br /&gt;&lt;br /&gt;SET ALTERNATE TO Crea un fichero de protocolo.&lt;br /&gt;&lt;br /&gt;SET COLOR TO Fija los colores de pantalla.&lt;br /&gt;&lt;br /&gt;SET DECIMALS TO Fija el número de los decimales a mostrar en los resultados de las funciones numéricas y cálculos.&lt;br /&gt;&lt;br /&gt;SET DEFAULT TO [:] Especifica la unidad y directorio por defecto para la creación de ficheros.&lt;br /&gt;&lt;br /&gt;SET DATE AMERICAN/ANSI/BRITISH/ITALIAN/FRENCH/GERMAN Fija el formato de los campos de fecha.&lt;br /&gt;&lt;br /&gt;AMERICAN mm/dd/aa&lt;br /&gt;ANSI aa.mm.dd&lt;br /&gt;BRITISH dd/mm/aa&lt;br /&gt;ITALIAN dd-mm-aa&lt;br /&gt;FREMCH dd/mm/aa&lt;br /&gt;GERMAN dd.mm.aa.&lt;br /&gt;&lt;br /&gt;SET DELIMITERS TO Especifica los caracteres empleados como delimitadores.&lt;br /&gt;&lt;br /&gt;SET DEVICE TO SCREEN/PRINTER Dirige el resultado de la instrucción @ al dispositivo elegido.&lt;br /&gt;&lt;br /&gt;SET FILTER TO Hace que la base de datos se vea como si sólo contuviese los registros que cumplen la condición.&lt;br /&gt;&lt;br /&gt;SET INDEX TO Abre el índice indicado y cierra los anteriores abiertos con la misma base de datos.&lt;br /&gt;&lt;br /&gt;SET KEY TO Asigna a una tecla un procedimiento.&lt;br /&gt;&lt;br /&gt;SET MARGIN TO Fija el margen izquierdo de la impresora.&lt;br /&gt;&lt;br /&gt;SET MESSAGE TO /CENTER Establece la línea donde se muestran los mensajes asociados a PROMPT.&lt;br /&gt;&lt;br /&gt;SET ORDER TO [] Establece que fichero índice es el principal.&lt;br /&gt;&lt;br /&gt;SET PATH TO [] Especifica la ruta de búsqueda que Clippersigue en el acceso a ficheros.&lt;br /&gt;&lt;br /&gt;SET PRINTER TO [/] Determina la salida de la impresora.&lt;br /&gt;&lt;br /&gt;SET PROCEDURE TO [] Activa fichero de procedimientos.&lt;br /&gt;&lt;br /&gt;2. Ordenes SET ON/OFF.&lt;br /&gt;&lt;br /&gt;SET ALTERNATE on/OFF Determina cuando la salida se envía al fichero.&lt;br /&gt;&lt;br /&gt;SET BELL on/OFF Determina cuándo suena la alarma durante la entradade datos.&lt;br /&gt;&lt;br /&gt;SET CENTURY on/OFF Determina si una fecha debe mostrar los dígitos del siglo o no.&lt;br /&gt;&lt;br /&gt;SET CONFIRM on/OFF Determina si se requiere pulsar return para cada GET.&lt;br /&gt;&lt;br /&gt;SET CONSOLE on/off Determina si la ejecución de los comandos utilizarán la pantalla como salida..&lt;br /&gt;&lt;br /&gt;SET CURSOR on/off Muestra u oculta el cursor en la pantalla.&lt;br /&gt;&lt;br /&gt;SET DELETED on/OFF Oculta/procesa los registros marcados para borrar.&lt;br /&gt;&lt;br /&gt;SET DELIMITERS on/OFF Determina si se muestran los delimitadores&lt;br /&gt;&lt;br /&gt;SET ESCAPE ON/off Activa/Desactiva el desvío producido al pulsar la tecla ESC.&lt;br /&gt;&lt;br /&gt;SET INTENSITY ON/off Muestra los campos de entrada durante los GETs en color o en vídeo inverso.&lt;br /&gt;&lt;br /&gt;SET PRINT on/OFF Determina si la salida de los comandos @...SAY se mandarán a la impresora.&lt;br /&gt;&lt;br /&gt;SET SCOREBOARD ON/off Determinan si los mensajes de clipper aparecen en la línea 0.&lt;br /&gt;&lt;br /&gt;SET SOFTSEEK on/OFF Permite acceder al registro más próximo si el buscado no se encuentra.&lt;br /&gt;&lt;br /&gt;SET UNIQUE on/OFF Determina si sólo los registros con clave no repetida aparecerán en el índice.&lt;br /&gt;&lt;br /&gt;SET WRAP on/OFF Permite el movimiento circular entre opciones de menús.&lt;br /&gt;&lt;br /&gt;XV. Redes locales.&lt;br /&gt;&lt;br /&gt;1. Bloqueo de registro.&lt;br /&gt;&lt;br /&gt;RLOCK() Bloquea/desbloquea el registro actual del área de trabajo en curso. Para utilizar en redes locales.&lt;br /&gt;&lt;br /&gt;RLOCK() / LOCK()&lt;br /&gt;&lt;br /&gt;2. Bloqueo de ficheros.&lt;br /&gt;&lt;br /&gt;FLOCK() Bloquea/desbloquea un archivo abierto de base de datos dependiendo de su estado anterior. Sólo se utiliza en redes locales.&lt;br /&gt;&lt;br /&gt;FLOCK()&lt;br /&gt;&lt;br /&gt;3. Desbloqueo.&lt;br /&gt;&lt;br /&gt;UNLOCK Desactiva el bloqueo de los archivos o registros bloqueados por el #ltimo usuario.&lt;br /&gt;&lt;br /&gt;UNLOCK [ALL]&lt;br /&gt;&lt;br /&gt;ALL - Quita todos los bloqueos en curso de todas las áreas de trabajo.&lt;br /&gt;&lt;br /&gt;4. Uso exclusivo de ficheros.&lt;br /&gt;&lt;br /&gt;SET EXCLUSIVE Permite el uso exclusivo o no de archivos de base de datos, índices y campos memos, en redes locales. Por defecto esta en ON.&lt;br /&gt;&lt;br /&gt;SET EXCLUSIVE ON/off&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5539267871087722416-316233384819763246?l=wifi4.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/316233384819763246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5539267871087722416/posts/default/316233384819763246'/><link rel='alternate' type='text/html' href='http://wifi4.blogspot.com/2009/11/programacion-en-c_29.html' title='PROGRAMACION EN C++'/><author><name>Wifi4.0</name><uri>http://www.blogger.com/profile/16699432101663038724</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5539267871087722416.post-9223084029380833739</id><published>2009-11-29T01:20:00.001-08:00</published><updated>2009-11-29T04:06:11.617-08:00</updated><title type='text'></title><content type='html'>&lt;span style="font-family: times new roman;font-size:130%;" &gt;Herramientas básicas&lt;br /&gt;&lt;br /&gt;   Existen dos formas de ejecutar código Python. Podemos escribir líneas de código en el intérprete y obtener una respuesta del intérprete para cada línea (sesión interactiva) o bien podemos escribir el código de un programa en un archivo de texto y ejecutarlo.&lt;br /&gt;A la hora de realizar una sesión interactiva os aconsejo instalar y utilizar iPython, en lugar de la consola interactiva de Python. Se puede encontrar en http://ipython.scipy.org/. iPython cuenta con características añadidas muy interesantes, como el autocompletado o el operador ?. (para activar la característica de autocompletado en Windows es necesario instalar PyReadline, que puede descargarse desde http://ipython.scipy.org/ moin/PyReadline/Intro)&lt;br /&gt;La función de autocompletado se lanza pulsando el tabulador. Si escribimos fi y pulsamos Tab nos mostrará una lista de los objetos que comienzan con fi (file, filter y finally). Si escribimos file. y pulsamos Tab nos mostrará una lista de los métodos y propiedades del objeto file.&lt;br /&gt;El operador ? nos muestra información sobre los objetos. Se utiliza añadiendo el símbolo de interrogación al final del nombre del objeto del cual queremos más información. Por ejemplo:&lt;br /&gt;In [3]: str?&lt;br /&gt;&lt;br /&gt;-Introducción&lt;br /&gt;Type: type&lt;br /&gt;Base Class:&lt;br /&gt;String Form:&lt;br /&gt;Namespace: Python builtin&lt;br /&gt;Docstring:&lt;br /&gt;str(object) -&gt; string&lt;br /&gt;&lt;br /&gt;  Return a nice string representation of the object.&lt;br /&gt;If the argument is a string, the return value is the same object.&lt;br /&gt;En el campo de IDEs y editores de código gratuitos PyDEV (http://pydev.sourceforge.net/) se alza como cabeza de serie. PyDEV es un plugin para Eclipse que permite utilizar este IDE multiplataforma para programar en Python. Cuenta con autocompletado de código (con información sobre cada elemento), resaltado de sintaxis, un depurador gráfico, resaltado de errores, explorador de clases, formateo del código, refactorización, etc. Sin duda es la opción más completa, sobre todo si instalamos las extensiones comerciales, aunque necesita de una cantidad importante de memoria y no es del todo estable.&lt;br /&gt;Otras opciones gratuitas a considerar son SPE o Stani’s Python Editor (http://sourceforge.net/projects/spe/), Eric (http://die-offenbachs.de/eric/), BOA Constructor (http://boa-constructor.sourceforge.net/) o incluso emacs o vim.&lt;br /&gt;Si no te importa desembolsar algo de dinero, Komodo (http://www.activestate.com/komodo_ide/) y Wing IDE (http://www.wingware.com/) son también muy buenas opciones, con montones de características interesantes, como PyDEV, pero mucho más estables y robustos. Además, si desarrollas software libre no comercial puedes contactar con Wing Ware y obtener, con un poco de suerte, una licencia gratuita para Wing IDE Professional :)&lt;br /&gt;&lt;br /&gt;  Mi primer programa en Python&lt;br /&gt;&lt;br /&gt;Como comentábamos en el capítulo anterior existen dos formas de ejecutar código Python, bien en una sesión interactiva (línea a línea) con el intérprete, o bien de la forma habitual, escribiendo el código en un archivo de código fuente y ejecutándolo.&lt;br /&gt;El primer programa que vamos a escribir en Python es el clásico Hola Mundo, y en este lenguaje es tan simple como:&lt;br /&gt;print “Hola Mundo”&lt;br /&gt;Vamos a probarlo primero en el intérprete. Ejecuta python o ipython según tus preferencias, escribe la línea anterior y pulsa Enter. El intérprete responderá mostrando en la consola el texto Hola Mundo.&lt;br /&gt;Vamos ahora a crear un archivo de texto con el código anterior, de forma que pudiéramos distribuir nuestro pequeño gran programa entre nuestros amigos. Abre tu editor de texto preferido o bien el IDE que hayas elegido y copia la línea anterior. Guárdalo como hola.py, por ejemplo.&lt;br /&gt;Ejecutar este programa es tan sencillo como indicarle el nombre del archivo a ejecutar al intérprete de Python&lt;br /&gt;python hola.py&lt;br /&gt;&lt;br /&gt;  Mi primer programa en Python&lt;br /&gt;pero vamos a ver cómo simplificarlo aún más.&lt;br /&gt;Si utilizas Windows los archivos .py ya estarán asociados al intérprete de Python, por lo que basta hacer doble clic sobre el archivo para ejecutar el programa. Sin embargo como este programa no hace más que imprimir un texto en la consola, la ejecución es demasiado rápida para poder verlo si quiera. Para remediarlo, vamos a añadir una nueva línea que espere la entrada de datos por parte del usuario.&lt;br /&gt;print “Hola Mundo”&lt;br /&gt;raw_input()&lt;br /&gt;De esta forma se mostrará una consola con el texto Hola Mundo hasta que pulsemos Enter.&lt;br /&gt;Si utilizas Linux (u otro Unix) para conseguir este comportamiento, es decir, para que el sistema operativo abra el archivo .py con el intérprete adecuado, es necesario añadir una nueva línea al principio del archivo:&lt;br /&gt;#!/usr/bin/python&lt;br /&gt;print “Hola Mundo”&lt;br /&gt;raw_input()&lt;br /&gt;A esta línea se le conoce en el mundo Unix como shebang, hashbang o sharpbang. El par de caracteres #! indica al sistema operativo que dicho script se debe ejecutar utilizando el intérprete especificado a continuación. De esto se desprende, evidentemente, que si esta no es la ruta en la que está instalado nuestro intérprete de Python, es necesario cambiarla.&lt;br /&gt;Otra opción es utilizar el programa env (de environment, entorno) para preguntar al sistema por la ruta al intérprete de Python, de forma que nuestros usuarios no tengan ningún problema si se diera el caso de que el programa no estuviera instalado en dicha ruta:&lt;br /&gt;#!/usr/bin/env python&lt;br /&gt;print “Hola Mundo”&lt;br /&gt;raw_input()&lt;br /&gt;Por supuesto además de añadir el shebang, tendremos que dar permisos de ejecución al programa.&lt;br /&gt;&lt;br /&gt;chmod +x hola.py&lt;br /&gt;Y listo, si hacemos doble clic el programa se ejecutará, mostrando una consola con el texto Hola Mundo, como en el caso de Windows.&lt;br /&gt;También podríamos correr el programa desde la consola como si tratara de un ejecutable cualquiera:&lt;br /&gt;./hola.py&lt;br /&gt;&lt;br /&gt;  Tipos básicos&lt;br /&gt;&lt;br /&gt;En Python los tipos básicos se dividen en:&lt;br /&gt;Números, como pueden ser • 3 (entero), 15.57 (de coma flotante) o 7 + 5j (complejos)&lt;br /&gt;Cadenas de texto, como • “Hola Mundo”&lt;br /&gt;Valores booleanos: • True (cierto) y False (falso).&lt;br /&gt;Vamos a crear un par de variables a modo de ejemplo. Una de tipo cadena y una de tipo entero:&lt;br /&gt;# esto es una cadena&lt;br /&gt;c = “Hola Mundo”&lt;br /&gt;# y esto es un entero&lt;br /&gt;e = 23&lt;br /&gt;# podemos comprobarlo con la función type&lt;br /&gt;type(c)&lt;br /&gt;type(e)&lt;br /&gt;Como veis en Python, a diferencia de muchos otros lenguajes, no se declara el tipo de la variable al crearla. En Java, por ejemplo, escribiríamos:&lt;br /&gt;String c = “Hola Mundo”;&lt;br /&gt;int e = 23;&lt;br /&gt;Este pequeño ejemplo también nos ha servido para presentar los comentarios inline en Python: cadenas de texto que comienzan con el carácter # y que Python ignora totalmente. Hay más tipos de comentarios, de los que hablaremos más adelante.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Números&lt;br /&gt;Como decíamos, en Python se pueden representar números enteros, reales y complejos.&lt;br /&gt;Enteros&lt;br /&gt;Los números enteros son aquellos números positivos o negativos que no tienen decimales (además del cero). En Python se pueden representar mediante el tipo int (de integer, entero) o el tipo long (largo). La única diferencia es que el tipo long permite almacenar números más grandes. Es aconsejable no utilizar el tipo long a menos que sea necesario, para no malgastar memoria.&lt;br /&gt;El tipo int de Python se implementa a bajo nivel mediante un tipo long de C. Y dado que Python utiliza C por debajo, como C, y a diferencia de Java, el rango de los valores que puede representar depende de la plataforma.&lt;br /&gt;En la mayor parte de las máquinas el long de C se almacena utilizando 32 bits, es decir, mediante el uso de una variable de tipo int de Python podemos almacenar números de -231 a 231 - 1, o lo que es lo mismo, de -2.147.483.648 a 2.147.483.647. En plataformas de 64 bits, el rango es de -9.223.372.036.854.775.808 hasta 9.223.372.036.854.775.807.&lt;br /&gt;El tipo long de Python permite almacenar números de cualquier precisión, estando limitados solo por la memoria disponible en la máquina.&lt;br /&gt;Al asignar un número a una variable esta pasará a tener tipo int, a menos que el número sea tan grande como para requerir el uso del tipo long.&lt;br /&gt;# type(entero) devolvería int&lt;br /&gt;entero = 23&lt;br /&gt;También podemos indicar a Python que un número se almacene usando long añadiendo una L al final:&lt;br /&gt;# type(entero) devolvería long&lt;br /&gt;entero = 23L&lt;br /&gt;&lt;br /&gt;Tipos básicos&lt;br /&gt;&lt;br /&gt;El literal que se asigna a la variable también se puede expresar como un octal, anteponiendo un cero:&lt;br /&gt;# 027 octal = 23 en base 10&lt;br /&gt;entero = 027&lt;br /&gt;o bien en hexadecimal, anteponiendo un 0x:&lt;br /&gt;# 0×17 hexadecimal = 23 en base 10&lt;br /&gt;entero = 0×17&lt;br /&gt;Reales&lt;br /&gt;Los números reales son los que tienen decimales. En Python se expresan mediante el tipo float. En otros lenguajes de programación, como C, tenemos también el tipo double, similar a float pero de mayor precisión (double = doble precisión). Python, sin embargo, implementa su tipo float a bajo nivel mediante una variable de tipo double de C, es decir, utilizando 64 bits, luego en Python siempre se utiliza doble precisión, y en concreto se sigue el estándar IEEE 754: 1 bit para el signo, 11 para el exponente, y 52 para la mantisa. Esto significa que los valores que podemos representar van desde ±2,2250738585072020 x 10-308 hasta ±1,7976931348623157×10308.&lt;br /&gt;La mayor parte de los lenguajes de programación siguen el mismo esquema para la representación interna. Pero como muchos sabréis esta tiene sus limitaciones, impuestas por el hardware. Por eso desde Python 2.4 contamos también con un nuevo tipo Decimal, para el caso de que se necesite representar fracciones de forma más precisa. Sin embargo este tipo está fuera del alcance de este tutorial, y sólo es necesario para el ámbito de la programación científica y otros relacionados. Para aplicaciones normales podeis utilizar el tipo float sin miedo, como ha venido haciéndose desde hace años, aunque teniendo en cuenta que los números en coma flotante no son precisos (ni en este ni en otros lenguajes de programación).&lt;br /&gt;Para representar un número real en Python se escribe primero la parte entera, seguido de un punto y por último la parte decimal.&lt;br /&gt;real = 0.2703&lt;br /&gt;&lt;br /&gt;También se puede utilizar notación científica, y añadir una e (de exponente) para indicar un exponente en base 10. Por ejemplo:&lt;br /&gt;real = 0.1e-3&lt;br /&gt;sería equivalente a 0.1 x 10-3 = 0.1 x 0.001 = 0.0001&lt;br /&gt;Complejos&lt;br /&gt;Los números complejos son aquellos que tienen parte imaginaria. Si no conocías de su existencia, es más que probable que nunca lo vayas a necesitar, por lo que puedes saltarte este apartado tranquilamente. De hecho la mayor parte de lenguajes de programación carecen de este tipo, aunque sea muy utilizado por ingenieros y científicos en general.&lt;br /&gt;En el caso de que necesitéis utilizar números complejos, o simplemente tengáis curiosidad, os diré que este tipo, llamado complex en Python, también se almacena usando coma flotante, debido a que estos números son una extensión de los números reales. En concreto se almacena en una estructura de C, compuesta por dos variables de tipo double, sirviendo una de ellas para almacenar la parte real y la otra para la parte imaginaria.&lt;br /&gt;Los números complejos en Python se representan de la siguiente forma:&lt;br /&gt;complejo = 2.1 + 7.8j&lt;br /&gt;Operadores&lt;br /&gt;Veamos ahora qué podemos hacer con nuestros números usando los operadores por defecto. Para operaciones más complejas podemos recurrir al módulo math.&lt;br /&gt;Operadores aritméticos&lt;br /&gt;Operador&lt;br /&gt;Descripción&lt;br /&gt;Ejemplo&lt;br /&gt;+&lt;br /&gt;Suma&lt;br /&gt;r = 3 + 2 # r es 5&lt;br /&gt;-&lt;br /&gt;Resta&lt;br /&gt;r = 4 - 7 # r es -3&lt;br /&gt;&lt;br /&gt;Tipos básicos&lt;br /&gt;&lt;br /&gt;Operador&lt;br /&gt;Descripción&lt;br /&gt;Ejemplo&lt;br /&gt;-&lt;br /&gt;Negación&lt;br /&gt;r = -7 # r es -7&lt;br /&gt;*&lt;br /&gt;Multiplicación&lt;br /&gt;r = 2 * 6 # r es 12&lt;br /&gt;**&lt;br /&gt;Exponente&lt;br /&gt;r = 2 ** 6 # r es 64&lt;br /&gt;/&lt;br /&gt;División&lt;br /&gt;r = 3.5 / 2 # r es 1.75&lt;br /&gt;//&lt;br /&gt;División entera&lt;br /&gt;r = 3.5 // 2 # r es 1.0&lt;br /&gt;%&lt;br /&gt;Módulo&lt;br /&gt;r = 7 % 2 # r es 1&lt;br /&gt;Puede que tengáis dudas sobre cómo funciona el operador de módulo, y cuál es la diferencia entre división y división entera.&lt;br /&gt;El operador de módulo no hace otra cosa que devolvernos el resto de la división entre los dos operandos. En el ejemplo, 7/2 sería 3, con 1 de resto, luego el módulo es 1.&lt;br /&gt;La diferencia entre división y división entera no es otra que la que indica su nombre. En la división el resultado que se devuelve es un número real, mientras que en la división entera el resultado que se devuelve es solo la parte entera.&lt;br /&gt;No obstante hay que tener en cuenta que si utilizamos dos operandos enteros, Python determinará que queremos que la variable resultado también sea un entero, por lo que el resultado de, por ejemplo, 3 / 2 y 3 // 2 sería el mismo: 1.&lt;br /&gt;Si quisiéramos obtener los decimales necesitaríamos que al menos uno de los operandos fuera un número real, bien indicando los decimales&lt;br /&gt;r = 3.0 / 2&lt;br /&gt;o bien utilizando la función float (no es necesario que sepais lo que significa el término función, ni que recordeis esta forma, lo veremos un poco más adelante):&lt;br /&gt;r = float(3) / 2&lt;br /&gt;Esto es así porque cuando se mezclan tipos de números, Python conPython&lt;br /&gt;para todos vierte todos los operandos al tipo más complejo de entre los tipos de los operandos.&lt;br /&gt;Operadores a nivel de bit&lt;br /&gt;Si no conocéis estos operadores es poco probable que vayáis a necesitarlos, por lo que podéis obviar esta parte. Si aún así tenéis curiosidad os diré que estos son operadores que actúan sobre las representaciones en binario de los operandos.&lt;br /&gt;Por ejemplo, si veis una operación como 3 &amp;amp; 2, lo que estais viendo es un and bit a bit entre los números binarios 11 y 10 (las representaciones en binario de 3 y 2).&lt;br /&gt;El operador and (&amp;amp;), del inglés “y”, devuelve 1 si el primer bit operando es 1 y el segundo bit operando es 1. Se devuelve 0 en caso contrario.&lt;br /&gt;El resultado de aplicar and bit a bit a 11 y 10 sería entonces el número binario 10, o lo que es lo mismo, 2 en decimal (el primer dígito es 1 para ambas cifras, mientras que el segundo es 1 sólo para una de ellas).&lt;br /&gt;El operador or (|), del inglés “o”, devuelve 1 si el primer operando es 1 o el segundo operando es 1. Para el resto de casos se devuelve 0.&lt;br /&gt;El operador xor u or exclusivo (^) devuelve 1 si uno de los operandos es 1 y el otro no lo es.&lt;br /&gt;El operador not (~), del inglés “no”, sirve para negar uno a uno cada bit; es decir, si el operando es 0, cambia a 1 y si es 1, cambia a 0.&lt;br /&gt;Por último los operadores de desplazamiento (&lt;&lt;&gt;&gt;) sirven para desplazar los bits n posiciones hacia la izquierda o la derecha.&lt;br /&gt;Operador&lt;br /&gt;Descripción&lt;br /&gt;Ejemplo&lt;br /&gt;&amp;amp;&lt;br /&gt;and&lt;br /&gt;r = 3 &amp;amp; 2 # r es 2&lt;br /&gt;|&lt;br /&gt;or&lt;br /&gt;r = 3 | 2 # r es 3&lt;br /&gt;^&lt;br /&gt;xor&lt;br /&gt;r = 3 ^ 2 # r es 1&lt;br /&gt;~&lt;br /&gt;not&lt;br /&gt;r = ~3 # r es -4&lt;br /&gt;&lt;br /&gt;&lt;&lt; r =" 3"&gt;&gt;&lt;br /&gt;Desplazamiento der.&lt;br /&gt;r = 3 &gt;&gt; 1 # r es 1&lt;br /&gt;Cadenas&lt;br /&gt;Las cadenas no son más que texto encerrado entre comillas simples (‘cadena’) o dobles (“cadena”). Dentro de las comillas se pueden añadir caracteres especiales escapándolos con \, como \n, el carácter de nueva línea, o \t, el de tabulación.&lt;br /&gt;Una cadena puede estar precedida por el carácter u o el carácter r, los cuales indican, respectivamente, que se trata de una cadena que utiliza codificación Unicode y una cadena raw (del inglés, cruda). Las cadenas raw se distinguen de las normales en que los caracteres escapados mediante la barra invertida (\) no se sustituyen por sus contrapartidas. Esto es especialmente útil, por ejemplo, para las expresiones regulares, como veremos en el capítulo correspondiente.&lt;br /&gt;unicode = u”äóè”&lt;br /&gt;raw = r”\n”&lt;br /&gt;También es posible encerrar una cadena entre triples comillas (simples o dobles). De esta forma podremos escribir el texto en varias líneas, y al imprimir la cadena, se respetarán los saltos de línea que introdujimos sin tener que recurrir al carácter \n, así como las comillas sin tener que escaparlas.&lt;br /&gt;triple = “““primera linea&lt;br /&gt;esto se vera en otra linea”””&lt;br /&gt;Las cadenas también admiten operadores como +, que funciona realizando una concatenación de las cadenas utilizadas como operandos y *, en la que se repite la cadena tantas veces como lo indique el número utilizado como segundo operando.&lt;br /&gt;a = “uno”&lt;br /&gt;b = “dos”&lt;br /&gt;c = a + b # c es “unodos”&lt;br /&gt;c = a * 3 # c es “unounouno”&lt;br /&gt;&lt;br /&gt;Booleanos&lt;br /&gt;Como decíamos al comienzo del capítulo una variable de tipo booleano sólo puede tener dos valores: True (cierto) y False (falso). Estos valores son especialmente importantes para las expresiones condicionales y los bucles, como veremos más adelante.&lt;br /&gt;En realidad el tipo bool (el tipo de los booleanos) es una subclase del tipo int. Puede que esto no tenga mucho sentido para tí si no conoces los términos de la orientación a objetos, que veremos más adelante, aunque tampoco es nada importante.&lt;br /&gt;Estos son los distintos tipos de operadores con los que podemos trabajar con valores booleanos, los llamados operadores lógicos o condicionales:&lt;br /&gt;Operador&lt;br /&gt;Descripción&lt;br /&gt;Ejemplo&lt;br /&gt;and&lt;br /&gt;¿se cumple a y b?&lt;br /&gt;r = True and False # r es False&lt;br /&gt;or&lt;br /&gt;¿se cumple a o b?&lt;br /&gt;r = True or False # r es True&lt;br /&gt;not&lt;br /&gt;No a&lt;br /&gt;r = not True # r es False&lt;br /&gt;Los valores booleanos son además el resultado de expresiones que utilizan operadores relacionales (comparaciones entre valores):&lt;br /&gt;Operador&lt;br /&gt;Descripción&lt;br /&gt;Ejemplo&lt;br /&gt;==&lt;br /&gt;¿son iguales a y b?&lt;br /&gt;r = 5 == 3 # r es False&lt;br /&gt;!=&lt;br /&gt;¿son distintos a y b?&lt;br /&gt;r = 5 != 3 # r es True&lt;br /&gt;&lt; ¿es a menor que b? r = 5 &lt;&gt;&lt;br /&gt;¿es a mayor que b?&lt;br /&gt;r = 5 &gt; 3 # r es True&lt;br /&gt;&lt;= ¿es a menor o igual que b? r = 5 &lt;= 5 # r es True &gt;=&lt;br /&gt;¿es a mayor o igual que b?&lt;br /&gt;r = 5 &gt;= 3 # r es True&lt;br /&gt;&lt;br /&gt;Colecc iones&lt;br /&gt;En el capítulo anterior vimos algunos tipos básicos, como los números, las cadenas de texto y los booleanos. En esta lección veremos algunos tipos de colecciones de datos: listas, tuplas y diccionarios.&lt;br /&gt;Listas&lt;br /&gt;La lista es un tipo de colección ordenada. Sería equivalente a lo que en otros lenguajes se conoce por arrays, o vectores.&lt;br /&gt;Las listas pueden contener cualquier tipo de dato: números, cadenas, booleanos, … y también listas.&lt;br /&gt;Crear una lista es tan sencillo como indicar entre corchetes, y separados por comas, los valores que queremos incluir en la lista:&lt;br /&gt;l = [22, True, “una lista”, [1, 2]]&lt;br /&gt;Podemos acceder a cada uno de los elementos de la lista escribiendo el nombre de la lista e indicando el índice del elemento entre corchetes. Ten en cuenta sin embargo que el índice del primer elemento de la lista es 0, y no 1:&lt;br /&gt;l = [11, False]&lt;br /&gt;mi_var = l[0] # mi_var vale 11&lt;br /&gt;Si queremos acceder a un elemento de una lista incluida dentro de otra lista tendremos que utilizar dos veces este operador, primero para indicar a qué posición de la lista exterior queremos acceder, y el segundo para seleccionar el elemento de la lista interior:&lt;br /&gt;l = [“una lista”, [1, 2]]&lt;br /&gt;&lt;br /&gt;Colecciones&lt;br /&gt;mi_var = l[1][0] # mi_var vale 1&lt;br /&gt;También podemos utilizar este operador para modificar un elemento de la lista si lo colocamos en la parte izquierda de una asignación:&lt;br /&gt;l = [22, True]&lt;br /&gt;l[0] = 99 # Con esto l valdrá [99, True]&lt;br /&gt;El uso de los corchetes para acceder y modificar los elementos de una lista es común en muchos lenguajes, pero Python nos depara varias sorpresas muy agradables.&lt;br /&gt;Una curiosidad sobre el operador [] de Python es que podemos utilizar también números negativos. Si se utiliza un número negativo como índice, esto se traduce en que el índice empieza a contar desde el final, hacia la izquierda; es decir, con [-1] accederíamos al último elemento de la lista, con [-2] al penúltimo, con [-3], al antepenúltimo, y así sucesivamente.&lt;br /&gt;Otra cosa inusual es lo que en Python se conoce como slicing o particionado, y que consiste en ampliar este mecanismo para permitir seleccionar porciones de la lista. Si en lugar de un número escribimos dos números inicio y fin separados por dos puntos (inicio:fin) Python interpretará que queremos una lista que vaya desde la posición inicio a la posición fin, sin incluir este último. Si escribimos tres números (inicio:fin:salto) en lugar de dos, el tercero se utiliza para determinar cada cuantas posiciones añadir un elemento a la lista.&lt;br /&gt;l = [99, True, “una lista”, [1, 2]]&lt;br /&gt;mi_var = l[0:2] # mi_var vale [99, True]&lt;br /&gt;mi_var = l[0:4:2] # mi_var vale [99, “una lista”]&lt;br /&gt;Los números negativos también se pueden utilizar en un slicing, con el mismo comportamiento que se comentó anteriormente.&lt;br /&gt;Hay que mencionar así mismo que no es necesario indicar el principio y el final del slicing, sino que, si estos se omiten, se usarán por defecto las posiciones de inicio y fin de la lista, respectivamente:&lt;br /&gt;l = [99, True, “una lista”]&lt;br /&gt;mi_var = l[1:] # mi_var vale [True, “una lista”]&lt;br /&gt;mi_var = l[:2] # mi_var vale [99, True]&lt;br /&gt;mi_var = l[:] # mi_var vale [99, True, “una lista”]&lt;br /&gt;mi_var = l[::2] # mi_var vale [99, “una lista”]&lt;br /&gt;También podemos utilizar este mecanismo para modificar la lista:&lt;br /&gt;l = [99, True, “una lista”, [1, 2]]&lt;br /&gt;l[0:2] = [0, 1] # l vale [0, 1, “una lista”, [1, 2]]&lt;br /&gt;pudiendo incluso modificar el tamaño de la lista si la lista de la parte derecha de la asignación tiene un tamaño menor o mayor que el de la selección de la parte izquierda de la asignación:&lt;br /&gt;l[0:2] = [False] # l vale [False, “una lista”, [1, 2]]&lt;br /&gt;En todo caso las listas ofrecen mecanismos más cómodos para ser modificadas a través de las funciones de la clase correspondiente, aunque no veremos estos mecanismos hasta más adelante, después de explicar lo que son las clases, los objetos y las funciones.&lt;br /&gt;Tuplas&lt;br /&gt;Todo lo que hemos explicado sobre las listas se aplica también a las tuplas, a excepción de la forma de definirla, para lo que se utilizan paréntesis en lugar de corchetes.&lt;br /&gt;t = (1, 2, True, “python”)&lt;br /&gt;En realidad el constructor de la tupla es la coma, no el paréntesis, pero el intérprete muestra los paréntesis, y nosotros deberíamos utilizarlos, por claridad.&lt;br /&gt;&gt;&gt;&gt; t = 1, 2, 3&lt;br /&gt;&gt;&gt;&gt; type(t)&lt;br /&gt;type “tuple”&lt;br /&gt;Además hay que tener en cuenta que es necesario añadir una coma para tuplas de un solo elemento, para diferenciarlo de un elemento entre paréntesis.&lt;br /&gt;&gt;&gt;&gt; t = (1)&lt;br /&gt;&gt;&gt;&gt; type(t)&lt;br /&gt;Colecciones&lt;br /&gt;type “int”&lt;br /&gt;&gt;&gt;&gt; t = (1,)&lt;br /&gt;&gt;&gt;&gt; type(t)&lt;br /&gt;type “tuple”&lt;br /&gt;Para referirnos a elementos de una tupla, como en una lista, se usa el operador []:&lt;br /&gt;mi_var = t[0] # mi_var es 1&lt;br /&gt;mi_var = t[0:2] # mi_var es (1, 2)&lt;br /&gt;Podemos utilizar el operador [] debido a que las tuplas, al igual que las listas, forman parte de un tipo de objetos llamados secuencias. Permitirme un pequeño inciso para indicaros que las cadenas de texto también son secuencias, por lo que no os extrañará que podamos hacer cosas como estas:&lt;br /&gt;c = “hola mundo”&lt;br /&gt;c[0] # h&lt;br /&gt;c[5:] # mundo&lt;br /&gt;c[::3] # hauo&lt;br /&gt;Volviendo al tema de las tuplas, su diferencia con las listas estriba en que las tuplas no poseen estos mecanismos de modificación a través de funciones tan útiles de los que hablábamos al final de la anterior sección.&lt;br /&gt;Además son inmutables, es decir, sus valores no se pueden modificar una vez creada; y tienen un tamaño fijo.&lt;br /&gt;A cambio de estas limitaciones las tuplas son más “ligeras” que las listas, por lo que si el uso que le vamos a dar a una colección es muy básico, puedes utilizar tuplas en lugar de listas y ahorrar memoria.&lt;br /&gt;Diccionarios&lt;br /&gt;Los diccionarios, también llamados matrices asociativas, deben su nombre a que son colecciones que relacionan una clave y un valor. Por ejemplo, veamos un diccionario de películas y directores:&lt;br /&gt;d = {“Love Actually “: “Richard Curtis”,&lt;br /&gt;“Kill Bill”: “Tarantino”,&lt;br /&gt;“Amélie”: “Jean-Pierre Jeunet”}&lt;br /&gt;El primer valor se trata de la clave y el segundo del valor asociado a la clave. Como clave podemos utilizar cualquier valor inmutable: podríamos usar números, cadenas, booleanos, tuplas, … pero no listas o diccionarios, dado que son mutables. Esto es así porque los diccionarios se implementan como tablas hash, y a la hora de introducir un nuevo par clave-valor en el diccionario se calcula el hash de la clave para después poder encontrar la entrada correspondiente rápidamente. Si se modificara el objeto clave después de haber sido introducido en el diccionario, evidentemente, su hash también cambiaría y no podría ser encontrado.&lt;br /&gt;La diferencia principal entre los diccionarios y las listas o las tuplas es que a los valores almacenados en un diccionario se les accede no por su índice, porque de hecho no tienen orden, sino por su clave, utilizando de nuevo el operador [].&lt;br /&gt;d[“Love Actually “] # devuelve “Richard Curtis”&lt;br /&gt;Al igual que en listas y tuplas también se puede utilizar este operador para reasignar valores.&lt;br /&gt;d[“Kill Bill”] = “Quentin Tarantino”&lt;br /&gt;Sin embargo en este caso no se puede utilizar slicing, entre otras cosas porque los diccionarios no son secuencias, si no mappings (mapeados, asociaciones).&lt;br /&gt;&lt;br /&gt;Control de flujo&lt;br /&gt;  En esta lección vamos a ver los condicionales y los bucles.&lt;br /&gt;Sentencias condicionales&lt;br /&gt;Si un programa no fuera más que una lista de órdenes a ejecutar de forma secuencial, una por una, no tendría mucha utilidad. Los condicionales nos permiten comprobar condiciones y hacer que nuestro programa se comporte de una forma u otra, que ejecute un fragmento de código u otro, dependiendo de esta condición.&lt;br /&gt;Aquí es donde cobran su importancia el tipo booleano y los operadores lógicos y relacionales que aprendimos en el capítulo sobre los tipos básicos de Python.&lt;br /&gt;if&lt;br /&gt;La forma más simple de un estamento condicional es un if (del inglés si) seguido de la condición a evaluar, dos puntos (:) y en la siguiente línea e indentado, el código a ejecutar en caso de que se cumpla dicha condición.&lt;br /&gt;fav = “mundogeek.net”&lt;br /&gt;# si (if) fav es igual a “mundogeek.net”&lt;br /&gt;if fav == “mundogeek.net”:&lt;br /&gt;print “Tienes buen gusto!”&lt;br /&gt;print “Gracias”&lt;br /&gt;Como veis es bastante sencillo.&lt;br /&gt;Eso si, aseguraros de que indentáis el código tal cual se ha hecho en el ejemplo, es decir, aseguraros de pulsar Tabulación antes de las dos órdenes print, dado que esta es la forma de Python de saber que vuestra intención es la de que los dos print se ejecuten sólo en el caso de que se cumpla la condición, y no la de que se imprima la primera cadena si se cumple la condición y la otra siempre, cosa que se expresaría así:&lt;br /&gt;if fav == “mundogeek.net”:&lt;br /&gt;print “Tienes buen gusto!”&lt;br /&gt;print “Gracias”&lt;br /&gt;En otros lenguajes de programación los bloques de código se determinan encerrándolos entre llaves, y el indentarlos no se trata más que de una buena práctica para que sea más sencillo seguir el flujo del programa con un solo golpe de vista. Por ejemplo, el código anterior expresado en Java sería algo así:&lt;br /&gt;String fav = “mundogeek.net”;&lt;br /&gt;if (fav.equals(“mundogeek.net”)){&lt;br /&gt;System.out.println(“Tienes buen gusto!”);&lt;br /&gt;System.out.println(“Gracias”);&lt;br /&gt;}&lt;br /&gt;Sin embargo, como ya hemos comentado, en Python se trata de una obligación, y no de una elección. De esta forma se obliga a los programadores a indentar su código para que sea más sencillo de leer :)&lt;br /&gt;if … else&lt;br /&gt;Vamos a ver ahora un condicional algo más complicado. ¿Qué haríamos si quisiéramos que se ejecutaran unas ciertas órdenes en el caso de que la condición no se cumpliera? Sin duda podríamos añadir otro if que tuviera como condición la negación del primero:&lt;br /&gt;if fav == “mundogeek.net”:&lt;br /&gt;print “Tienes buen gusto!”&lt;br /&gt;print “Gracias”&lt;br /&gt;if fav != “mundogeek.net”:&lt;br /&gt;print “Vaya, que lástima”&lt;br /&gt;pero el condicional tiene una segunda construcción mucho más útil:&lt;br /&gt;if fav == “mundogeek.net”:&lt;br /&gt;print “Tienes buen gusto!”&lt;br /&gt;print “Gracias”&lt;br /&gt;else:&lt;br /&gt;print “Vaya, que lástima”&lt;br /&gt;&lt;br /&gt;Control de flujo&lt;br /&gt;Vemos que la segunda condición se puede sustituir con un else (del inglés: si no, en caso contrario). Si leemos el código vemos que tiene bastante sentido: “si fav es igual a mundogeek.net, imprime esto y esto, si no, imprime esto otro”.&lt;br /&gt;if … elif … elif … else&lt;br /&gt;Todavía queda una construcción más que ver, que es la que hace uso del elif.&lt;br /&gt;if numero &lt;&gt; 0:&lt;br /&gt;print “Positivo”&lt;br /&gt;else:&lt;br /&gt;print “Cero”&lt;br /&gt;elif es una contracción de else if, por lo tanto elif numero &gt; 0 puede leerse como “si no, si numero es mayor que 0”. Es decir, primero se evalúa la condición del if. Si es cierta, se ejecuta su código y se continúa ejecutando el código posterior al condicional; si no se cumple, se evalúa la condición del elif. Si se cumple la condición del elif se ejecuta su código y se continua ejecutando el código posterior al condicional; si no se cumple y hay más de un elif se continúa con el siguiente en orden de aparición. Si no se cumple la condición del if ni de ninguno de los elif, se ejecuta el código del else.&lt;br /&gt;A if C else B&lt;br /&gt;También existe una construcción similar al operador ? de otros lenguajes, que no es más que una forma compacta de expresar un if else. En esta construcción se evalúa el predicado C y se devuelve A si se cumple o B si no se cumple: A if C else B. Veamos un ejemplo:&lt;br /&gt;var = “par” if (num % 2 == 0) else “impar”&lt;br /&gt;Y eso es todo. Si conocéis otros lenguajes de programación puede que esperarais que os hablara ahora del switch, pero en Python no existe esta construcción, que podría emularse con un simple diccionario, así que pasemos directamente a los bucles.&lt;br /&gt;&lt;br /&gt;Bucles&lt;br /&gt;Mientras que los condicionales nos permiten ejecutar distintos fragmentos de código dependiendo de ciertas condiciones, los bucles nos permiten ejecutar un mismo fragmento de código un cierto número de veces, mientras se cumpla una determinada condición.&lt;br /&gt;while&lt;br /&gt;El bucle while (mientras) ejecuta un fragmento de código mientras se cumpla una condición.&lt;br /&gt;edad = 0&lt;br /&gt;while edad &lt; edad =" edad" entrada =" raw_input(“"&gt; “)&lt;br /&gt;if entrada == “adios”:&lt;br /&gt;break&lt;br /&gt;else:&lt;br /&gt;print entrada&lt;br /&gt;Para obtener lo que el usuario escriba en pantalla utilizamos la función raw_input. No es necesario que sepais qué es una función ni cómo funciona exactamente, simplemente aceptad por ahora que en cada iteración del bucle la variable entrada contendrá lo que el usuario escribió hasta pulsar Enter.&lt;br /&gt;Comprobamos entonces si lo que escribió el usuario fue adios, en cuyo caso se ejecuta la orden break o si era cualquier otra cosa, en cuyo caso se imprime en pantalla lo que el usuario escribió.&lt;br /&gt;La palabra clave break (romper) sale del bucle en el que estamos.&lt;br /&gt;Este bucle se podría haber escrito también, no obstante, de la siguiente forma:&lt;br /&gt;salir = False&lt;br /&gt;while not salir:&lt;br /&gt;entrada = raw_input()&lt;br /&gt;if entrada == “adios”:&lt;br /&gt;salir = True&lt;br /&gt;else:&lt;br /&gt;print entrada&lt;br /&gt;pero nos ha servido para ver cómo funciona break.&lt;br /&gt;Otra palabra clave que nos podemos encontrar dentro de los bucles es continue (continuar). Como habréis adivinado no hace otra cosa que pasar directamente a la siguiente iteración del bucle.&lt;br /&gt;edad = 0&lt;br /&gt;while edad &lt; edad =" edad" 2 ="="" secuencia =" [“uno”," i =" 0;" param2 =" 2," param1 =" “hola”)"&gt;&gt;&gt; mi_funcion(“hola”)&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;File “&lt;/span&gt;&lt;stdin&gt;&lt;span style="font-family: times new roman;font-size:130%;" &gt;”, line 1, in &lt;/span&gt;&lt;module&gt;&lt;span style="font-family: times new roman;font-size:130%;" &gt;&lt;br /&gt;TypeError: mi_funcion() takes exactly 2 arguments (1 given)&lt;br /&gt;También es posible, no obstante, definir funciones con un número variable de argumentos, o bien asignar valores por defecto a los parámetros para el caso de que no se indique ningún valor para ese parámetro al llamar a la función.&lt;br /&gt;Los valores por defecto para los parámetros se definen situando un signo igual después del nombre del parámetro y a continuación el valor por defecto:&lt;br /&gt;def imprimir(texto, veces = 1):&lt;br /&gt;print veces * texto&lt;br /&gt;En el ejemplo anterior si no indicamos un valor para el segundo parámetro se imprimirá una sola vez la cadena que le pasamos como primer parámetro:&lt;br /&gt;&gt;&gt;&gt; imprimir(“hola”)&lt;br /&gt;hola&lt;br /&gt;si se le indica otro valor, será este el que se utilice:&lt;br /&gt;&gt;&gt;&gt; imprimir(“hola”, 2)&lt;br /&gt;holahola&lt;br /&gt;Para definir funciones con un número variable de argumentos colocamos un último parámetro para la función cuyo nombre debe precederse de un signo *:&lt;br /&gt;def varios(param1, param2, *otros):&lt;br /&gt;for val in otros:&lt;br /&gt;print val&lt;br /&gt;varios(1, 2)&lt;br /&gt;varios(1, 2, 3)&lt;br /&gt;varios(1, 2, 3, 4)&lt;br /&gt;Esta sintaxis funciona creando una tupla (de nombre otros en el ejemplo) en la que se almacenan los valores de todos los parámetros extra pasados como argumento. Para la primera llamada, varios(1, 2), la tupla otros estaría vacía dado que no se han pasado más parámetros que los dos definidos por defecto, por lo tanto no se imprimiría nada. En la segunda llamada otros valdría (3, ), y en la tercera (3, 4).&lt;br /&gt;También se puede preceder el nombre del último parámetro con **, en cuyo caso en lugar de una tupla se utilizaría un diccionario. Las claves de este diccionario serían los nombres de los parámetros indicados al&lt;br /&gt;Funciones&lt;br /&gt;llamar a la función y los valores del diccionario, los valores asociados a estos parámetros.&lt;br /&gt;En el siguiente ejemplo se utiliza la función items de los diccionarios, que devuelve una lista con sus elementos, para imprimir los parámetros que contiene el diccionario.&lt;br /&gt;def varios(param1, param2, **otros):&lt;br /&gt;for i in otros.items():&lt;br /&gt;print i&lt;br /&gt;varios(1, 2, tercero = 3)&lt;br /&gt;Los que conozcáis algún otro lenguaje de programación os estaréis preguntando si en Python al pasar una variable como argumento de una función estas se pasan por referencia o por valor. En el paso por referencia lo que se pasa como argumento es una referencia o puntero a la variable, es decir, la dirección de memoria en la que se encuentra el contenido de la variable, y no el contenido en si. En el paso por valor, por el contrario, lo que se pasa como argumento es el valor que contenía la variable.&lt;br /&gt;La diferencia entre ambos estriba en que en el paso por valor los cambios que se hagan sobre el parámetro no se ven fuera de la función, dado que los argumentos de la función son variables locales a la función que contienen los valores indicados por las variables que se pasaron como argumento. Es decir, en realidad lo que se le pasa a la función son copias de los valores y no las variables en si.&lt;br /&gt;Si quisiéramos modificar el valor de uno de los argumentos y que estos cambios se reflejaran fuera de la función tendríamos que pasar el parámetro por referencia.&lt;br /&gt;En C los argumentos de las funciones se pasan por valor, aunque se puede simular el paso por referencia usando punteros. En Java también se usa paso por valor, aunque para las variables que son objetos lo que se hace es pasar por valor la referencia al objeto, por lo que en realidad parece paso por referencia.&lt;br /&gt;En Python también se utiliza el paso por valor de referencias a objetos, como en Java, aunque en el caso de Python, a diferencia de Java, todo es un objeto (para ser exactos lo que ocurre en realidad es que al objeto se le asigna otra etiqueta o nombre en el espacio de nombres local de la función).&lt;br /&gt;Sin embargo no todos los cambios que hagamos a los parámetros dentro de una función Python se reflejarán fuera de esta, ya que hay que tener en cuenta que en Python existen objetos inmutables, como las tuplas, por lo que si intentáramos modificar una tupla pasada como parámetro lo que ocurriría en realidad es que se crearía una nueva instancia, por lo que los cambios no se verían fuera de la función.&lt;br /&gt;Veamos un pequeño programa para demostrarlo. En este ejemplo se hace uso del método append de las listas. Un método no es más que una función que pertenece a un objeto, en este caso a una lista; y append, en concreto, sirve para añadir un elemento a una lista.&lt;br /&gt;def f(x, y):&lt;br /&gt;x = x + 3&lt;br /&gt;y.append(23)&lt;br /&gt;print x, y&lt;br /&gt;x = 22&lt;br /&gt;y = [22]&lt;br /&gt;f(x, y)&lt;br /&gt;print x, y&lt;br /&gt;El resultado de la ejecución de este programa sería&lt;br /&gt;25 [22, 23]&lt;br /&gt;22 [22, 23]&lt;br /&gt;Como vemos la variable x no conserva los cambios una vez salimos de la función porque los enteros son inmutables en Python. Sin embargo la variable y si los conserva, porque las listas son mutables.&lt;br /&gt;En resumen: los valores mutables se comportan como paso por referencia, y los inmutables como paso por valor.&lt;br /&gt;Con esto terminamos todo lo relacionado con los parámetros de las funciones. Veamos por último cómo devolver valores, para lo que se utiliza la palabra clave return:&lt;br /&gt;def sumar(x, y):&lt;br /&gt;return x + y&lt;br /&gt;print sumar(3, 2)&lt;br /&gt;Como vemos esta función tan sencilla no hace otra cosa que sumar los valores pasados como parámetro y devolver el resultado como valor de retorno.&lt;br /&gt;También podríamos pasar varios valores que retornar a return.&lt;br /&gt;def f(x, y):&lt;br /&gt;return x * 2, y * 2&lt;br /&gt;a, b = f(1, 2)&lt;br /&gt;Sin embargo esto no quiere decir que las funciones Python puedan devolver varios valores, lo que ocurre en realidad es que Python crea una tupla al vuelo cuyos elementos son los valores a retornar, y esta única variable es la que se devuelve.&lt;br /&gt;Orientación a Objetos&lt;br /&gt;En el capítulo de introducción ya comentábamos que Python es un lenguaje multiparadigma en el se podía trabajar con programación estructurada, como veníamos haciendo hasta ahora, o con programación orientada a objetos o programación funcional.&lt;br /&gt;La Programación Orientada a Objetos (POO u OOP según sus siglas en inglés) es un paradigma de programación en el que los conceptos del mundo real relevantes para nuestro problema se modelan a través de clases y objetos, y en el que nuestro programa consiste en una serie de interacciones entre estos objetos.&lt;br /&gt;Clases y objetos&lt;br /&gt;Para entender este paradigma primero tenemos que comprender qué es una clase y qué es un objeto. Un objeto es una entidad que agrupa un estado y una funcionalidad relacionadas. El estado del objeto se define a través de variables llamadas atributos, mientras que la funcionalidad se modela a través de funciones a las que se les conoce con el nombre de métodos del objeto.&lt;br /&gt;Un ejemplo de objeto podría ser un coche, en el que tendríamos atributos como la marca, el número de puertas o el tipo de carburante y métodos como arrancar y parar. O bien cualquier otra combinación de atributos y métodos según lo que fuera relevante para nuestro programa.&lt;br /&gt;Una clase, por otro lado, no es más que una plantilla genérica a partir de la cuál instanciar los objetos; plantilla que es la que define qué atributos y métodos tendrán los objetos de esa clase.&lt;br /&gt;Volviendo a nuestro ejemplo: en el mundo real existe un conjunto de objetos a los que llamamos coches y que tienen un conjunto de atributos comunes y un comportamiento común, esto es a lo que llamamos clase. Sin embargo, mi coche no es igual que el coche de mi vecino, y aunque pertenecen a la misma clase de objetos, son objetos distintos.&lt;br /&gt;En Python las clases se definen mediante la palabra clave class seguida del nombre de la clase, dos puntos (:) y a continuación, indentado, el cuerpo de la clase. Como en el caso de las funciones, si la primera línea del cuerpo se trata de una cadena de texto, esta será la cadena de documentación de la clase o docstring.&lt;br /&gt;class Coche:&lt;br /&gt;“””Abstraccion de los objetos coche.”””&lt;br /&gt;def __init__(self, gasolina):&lt;br /&gt;self.gasolina = gasolina&lt;br /&gt;print “Tenemos”, gasolina, “litros”&lt;br /&gt;def arrancar(self):&lt;br /&gt;if self.gasolina &gt; 0:&lt;br /&gt;print “Arranca”&lt;br /&gt;else:&lt;br /&gt;print “No arranca”&lt;br /&gt;def conducir(self):&lt;br /&gt;if self.gasolina &gt; 0:&lt;br /&gt;self.gasolina -= 1&lt;br /&gt;print “Quedan”, self.gasolina, “litros”&lt;br /&gt;else:&lt;br /&gt;print “No se mueve”&lt;br /&gt;Lo primero que llama la atención en el ejemplo anterior es el nombre tan curioso que tiene el método __init__. Este nombre es una convención y no un capricho. El método __init__, con una doble barra baja al principio y final del nombre, se ejecuta justo después de crear un nuevo objeto a partir de la clase, proceso que se conoce con el nombre de instanciación. El método __init__ sirve, como sugiere su nombre, para realizar cualquier proceso de inicialización que sea necesario.&lt;br /&gt;Como vemos el primer parámetro de __init__ y del resto de métodos de la clase es siempre self. Esta es una idea inspirada en Modula-3 y sirve para referirse al objeto actual. Este mecanismo es necesario para poder acceder a los atributos y métodos del objeto diferenciando, por ejemplo, una variable local mi_var de un atributo del objeto self.mi_var.&lt;br /&gt;Si volvemos al método __init__ de nuestra clase Coche veremos cómo se utiliza self para asignar al atributo gasolina del objeto (self.gasolina) el valor que el programador especificó para el parámetro gasolina. El parámetro gasolina se destruye al final de la función, mientras que el atributo gasolina se conserva (y puede ser accedido) mientras el objeto viva.&lt;br /&gt;Para crear un objeto se escribiría el nombre de la clase seguido de cualquier parámetro que sea necesario entre paréntesis. Estos parámetros son los que se pasarán al método __init__, que como decíamos es el método que se llama al instanciar la clase.&lt;br /&gt;mi_coche = Coche(3)&lt;br /&gt;Os preguntareis entonces cómo es posible que a la hora de crear nuestro primer objeto pasemos un solo parámetro a __init__, el número 3, cuando la definición de la función indica claramente que precisa de dos parámetros (self y gasolina). Esto es así porque Python pasa el primer argumento (la referencia al objeto que se crea) automágicamente.&lt;br /&gt;Ahora que ya hemos creado nuestro objeto, podemos acceder a sus atributos y métodos mediante la sintaxis objeto.atributo y objeto.metodo():&lt;br /&gt;&gt;&gt;&gt; print mi_coche.gasolina&lt;br /&gt;3&lt;br /&gt;&gt;&gt;&gt; mi_coche.arrancar()&lt;br /&gt;Arranca&lt;br /&gt;&gt;&gt;&gt; mi_coche.conducir()&lt;br /&gt;Quedan 2 litros&lt;br /&gt;&gt;&gt;&gt; mi_coche.conducir()&lt;br /&gt;Quedan 1 litros&lt;br /&gt;&gt;&gt;&gt; mi_coche.conducir()&lt;br /&gt;Quedan 0 litros&lt;br /&gt;&gt;&gt;&gt; mi_coche.conducir()&lt;br /&gt;No se mueve&lt;br /&gt;&gt;&gt;&gt; mi_coche.arrancar()&lt;br /&gt;No arranca&lt;br /&gt;&gt;&gt;&gt; print mi_coche.gasolina&lt;br /&gt;0&lt;br /&gt;Como último apunte recordar que en Python, como ya se comentó en repetidas ocasiones anteriormente, todo son objetos. Las cadenas, por ejemplo, tienen métodos como upper(), que devuelve el texto en mayúsculas o count(sub), que devuelve el número de veces que se encontró la cadena sub en el texto.&lt;br /&gt;Herencia&lt;br /&gt;Hay tres conceptos que son básicos para cualquier lenguaje de programación orientado a objetos: el encapsulamiento, la herencia y el polimorfismo.&lt;br /&gt;En un lenguaje orientado a objetos cuando hacemos que una clase (subclase) herede de otra clase (superclase) estamos haciendo que la subclase contenga todos los atributos y métodos que tenía la superclase. No obstante al acto de heredar de una clase también se le llama a menudo “extender una clase”.&lt;br /&gt;Supongamos que queremos modelar los instrumentos musicales de una banda, tendremos entonces una clase Guitarra, una clase Batería, una clase Bajo, etc. Cada una de estas clases tendrá una serie de atributos y métodos, pero ocurre que, por el mero hecho de ser instrumentos musicales, estas clases compartirán muchos de sus atributos y métodos; un ejemplo sería el método tocar().&lt;br /&gt;Es más sencillo crear un tipo de objeto Instrumento con las atributos y métodos comunes e indicar al programa que Guitarra, Batería y Bajo son tipos de instrumentos, haciendo que hereden de Instrumento.&lt;br /&gt;Para indicar que una clase hereda de otra se coloca el nombre de la clase de la que se hereda entre paréntesis después del nombre de la clase:&lt;br /&gt;class Instrumento:&lt;br /&gt;def __init__(self, precio):&lt;br /&gt;self.precio = precio&lt;br /&gt;def tocar(self):&lt;br /&gt;print “Estamos tocando musica”&lt;br /&gt;def romper(self):&lt;br /&gt;print “Eso lo pagas tu”&lt;br /&gt;print “Son”, self.precio, “$$$”&lt;br /&gt;class Bateria(Instrumento):&lt;br /&gt;pass&lt;br /&gt;class Guitarra(Instrumento):&lt;br /&gt;pass&lt;br /&gt;Como Bateria y Guitarra heredan de Instrumento, ambos tienen un método tocar() y un método romper(), y se inicializan pasando un parámetro precio. Pero, ¿qué ocurriría si quisiéramos especificar un nuevo parámetro tipo_cuerda a la hora de crear un objeto Guitarra? Bastaría con escribir un nuevo método __init__ para la clase Guitarra que se ejecutaría en lugar del __init__ de Instrumento. Esto es lo que se conoce como sobreescribir métodos.&lt;br /&gt;Ahora bien, puede ocurrir en algunos casos que necesitemos sobreescribir un método de la clase padre, pero que en ese método queramos ejecutar el método de la clase padre porque nuestro nuevo método no necesite más que ejecutar un par de nuevas instrucciones extra. En ese caso usaríamos la sintaxis SuperClase.metodo(self, args) para llamar al método de igual nombre de la clase padre. Por ejemplo, para llamar al método __init__ de Instrumento desde Guitarra usaríamos Instrumento.__init__(self, precio)&lt;br /&gt;Observad que en este caso si es necesario especificar el parámetro self.&lt;br /&gt;Herencia múltiple&lt;br /&gt;En Python, a diferencia de otros lenguajes como Java o C#, se permite la herencia múltiple, es decir, una clase puede heredar de varias clases a la vez. Por ejemplo, podríamos tener una clase Cocodrilo que heredara de la clase Terrestre, con métodos como caminar() y atributos como velocidad_caminar y de la clase Acuatico, con métodos como nadar() y atributos como velocidad_nadar. Basta con enumerar las clases de las que se hereda separándolas por comas:&lt;br /&gt;class Cocodrilo(Terrestre, Acuatico):&lt;br /&gt;pass&lt;br /&gt;En el caso de que alguna de las clases padre tuvieran métodos con el mismo nombre y número de parámetros las clases sobreescribirían la implementación de los métodos de las clases más a su derecha en la definición.&lt;br /&gt;En el siguiente ejemplo, como Terrestre se encuentra más a la izquierda, sería la definición de desplazar de esta clase la que prevalecería, y por lo tanto si llamamos al método desplazar de un objeto de tipo Cocodrilo lo que se imprimiría sería “El animal anda”.&lt;br /&gt;class Terrestre:&lt;br /&gt;def desplazar(self):&lt;br /&gt;print “El animal anda”&lt;br /&gt;class Acuatico:&lt;br /&gt;def desplazar(self):&lt;br /&gt;print “El animal nada”&lt;br /&gt;class Cocodrilo(Terrestre, Acuatico):&lt;br /&gt;pass&lt;br /&gt;c = Cocodrilo()&lt;br /&gt;c.desplazar()&lt;br /&gt;Polimorfismo&lt;br /&gt;La palabra polimorfismo, del griego poly morphos (varias formas), se refiere a la habilidad de objetos de distintas clases de responder al mismo mensaje. Esto se puede conseguir a través de la herencia: un objeto de una clase derivada es al mismo tiempo un objeto de la clase padre, de forma que allí donde se requiere un objeto de la clase padre también se puede utilizar uno de la clase hija.&lt;br /&gt;Python, al ser de tipado dinámico, no impone restricciones a los tipos que se le pueden pasar a una función, por ejemplo, más allá de que el objeto se comporte como se espera: si se va a llamar a un método f() del objeto pasado como parámetro, por ejemplo, evidentemente el objeto tendrá que contar con ese método. Por ese motivo, a diferencia de lenguajes de tipado estático como Java o C++, el polimorfismo en Python no es de gran importancia.&lt;br /&gt;En ocasiones también se utiliza el término polimorfismo para referirse a la sobrecarga de métodos, término que se define como la capacidad del lenguaje de determinar qué método ejecutar de entre varios métodos con igual nombre según el tipo o número de los parámetros que se le pasa. En Python no existe sobrecarga de métodos (el último método sobreescribiría la implementación de los anteriores), aunque se puede conseguir un comportamiento similar recurriendo a funciones con valores por defecto para los parámetros o a la sintaxis *params o **params explicada en el capítulo sobre las funciones en Python, o bien usando decoradores (mecanismo que veremos más adelante).&lt;br /&gt;Encapsulación&lt;br /&gt;La encapsulación se refiere a impedir el acceso a determinados métodos y atributos de los objetos estableciendo así qué puede utilizarse desde fuera de la clase.&lt;br /&gt;Esto se consigue en otros lenguajes de programación como Java utilizando modificadores de acceso que definen si cualquiera puede acceder a esa función o variable (public) o si está restringido el acceso a la propia clase (private).&lt;br /&gt;En Python no existen los modificadores de acceso, y lo que se suele hacer es que el acceso a una variable o función viene determinado por su nombre: si el nombre comienza con dos guiones bajos (y no termina también con dos guiones bajos) se trata de una variable o función privada, en caso contrario es pública. Los métodos cuyo nombre comienza y termina con dos guiones bajos son métodos especiales que Python llama automáticamente bajo ciertas circunstancias, como veremos al final del capítulo.&lt;br /&gt;En el siguiente ejemplo sólo se imprimirá la cadena correspondiente al método publico(), mientras que al intentar llamar al método __privado() Python lanzará una excepción quejándose de que no existe (evidentemente existe, pero no lo podemos ver porque es privado).&lt;br /&gt;class Ejemplo:&lt;br /&gt;def publico(self):&lt;br /&gt;print “Publico”&lt;br /&gt;def __privado(self):&lt;br /&gt;print “Privado”&lt;br /&gt;ej = Ejemplo()&lt;br /&gt;ej.publico()&lt;br /&gt;ej.__privado()&lt;br /&gt;Este mecanismo se basa en que los nombres que comienzan con un doble guión bajo se renombran para incluir el nombre de la clase (característica que se conoce con el nombre de name mangling). Esto implica que el método o atributo no es realmente privado, y podemos acceder a él mediante una pequeña trampa:&lt;br /&gt;ej._Ejemplo__privado()&lt;br /&gt;En ocasiones también puede suceder que queramos permitir el acceso a algún atributo de nuestro objeto, pero que este se produzca de forma controlada. Para esto podemos escribir métodos cuyo único cometido sea este, métodos que normalmente, por convención, tienen nombres como getVariable y setVariable; de ahí que se conozcan también con el nombre de getters y setters.&lt;br /&gt;class Fecha():&lt;br /&gt;def __init__(self):&lt;br /&gt;self.__dia = 1&lt;br /&gt;def getDia(self):&lt;br /&gt;return self.__dia&lt;br /&gt;def setDia(self, dia):&lt;br /&gt;if dia &gt; 0 and dia &lt; __dia =" dia" mi_fecha =" Fecha()" __dia =" 1"&gt; 0 and dia &lt; __dia =" dia" dia =" property(getDia," mi_fecha =" Fecha()" dia =" 33"&gt; o &gt;= se lanzará una excepción. Si se utilizan los operadores == o != para comprobar si dos objetos son iguales, se comprueba si son el mismo objeto (si tienen el mismo id).&lt;br /&gt;__len__(self)&lt;br /&gt;Método llamado para comprobar la longitud del objeto. Se utiliza, por ejemplo, cuando se llama a la función len(obj) sobre nuestro objeto. Como es de suponer, el método debe devolver la longitud del objeto.&lt;br /&gt;Existen bastantes más métodos especiales, que permite entre otras cosas utilizar el mecanismo de slicing sobre nuestro objeto, utilizar los operadores aritméticos o usar la sintaxis de diccionarios, pero un estudio exhaustivo de todos los métodos queda fuera del propósito del capítulo.&lt;br /&gt;&lt;br /&gt;Revisitando Objetos&lt;br /&gt;En los capítulos dedicados a los tipos simples y las colecciones veíamos por primera vez algunos de los objetos del lenguaje Python: números, booleanos, cadenas de texto, diccionarios, listas y tuplas.&lt;br /&gt;Ahora que sabemos qué son las clases, los objetos, las funciones, y los métodos es el momento de revisitar estos objetos para descubrir su verdadero potencial.&lt;br /&gt;Veremos a continuación algunos métodos útiles de estos objetos. Evidentemente, no es necesario memorizarlos, pero si, al menos, recordar que existen para cuando sean necesarios.&lt;br /&gt;Diccionarios&lt;br /&gt;D.get(k[, d])&lt;br /&gt;Busca el valor de la clave k en el diccionario. Es equivalente a utilizar D[k] pero al utilizar este método podemos indicar un valor a devolver por defecto si no se encuentra la clave, mientras que con la sintaxis D[k], de no existir la clave se lanzaría una excepción.&lt;br /&gt;D.has_key(k)&lt;br /&gt;Comprueba si el diccionario tiene la clave k. Es equivalente a la sintaxis k in D.&lt;br /&gt;D.items()&lt;br /&gt;Devuelve una lista de tuplas con pares clave-valor.&lt;br /&gt;D.keys()&lt;br /&gt;Devuelve una lista de las claves del diccionario.&lt;br /&gt;D.pop(k[, d])&lt;br /&gt;Borra la clave k del diccionario y devuelve su valor. Si no se encuentra dicha clave se devuelve d si se especificó el parámetro o bien se lanza una excepción.&lt;br /&gt;D.values()&lt;br /&gt;Devuelve una lista de los valores del diccionario.&lt;br /&gt;Cadenas&lt;br /&gt;S.count(sub[, start[, end]])&lt;br /&gt;Devuelve el número de veces que se encuentra sub en la cadena. Los parámetros opcionales start y end definen una subcadena en la que buscar.&lt;br /&gt;S.find(sub[, start[, end]])&lt;br /&gt;Devuelve la posición en la que se encontró por primera vez sub en la cadena o -1 si no se encontró.&lt;br /&gt;S.join(sequence)&lt;br /&gt;Devuelve una cadena resultante de concatenar las cadenas de la secuencia seq separadas por la cadena sobre la que se llama el método.&lt;br /&gt;S.partition(sep)&lt;br /&gt;Busca el separador sep en la cadena y devuelve una tupla con la subcadena hasta dicho separador, el separador en si, y la subcadena del separador hasta el final de la cadena. Si no se encuentra el separador, la tupla contendrá la cadena en si y dos cadenas vacías.&lt;br /&gt;S.replace(old, new[, count])&lt;br /&gt;Devuelve una cadena en la que se han reemplazado todas las ocurrencias de la cadena old por la cadena new. Si se especifica el parámetro count, este indica el número máximo de ocurrencias a reemplazar.&lt;br /&gt;S.split([sep [,maxsplit]])&lt;br /&gt;Devuelve una lista conteniendo las subcadenas en las que se divide nuestra cadena al dividirlas por el delimitador sep. En el caso de que no se especifique sep, se usan espacios. Si se especifica maxsplit, este indica el número máximo de particiones a realizar.&lt;br /&gt;Listas&lt;br /&gt;L.append(object)&lt;br /&gt;Añade un objeto al final de la lista.&lt;br /&gt;L.count(value)&lt;br /&gt;Devuelve el número de veces que se encontró value en la lista.&lt;br /&gt;L.extend(iterable)&lt;br /&gt;Añade los elementos del iterable a la lista.&lt;br /&gt;L.index(value[, start[, stop]])&lt;br /&gt;Devuelve la posición en la que se encontró la primera ocurrencia de value. Si se especifican, start y stop definen las posiciones de inicio y fin de una sublista en la que buscar.&lt;br /&gt;L.insert(index, object)&lt;br /&gt;Inserta el objeto object en la posición index.&lt;br /&gt;L.pop([index])&lt;br /&gt;Devuelve el valor en la posición index y lo elimina de la lista. Si no se especifica la posición, se utiliza el último elemento de la lista.&lt;br /&gt;L.remove(value)&lt;br /&gt;Eliminar la primera ocurrencia de value en la lista.&lt;br /&gt;L.reverse()&lt;br /&gt;Invierte la lista. Esta función trabaja sobre la propia lista desde la que se invoca el método, no sobre una copia.&lt;br /&gt;L.sort(cmp=None, key=None, reverse=False)&lt;br /&gt;Ordena la lista. Si se especifica cmp, este debe ser una función que tome como parámetro dos valores x e y de la lista y devuelva -1 si x es menor que y, 0 si son iguales y 1 si x es mayor que y.&lt;br /&gt;El parámetro reverse es un booleano que indica si se debe ordenar la lista de forma inversa, lo que sería equivalente a llamar primero a L.sort() y después a L.reverse().&lt;br /&gt;Por último, si se especifica, el parámetro key debe ser una función que tome un elemento de la lista y devuelva una clave a utilizar a la hora de comparar, en lugar del elemento en si.&lt;br /&gt;&lt;br /&gt;Programación funcional&lt;br /&gt;La programación funcional es un paradigma en el que la programación se basa casi en su totalidad en funciones, entendiendo el concepto de función según su definición matemática, y no como los simples subprogramas de los lenguajes imperativos que hemos visto hasta ahora.&lt;br /&gt;En los lenguajes funcionales puros un programa consiste exclusivamente en la aplicación de distintas funciones a un valor de entrada para obtener un valor de salida.&lt;br /&gt;Python, sin ser un lenguaje puramente funcional incluye varias características tomadas de los lenguajes funcionales como son las funciones de orden superior o las funciones lambda (funciones anónimas).&lt;br /&gt;Funciones de orden superior&lt;br /&gt;El concepto de funciones de orden superior se refiere al uso de funciones como si de un valor cualquiera se tratara, posibilitando el pasar funciones como parámetros de otras funciones o devolver funciones como valor de retorno.&lt;br /&gt;Esto es posible porque, como hemos insistido ya en varias ocasiones, en Python todo son objetos. Y las funciones no son una excepción.&lt;br /&gt;Veamos un pequeño ejemplo&lt;br /&gt;def saludar(lang):&lt;br /&gt;def saludar_es():&lt;br /&gt;print “Hola”&lt;br /&gt;def saludar_en():&lt;br /&gt;print “Hi”&lt;br /&gt;def saludar_fr():&lt;br /&gt;print “Salut”&lt;br /&gt;lang_func = {“es”: saludar_es,&lt;br /&gt;“en”: saludar_en,&lt;br /&gt;“fr”: saludar_fr}&lt;br /&gt;return lang_func[lang]&lt;br /&gt;f = saludar(“es”)&lt;br /&gt;f()&lt;br /&gt;Como podemos observar lo primero que hacemos en nuestro pequeño programa es llamar a la función saludar con un parámetro “es”. En la función saludar se definen varias funciones: saludar_es, saludar_en y saludar_fr y a continuación se crea un diccionario que tiene como claves cadenas de texto que identifican a cada lenguaje, y como valores las funciones. El valor de retorno de la función es una de estas funciones. La función a devolver viene determinada por el valor del parámetro lang que se pasó como argumento de saludar.&lt;br /&gt;Como el valor de retorno de saludar es una función, como hemos visto, esto quiere decir que f es una variable que contiene una función. Podemos entonces llamar a la función a la que se refiere f de la forma en que llamaríamos a cualquier otra función, añadiendo unos paréntesis y, de forma opcional, una serie de parámetros entre los paréntesis.&lt;br /&gt;Esto se podría acortar, ya que no es necesario almacenar la función que nos pasan como valor de retorno en una variable para poder llamarla:&lt;br /&gt;&gt;&gt;&gt; saludar(“en”)()&lt;br /&gt;Hi&lt;br /&gt;&gt;&gt;&gt; saludar(“fr”)()&lt;br /&gt;Salut&lt;br /&gt;En este caso el primer par de paréntesis indica los parámetros de la función saludar, y el segundo par, los de la función devuelta por saludar.&lt;br /&gt;Iteraciones de orden superior sobre listas&lt;br /&gt;Una de las cosas más interesantes que podemos hacer con nuestras funciones de orden superior es pasarlas como argumentos de las funciones map, filter y reduce. Estas funciones nos permiten sustituir los bucles típicos de los lenguajes imperativos mediante construcciones equivalentes.&lt;br /&gt;map(function, sequence[, sequence, ...])&lt;br /&gt;La función map aplica una función a cada elemento de una secuencia y devuelve una lista con el resultado de aplicar la función a cada elemento. Si se pasan como parámetros n secuencias, la función tendrá que aceptar n argumentos. Si alguna de las secuencias es más pequeña que las demás, el valor que le llega a la función function para posiciones mayores que el tamaño de dicha secuencia será None.&lt;br /&gt;A continuación podemos ver un ejemplo en el que se utiliza map para elevar al cuadrado todos los elementos de una lista:&lt;br /&gt;def cuadrado(n):&lt;br /&gt;return n ** 2&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = map(cuadrado, l)&lt;br /&gt;filter(function, sequence)&lt;br /&gt;La funcion filter verifica que los elementos de una secuencia cumplan una determinada condición, devolviendo una secuencia con los elementos que cumplen esa condición. Es decir, para cada elemento de sequence se aplica la función function; si el resultado es True se añade a la lista y en caso contrario se descarta.&lt;br /&gt;A continuación podemos ver un ejemplo en el que se utiliza filter para conservar solo los números que son pares.&lt;br /&gt;def es_par(n):&lt;br /&gt;return (n % 2.0 == 0)&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = filter(es_par, l)&lt;br /&gt;reduce(function, sequence[, initial])&lt;br /&gt;La función reduce aplica una función a pares de elementos de una secuencia hasta dejarla en un solo valor.&lt;br /&gt;A continuación podemos ver un ejemplo en el que se utiliza reduce para sumar todos los elementos de una lista.&lt;br /&gt;def sumar(x, y):&lt;br /&gt;return x + y&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = reduce(sumar, l)&lt;br /&gt;Funciones lambda&lt;br /&gt;El operador lambda sirve para crear funciones anónimas en línea. Al ser funciones anónimas, es decir, sin nombre, estas no podrán ser referenciadas más tarde.&lt;br /&gt;Las funciones lambda se construyen mediante el operador lambda, los parámetros de la función separados por comas (atención, SIN paréntesis), dos puntos (:) y el código de la función.&lt;br /&gt;Esta construcción podrían haber sido de utilidad en los ejemplos anteriores para reducir código. El programa que utilizamos para explicar filter, por ejemplo, podría expresarse así:&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = filter(lambda n: n % 2.0 == 0, l)&lt;br /&gt;Comparemoslo con la versión anterior:&lt;br /&gt;def es_par(n):&lt;br /&gt;return (n % 2.0 == 0)&lt;br /&gt;l = [1, 2, 3]&lt;br /&gt;l2 = filter(es_par, l)&lt;br /&gt;Las funciones lambda están restringidas por la sintaxis a una sola expresión.&lt;br /&gt;Comprensión de listas&lt;br /&gt;En Python 3000 map, filter y reduce perderán importancia. Y aunque estas funciones se mantendrán, reduce pasará a formar parte del módulo functools, con lo que quedará fuera de las funciones disponibles por defecto, y map y filter se desaconsejarán en favor de las list comprehensions o comprensión de listas.&lt;br /&gt;La comprensión de listas es una característica tomada del lenguaje de programación funcional Haskell que está presente en Python desde la versión 2.0 y consiste en una construcción que permite crear listas a partir de otras listas.&lt;br /&gt;Cada una de estas construcciones consta de una expresión que determina cómo modificar el elemento de la lista original, seguida de una o varias clausulas for y opcionalmente una o varias clausulas if.&lt;br /&gt;Veamos un ejemplo de cómo se podría utilizar la comprensión de listas para elevar al cuadrado todos los elementos de una lista, como hicimos en nuestro ejemplo de map.&lt;br /&gt;l2 = [n ** 2 for n in l]&lt;br /&gt;Esta expresión se leería como “para cada n en l haz n ** 2”. Como vemos tenemos primero la expresión que modifica los valores de la lista original (n ** 2), después el for, el nombre que vamos a utilizar para referirnos al elemento actual de la lista original, el in, y la lista sobre la que se itera.&lt;br /&gt;El ejemplo que utilizamos para la función filter (conservar solo los números que son pares) se podría expresar con comprensión de listas así:&lt;br /&gt;l2 = [n for n in l if n % 2.0 == 0]&lt;br /&gt;Veamos por último un ejemplo de compresión de listas con varias clausulas for:&lt;br /&gt;l = [0, 1, 2, 3]&lt;br /&gt;m = [“a”, “b”]&lt;br /&gt;n = [s * v for s in m&lt;br /&gt;for v in l&lt;br /&gt;if v &gt; 0]&lt;br /&gt;Esta construcción sería equivalente a una serie de for-in anidados:&lt;br /&gt;l = [0, 1, 2, 3]&lt;br /&gt;m = [“a”, “b”]&lt;br /&gt;n = []&lt;br /&gt;for s in m:&lt;br /&gt;for v in l:&lt;br /&gt;if v &gt; 0:&lt;br /&gt;n.append(s* v)&lt;br /&gt;Generadores&lt;br /&gt;Las expresiones generadoras funcionan de forma muy similar a la comprensión de listas. De hecho su sintaxis es exactamente igual, a excepción de que se utilizan paréntesis en lugar de corchetes:&lt;br /&gt;l2 = (n ** 2 for n in l)&lt;br /&gt;Sin embargo las expresiones generadoras se diferencian de la comprensión de listas en que no se devuelve una lista, sino un generador.&lt;br /&gt;&gt;&gt;&gt; l2 = [n ** 2 for n in l]&lt;br /&gt;&gt;&gt;&gt; l2&lt;br /&gt;[0, 1, 4, 9]&lt;br /&gt;&gt;&gt;&gt; l2 = (n ** 2 for n in l)&lt;br /&gt;&gt;&gt;&gt; l2&lt;br /&gt;&lt;/span&gt;&lt;generator object="" at="" 00e33210=""&gt;&lt;span style="font-family: times new roman;font-size:130%;" &gt;&lt;br /&gt;Un generador es una clase especial de función que genera valores sobre los que iterar. Para devolver el siguiente valor sobre el que iterar se utiliza la palabra clave yield en lugar de return. Veamos por ejemplo un generador que devuelva números de n a m con un salto s.&lt;br /&gt;def mi_generador(n, m, s):&lt;br /&gt;while(n &lt;= m): yield n n += s Programación funcional 63 &gt;&gt;&gt; x = mi_generador(0, 5, 1)&lt;br /&gt;&gt;&gt;&gt; x&lt;br /&gt;&lt;/span&gt;&lt;generator object="" at="" 00e25710=""&gt;&lt;span style="font-family: times new roman;font-size:130%;" &gt;&lt;br /&gt;El generador se puede utilizar en cualquier lugar donde se necesite un objeto iterable. Por ejemplo en un for-in:&lt;br /&gt;for n in mi_generador(0, 5, 1):&lt;br /&gt;print n&lt;br /&gt;Como no estamos creando una lista completa en memoria, sino generando un solo valor cada vez que se necesita, en situaciones en las que no sea necesario tener la lista completa el uso de generadores puede suponer una gran diferencia de memoria. En todo caso siempre es posible crear una lista a partir de un generador mediante la función list:&lt;br /&gt;lista = list(mi_generador)&lt;br /&gt;Decoradores&lt;br /&gt;Un decorador no es es mas que una función que recibe una función como parámetro y devuelve otra función como resultado. Por ejemplo podríamos querer añadir la funcionalidad de que se imprimiera el nombre de la función llamada por motivos de depuración:&lt;br /&gt;def mi_decorador(funcion):&lt;br /&gt;def nueva(*args):&lt;br /&gt;print “Llamada a la funcion”, funcion.__name__&lt;br /&gt;retorno = funcion(*args)&lt;br /&gt;return retorno&lt;br /&gt;return nueva&lt;br /&gt;Como vemos el código de la función mi_decorador no hace más que crear una nueva función y devolverla. Esta nueva función imprime el nombre de la función a la que “decoramos”, ejecuta el código de dicha función, y devuelve su valor de retorno. Es decir, que si llamáramos a la nueva función que nos devuelve mi_decorador, el resultado sería el mismo que el de llamar directamente a la función que le pasamos como parámetro, exceptuando el que se imprimiría además el nombre de la función.&lt;br /&gt;Supongamos como ejemplo una función imp que no hace otra cosa que mostrar en pantalla la cadena pasada como parámetro.&lt;br /&gt;&gt;&gt;&gt; imp(“hola”)&lt;br /&gt;hola&lt;br /&gt;&gt;&gt;&gt; mi_decorador(imp)(“hola”)&lt;br /&gt;Llamada a la función imp&lt;br /&gt;hola&lt;br /&gt;La sintaxis para llamar a la función que nos devuelve mi_decorador no es muy clara, aunque si lo estudiamos detenidamente veremos que no tiene mayor complicación. Primero se llama a la función que decor
