Beruflich Dokumente
Kultur Dokumente
Emmanuel Paradis
Institut des Sciences de l’Évolution
Universit Montpellier II
F-34095 Montpellier cdex 05
France
E-mail: paradis@isem.univ-montp2.fr
traducido por
Jorge A. Ahumada
RCUH/ University of Hawaii &
USGS/ National Wildlife Health Center
E-mail: jahumada@usgs.gov
Quiero agradecerle a Julien Claude, Christophe Declercq, Élodie Gazave, Friedrich Leisch
y Mathieu Ros por sus comentarios y sugerencias en versiones anteriores de este documento.
También estoy muy agradecido con los miembros del grupo nuclear de programadores de R por
sus esfuerzos considerables en el desarrollo de R y su ánimo en la lista de discusión ‘rhelp’.
Gracias a todos los usuarios de R cuyas preguntas y comentarios me ayudaron a escribir ’R para
prinicpiantes’.
c 2002, Emmanuel Paradis (3 de marzo de 2003)
1
Índice
1. Prólogo 3
4. Haciendo gráficas en R 29
4.1. Manejo de gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1.1. Abriendo múltiples dispositivos gráficos . . . . . . . . . . . . . . . . . . 29
4.1.2. Disposición de una gráfica . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2. Funciones gráficas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.3. Comandos de graficación de bajo nivel . . . . . . . . . . . . . . . . . . . . . . . 33
4.4. Parámetros gráficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.5. Un ejemplo práctico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.6. Los paquetes grid y lattice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2
1. Prólogo
3
teclado comandos .../library/base/
librerı́a de
- funciones y operadores /ctest/
ratón ... funciones
?
- datos
“datos” objetos archivos
internet
XXX
) 6 XXX
pantalla ? z
X
“resultados” objetos PS JPEG ...
comparar los coeficientes de regresión, R le puede mostrar únicamente los coeficientes estimados:
de esta manera los resultados se pueden resumir en una sola lı́nea, mientras que un programa clási-
co le puede abrir 20 ventanas de resultados. Más adelante, veremos otros ejemplos que ilustran y
comparan la flexibilidad de R con programas de estadı́stica más tradicionales.
Una vez instale R en su computador, el programa se puede iniciar corriendo el archivo ejecu-
table correspondiente. El cursor, que por defecto es el sı́mbolo ‘>’, indica que R está listo para
recibir un comando. En Windows, algunos comandos pueden ser ejecutados a través de los menus
interactivos (por ej. buscar ayuda en lı́nea, abrir archivos, . . . ). En este punto, un nuevo usuario de
R probablemente estará pensando “Y ahora que hago?”. De hecho, cuando se utiliza R por primera
vez, es muy útil tener una idea general de como funciona y eso es precisamente lo que vamos a
hacer ahora. Como primera medida, veremos brevemente como funciona R. Posteriormente, des-
cribiré el operador “asignar” el cual permite crear objetos en R, miraremos como manejar estos
objetos en memoria, y finalmente veremos cómo usar la ayuda en lı́nea, la cual a diferencia de las
ayudas en otros programas estadı́sticos, es bastante útil e intuituva.
4
Orientado a Objetos significa que las variables, datos, funciones, resultados, etc., se guardan
en la memoria activa del computador en forma de objetos con un nombre especı́fico. El usuario
puede modificar o manipular estos objetos con operadores (aritméticos, lógicos, y comparativos)
y funciones (que a su vez son objetos).
El uso y funcionamiento de los operadores es relativamente intuitivo, y veremos los detalles
más adelante (p. 22). Una función en R se puede delinear de la siguiente manera:
argumentos −→ función
↑ =⇒resultado
opciones −→ argumentos por defecto
Los argumentos pueden ser objetos (“datos”, fórmulas, expresiones, . . . ), algunos de los cuales
pueden ser definidos por defecto en la función; sin embargo estos argumentos pueden ser modifi-
cados por el usuario con opciones. Una función en R puede carecer totalmente de argumentos, ya
sea porque todos están definidos por defecto (y sus valores modificados con opciones), o porque
la función realmente no tiene argumentos. Veremos más tarde en detalle como usar y construir
funciones (p. 57). Por ahora esta corta descripción es suficiente para entender el funcionamiento
básico de R.
Todas las acciones en R se realizan con objetos que son guardados en la memoria activa del
ordenador, sin usar archivos temporales (Fig. 1). La lectura y escritura de archivos solo se realiza
para la entrada y salida de datos y resultados (gráficas, . . . ). El usuario ejecuta las funciones con
la ayuda de comandos definidos. Los resultados se pueden visualizar directamente en la pantalla,
guardar en un objeto o escribir directamente en el disco (particularmente para gráficos). Debido
a que los resultados mismos son objetos, pueden ser considerados como datos y analizados como
tal. Archivos que contengan datos pueden ser leidos directamente desde el disco local o en un
servido remoto a través de la red.
Las funciones disponibles están guardadas en una librerı́a localizada en el directorio R HOME/library
(R HOME es el directorio donde R está instalado). Este directorio contiene paquetes de funciones,
las cuales a su vez están estructuradas en directorios. El paquete denominado base constituye el
núcleo de R y contiene las funciones básicas del lenguaje para leer y manipular datos, algunas
funciones gráficas y algunas funciones estadı́sticas (regresión lineal y análisis de varianza). Cada
paquete contiene un directorio denominado R con un archivo con el mismo nombre del paquete
(por ejemplo, para el paquete base, existe el archivo R HOME/library/base/R/base). Este archivo
está en formato ASCII y contiene todas las funciones del paquete.
El comando más simple es escribir el nombre de un objeto para visualizar su contenido. Por
ejemplo, si un objeto n contiene el valor 10:
> n
[1] 10
El dı́gito 1 indica que la visualización del objeto comienza con el primer elementp de n. Este
comando constituye un uso implicito de la función print, y el ejemplo anterior es similar a
print(n) (en algunas situaciones la función print debe ser usada explicitamente, como por
ejemplo dentro de una función o un bucle).
El nombre de un objeto debe comenzar con una letra (A-Z and a-z) y puede incluir letras,
dı́gitos (0-9), y puntos (.). R discrimina entre letras mayúsculas y minúsculas para el nombre de
un objeto, de tal manera que x y X se refiere a objetos diferentes (inclusive bajo Windows).
5
2.2. Creación, listado y remoción de objetos en memoria
Un objeto puede ser creado con el operador “assignar” el cual se denota como una flecha con
el signo menos y el sı́mbolo “>” o “<” dependiendo de la dirección en que asigna el objeto:
> n <- 15
> n
[1] 15
> 5 -> n
> n
[1] 5
> x <- 1
> X <- 10
> x
[1] 1
> X
[1] 10
> n <- 10 + 2
> n
[1] 12
> n <- 3 + rnorm(1)
> n
[1] 2.208807
La función rnorm(1) genera un dato al azar muestrado de una distribución normal con media
0 y varianza 1 (p. 16). Note que se puede escribir una expresión sin asignar su valor a un objeto;
en este caso el resultado será visible en la pantalla pero no será guardado en memoria:
> (10 + 2) * 5
[1] 60
La asignación será omitida de los ejemplos si no es necesaria para la comprensión del mismo.
La función ls simplemente lista los objetos en memoria: sólo se muestran los nombres de los
mismos.
> name <- "Carmen"; n1 <- 10; n2 <- 100; m <- 0.5
> ls()
[1] "m" "n1" "n2" "name"
Note el uso del punto y coma para separar comandos diferentes en la misma lı́nea. Si se
quiere listar solo aquellos objetos que contengan un caracter en particular, se puede usar la opción
pattern (que se puede abreviar como pat):
Para restringir la lista a aquellos objetos que comienzan con este caracter:
6
> ls(pat = "ˆm")
[1] "m"
> ls.str()
m : num 0.5
n1 : num 10
n2 : num 100
name : chr "Carmen"
La opción pattern se puede usar de la misma manera con ls.str(). Otra opción útil en
esta función es max.level la cual especifica el nivel de detalle para la visualización de obje-
tos compuestos. Por defecto, ls.str() muestra todos los detalles de los objetos en memoria,
incluyendo las columnas de los marcos de datos (“data frames”), matrices y listas, lo cual pue-
de generar una gran cantidad de información. Podemos evitar mostrar todos estos detalles con la
opción max.level = -1:
Para borrar objetos en memoria, utilizamos la función rm(): rm(x) elimina el objeto x,
rm(x,y) elimina ambos objetos x y y, y rm(list=ls()) elimina todos los objetos en me-
moria; las mismas opciones mencionadas para la función ls() se pueden usar para borrar selec-
tivamente algunos objetos: rm(list=ls(pat="ˆm")).
> ?lm
mostrará dentro de R, ayuda para la función lm() (modelo lineal). El comando help(lm) o
help("lm") tiene el mismo efecto. Esta última función se debe usar para acceder a la ayuda
con caracteres no-convencionales:
> ?*
Error: syntax error
> help("*")
Arithmetic package:base R Documentation
Arithmetic Operators
...
7
Al llamar la ayuda, se abre una ventana o página (esto depende del sistema operativo) con
información general sobre la función en la primera lı́nea, tal como el nombre del paquete donde
se encuentra la función u operador. Después viene el tı́tulo, seguido de secciones con información
especı́fica acerca de la misma.
Usage: para una función, proporciona el nombre de la misma con todos sus argumentos y los
posibles valores por defecto (opciones); para un operador describe su uso tı́pico.
Arguments: para una función, describe en detalle cada uno de sus argumentos.
Examples: algunos ejemplos que generalmente pueden ser ejecutados sin abrir la ayuda con la
función examples().
Para aquellos que hasta ahora están comenzando en R, es muy útil estudiar la sección Exam-
ples:. También es útil leer cuidadosamente la sección Arguments:. Otras secciones que pueden
estar presentes son Note: (notas adicionales), References: (bibliografı́a que puede ser útil) o Aut-
hor(s): (nombre del autor o autores).
Por defecto, la función help sólo busca en los paquetes que están cargados en memoria. La
opción try.all.packages, que por defecto tiene el valor FALSE (falso), permite buscar en
todos los paquetes disponibles si su valor se cambia a TRUE (verdadero):
> help("bs")
Error in help("bs") : No documentation for ‘bs’ in specified
packages and libraries:
you could try ‘help.search("bs")’
> help("bs", try.all.packages=TRUE)
topic ‘bs’ is not in any loaded package
but can be found in package ‘splines’ in library ‘D:/rw1041/library’
Para ver la ayuda en formato html (por ejemplo a través de Netscape) escriba el comando:
> help.start()
Con esta ayuda en html es posible realizar búsquedas usando palabras clave. La sección See
Also: contiene referencias en hipertexto a otras páginas de ayuda. También se pueden realizar
búsquedas por palabra clave con la función help.search pero esto está aún en estado experi-
mental (versión 1.5.0 de R).
La función apropos encuentra todas aquellas funciones cuyo nombre contiene la palabra
dada como argumento para los paquetes cargados en memoria:
> apropos(help)
[1] "help" "help.search" "help.start"
[4] "link.html.help"
8
3. Manejando Datos con R
3.1. Objetos
Hemos visto que R trabaja con objetos los cuales tienen nombre y contenido, pero también
atributos que especifican el tipo de datos representados por el objeto. Para entender la utilidad
de estos atributos, consideremos una variable que toma los valores 1, 2, o 3: tal variable podrı́a
ser un número entero (por ejemplo, el número de huevos en un nido), o el código de una variable
categórica (por ejemplo, el sexo de los individuos en una población de crustáceos: macho, hembra,
o hermafrodita).
Es claro que los resultados de un análisis estadı́stico de esta variable no será el mismo en
ambos casos: con R, los atributos del objeto proporcionan la información necesaria. En general,
y hablando un poco más técnicamente, la acción de una función sobre un objeto depende de los
atributos de este último.
Todo objeto tiene dos atributos intrı́nsecos: tipo y longitud. El tipo se refiere a la clase básica
de los elementos en el objeto; existen cuatro tipos principales: numérico, caracter, complejo7 , y
lógico (FALSE [Falso] or TRUE [Verdadero]). Existen otros tipos, pero no representan datos como
tal (por ejemplo funciones o expresiones). La longitud es simplemente el número de elementos en
el objeto. Para ver el tipo y la longitud de un objeto se pueden usar las funciones mode y length,
respectivamente:
> x <- 1
> mode(x)
[1] "numeric"
> length(x)
[1] 1
> A <- "Gomphotherium"; compar <- TRUE; z <- 1i
> mode(A); mode(compar); mode(z)
[1] "character"
[1] "logical"
[1] "complex"
Cuando un dato no está disponible se representa como NA (del inglés ’not available’) inde-
pendientemente del tipo del dato. Datos numéricos que son muy grandes se pueden expresar en
notación exponencial:
R representa correctamente valores numéricos no-finitos como ±∞ con Inf y -Inf, o valores
que no son numéricos con NaN (del inglés ’not a number’).
9
> x - x
[1] NaN
Variables que necesitan ser representadas como caracteres se delimitan con comillas ". Es
posible incluir la comilla misma dentro de la variable si está precedida por el sı́mbolo \. Los dos
caracteres juntos \" pueden ser usados por funciones como cat para visualización en pantalla, o
write.table para escritura en archivos (p. 13, véase la opción qmethod de esta función).
> cit <- "Ella dijo: \"Las comillas se pueden incluir en textos en R.\""
> cit
[1] "Ella dijo: \"Las comillas se pueden incluir en textos en R.\""
> cat(cit)
Ella dijo: "Las comillas se pueden incluir en textos en R."
La siguiente tabla resume los tipos de objetos y los datos que representan.
menzar en:” bajo la lengueta “Alias”: este directorio será entonces el nuevo directorio de trabajo cuando R se ejecuta
usando el alias.
10
R puede leer datos guardados como archivos de texto (ASCII) con las siguientes funciones:
read.table (con sus variantes, ver abajo), scan y read.fwf. R también puede leer archivos
en otros formatos (Excel, SAS, SPSS, . . . ), y acceder a bases de datos tipo SQL, pero las funciones
necesarias no están incluidas en el paquete base. Aunque esta funcionalidad es muy útil para el
usuario avanzado, nos restringiremos a describir las funciones para leer archivos en formato ASCII
únicamente.
La función read.table crea un marco de datos (’data frame’) y constituye la manera más
usual de leer datos en forma tabular. Por ejemplo si tenemos un archivo de nombre data.dat, el
comando:
creará un marco de datos denominado misdatos, y cada variable recibirá por defecto el nombre
V1, V2, . . . y puede ser accedida individualmente escribiendo misdatos$V1, misdatos$V2,
. . . , o escribiendo misdatos["V1"], misdatos["V2"], . . . , o, también escribiendo misdatos[,
1], misdatos[,2 ], . . . 9 Existen varias opciones con valores por defecto (aquellos usados por
R si son omitidos por el usuario) que se detallan en la siguiente tabla:
file el nombre del archivo (entre “” o como una variable de tipo caracter), posiblemente con
su dirección si se encuentra en un directorio diferente al de trabajo (el sı́mbolo \no es
permitido y debe reemplazarse con /, inclusive en Windows), o una dirección remota al
archivo tipo URL (http://...)
header una variable lógica (FALSE (falso) o TRUE (verdadero)) indicando si el archivo contie-
ne el nombre de las variables en la primera fila o lı́nea
sep el separador de campo usado en el archivo; por ejemplo sep="\t" si es una tabulación
quote los caracteres usados para citar las variables en modo caracter
dec el caracter usado para representar el punto decimal
row.names un vector con los nombres de las lı́neas de tipo caracter o numérico (por defecto: 1, 2,
3, . . . )
col.names un vector con los nombres de las variables (por defecto: V1, V2, V3, . . . )
as.is controla la conversión de variables tipo caracter a factores (si es FALSE) o las mantiene
como caracteres (TRUE); as.is puede ser un vector lógico o numérico que especifique
las variables que se deben mantener como caracteres
na.strings el valor con el que se codifican datos ausentes (convertido a NA)
colClasses un vector de caracteres que proporciona clases para las columnas
nrows el número máximo de lı́neas a leer (se ignoran valores negativos)
skip el número de lı́neas ignoradas antes de leer los datos
check.names si es TRUE, chequea que el nombre de las variables sea válido para R
fill si es TRUE y todas las filas no tienen el mismo número de variables, agrega “blancos”
strip.white (condicional a sep) si es TRUE, borra espacios extra antes y despues de variables tipo
caracter
blank.lines.skip si es TRUE, ignora lı́neas en “blanco”
comment.char un caracter que define comentarios en el archivo de datos; lı́neas que comienzen
con este caracter son ignoradas en la lectura (para desactivar este argumento utilize
comment.char =“”)
9 Existe una diferencia: misdatos$V1 y misdatos[, 1] son vectores mientras que misdatos["V1"] es un
marco de datos. Mas adelante veremos algunos detalles acerca de la manipulación de objetos (p. 17)
11
Las variantes de read.table son útiles ya que vienen con diferentes opciones por defecto:
La función scan es más flexible que read.table. A diferencia de esta última es posible
especificar el modo de las variables:
En este ejemplo scan lee tres variables del archivo data.dat; el primero es un caracter y los
siguientes dos son numéricos. Otra distinción importante es la capacidad de scan() de crear
diferentes objetos como vectores, matrices, marcos de datos, listas, . . . En el ejemplo anterior,
misdatos es una lista de tres vectores. Por defecto, es decir si se omite el argumento what,
scan() crea un vector numérico. Si los datos leidos no corresponden al modo (o modos) esperado
(s) (ya sea por defecto o especificado a través de what), se genera un mensaje de error. Las
opciones son las siguientes:
12
file el nombre del archivo(entre “”), posiblemente incluyendo la dirección completa (el
sı́mbolo \no es permitido y debe ser reemplazado por /, inclusive bajo Windows), o
acceso remoto del tipoURL (http://...); si file=“”, los datos deben ser introducidos
desde el teclado (la entrada se termina con una lı́nea en blanco)
what especifica el tipo (s) de los datos (numérico por defecto)
nmax el número máximo de datos a ser leido, o si what es una lista, el número de lı́neas por
leer (por defecto, scan lee los datos hasta que encuentra el final del archivo)
n el número de datos por leer (por defecto no hay limite)
sep el separador de campos usado en el archivo
quote los caracteres usados para citar las variables de tipo caracter
dec el caracter usado para el punto decimal
skip el número de lı́neas ignorado antes de empezar a leer datos
nlines el número de lı́neas a leer
na.string el valor asignado a datos ausentes (convertido a NA)
flush si es TRUE, scan va a la siguiente lı́nea una vez se han leido todas las columnas (el
usuario puede agregar comentarios en el archivo de datos)
fill agrega “blancos” si es TRUE y todas las lı́neas no tienen el mismo número de variables
strip.white (condicional a sep) si es TRUE, elimina espacios extras antes y despues de variables
de tipo caracter
quiet si es FALSE, scan muestra una lı́nea indicando los campos que han sido leidos
blank.lines.skip si es TRUE, ignora lı́neas en blanco
multi.line si what es una lista, especifica si las variables de un mismo individuo están en una sola
lı́nea en el archivo (FALSE)
comment.char un caracter que define comentarios en el archivo; aquellas lı́neas que comiencen con
este caracter son ignoradas
La función read.fwf puede usarse para leer datos en archivos en formato fijo ancho:
read.fwf(file, widths, sep="\t", as.is = FALSE,
skip = 0, row.names, col.names, n = -1)
Las opciones son las mismas que para read.table() con excepción A1.501.2
de widths que especifica la anchura de los campos. Por ejemplo, si un A1.551.3
archivo de nombre datos.txt tiene los datos mostrados a la derecha, esto B1.601.4
se puede leer con el comando: B1.651.5
C1.701.6
C1.751.7
> misdatos <- read.fwf("datos.txt", widths=c(1, 4, 3))
> misdatos
V1 V2 V3
1 A 1.50 1.2
2 A 1.55 1.3
3 B 1.60 1.4
4 B 1.65 1.5
5 C 1.70 1.6
6 C 1.75 1.7
13
x el nombre del objeto a exportar
file el nombre del archivo (por defecto, el objeto se muestra en la pantalla)
append si es TRUE anexa los datos al archivo sin borrar datos ya existentes en el mismo
quote lógico o numérico : si es TRUE variables de tipo caracter y factores se escriben entre ; si
es un vector numérico, este indica el número de las variables a ser es mostradas entre (en
ambos casos los nombres de las variables se escriben entre pero no si quote = FALSE)
sep el separador de campo utilizado en el archivo
eol el caracter que indica el final de lı́nea ("\n" es ’retorno’)
na el caracter a usarse para datos faltantes
dec el caracter usado para el punto decimal
row.names una opción lógica que indica si los nombres de las lı́neas se escriben en el archivo
col.names identificación para los nombres de las columnas
qmethod si es quote=TRUE, especifica la manera como se debe tratar las comillas dobles ”en va-
riables tipo caracter: si es ”escape”(o ”e”, por defecto) cada ”es reemplazada por \; si es
”dçada ”es reemplazada por
Una secuencia regular de números enteros, por ejemplo de 1 hasta 30, se puede generar con:
El vector resultante x tiene 30 elementos. El operador ‘:’ tiene prioridad sobre otros operadores
aritméticos en una expresión:
> 1:10-1
[1] 0 1 2 3 4 5 6 7 8 9
> 1:(10-1)
[1] 1 2 3 4 5 6 7 8 9
14
> seq(length=9, from=1, to=5)
[1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
También se pueden escribir los valores directamente usando la función c:
> c(1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5)
[1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
Si se quiere, también es posible introducir datos directamente desde el teclado usando la fun-
ción scan sin opciones:
> z <- scan()
1: 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
10:
Read 9 items
> z
[1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
La función rep crea un vector con elementos idénticos:
> rep(1, 30)
[1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
La función sequence crea una serie de secuencias de números enteros donde cada secuencia
termina en el número (o números) especificado (s) como argumento (s):
> sequence(4:5)
[1] 1 2 3 4 1 2 3 4 5
> sequence(c(10,5))
[1] 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5
La función gl (generador de niveles) es muy útil porque genera series regulares de factores.
La función tiene la forma gl(k, n) donde k es el número de niveles (o clases), y n es el número
de réplicas en cada nivel. Se pueden usar dos opciones: length para especificar el número de
datos producidos, y labels para especificar los nombres de los factores. Ejemplos:
> gl(3, 5)
[1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3
Levels: 1 2 3
> gl(3, 5, length=30)
[1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3
Levels: 1 2 3
> gl(2, 6, label=c("Macho", "Hembra"))
[1] Macho Macho Macho Macho Macho Macho
[7] Hembra Hembra Hembra Hembra Hembra Hembra
Levels: Macho Hembra
> gl(2, 10)
[1] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2
Levels: 1 2
> gl(2, 1, length=20)
[1] 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2
Levels: 1 2
> gl(2, 2, length=20)
[1] 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 2
Levels: 1 2
15
Finalmente, expand.grid() crea un marco de datos con todas las combinaciones de vec-
tores o factores proporcionados como argumentos:
Distribución/función función
Gausse (normal) rnorm(n, mean=0, sd=1)
exponencial rexp(n, rate=1)
gamma rgamma(n, shape, scale=1)
Poisson rpois(n, lambda)
Weibull rweibull(n, shape, scale=1)
Cauchy rcauchy(n, location=0, scale=1)
beta rbeta(n, shape1, shape2)
‘Student’ (t) rt(n, df)
Fisher–Snedecor (F) rf(n, df1, df2)
Pearson (χ2 ) rchisq(n, df)
binomial rbinom(n, size, prob)
geométrica rgeom(n, prob)
hypergeométrica rhyper(nn, m, n, k)
logı́stica rlogis(n, location=0, scale=1)
lognormal rlnorm(n, meanlog=0, sdlog=1)
binomial negativa rnbinom(n, size, prob)
uniforme runif(n, min=0, max=1)
Estadı́stico de Wilcoxon’s rwilcox(nn, m, n), rsignrank(nn, n)
16
3.5. Manipulación de objetos
3.5.1. Creación de objetos
En las secciones anteriores vimos diferentes maneras de crear objetos usando el operador
de asignación; el tipo y clase de los objetos ası́ creados se determina generalmente de manera
implı́cita. Es posible, sin embargo, generar un objeto especificando su clase, tipo, longitud, etc.
Esta aproximación es interesante desde el punto de vista de la manipulación de objetos. Por ejem-
plo, se puede crear un objeto ‘vacı́o’ y modificar de manera sucesiva sus elementos; esto puede
ser más eficiente que colocar todos los elementos juntos usando c(). El sistema de indexado se
puede usar en estas circunstancias, como veremos más adelante (p. 23).
También puede ser bastante conveniente crear nuevos objetos a partir de objetos ya existentes.
Por ejemplo, si se quiere ajustar una serie de modelos, es fácil colocar las fórmulas en una lista, y
despues extraer sucesivamente los elementos para insertarlos en una función lm.
En esta etapa de nuestro aprendizaje de R, la utilidad de aprender las siguientes funciones
no es solo práctica sino didáctica. La construcción explı́cita de un objeto nos proporciona un
mejor entendimiento de su estructura, y nos permite ahondar en algunas nociones mencionadas
previamente.
Vector. La función vector, que tiene dos argumentos mode y length, crea un vector cuyos
elementos pueden ser de tipo numérico, lógico o caracter dependiendo del argumento espe-
cificado en mode (0, FALSE o “ ” respectivamente). Las siguientes funciones tienen exac-
tamente el mismo efecto y tienen un solo argumento (la longitud del vector): numeric(),
logical(), y character().
Factor. Un factor incluye no solo los valores correspondientes a una variable categórica, pero
también los diferentes niveles posibles de esta variable (inclusive si están presentes en los
datos). La función factor crea un factor con las siguientes opciones:
levels especifica los posibles niveles del factor (por defecto los valores únicos de x),
labels define los nombres de los niveles, exclude especifica los valores x que se deben
excluir de los niveles, y ordered es un argumento lógico que especifica si los niveles del
factor están ordenados. Recuerde que x es de tipo numérico o caracter. Ejemplos:
> factor(1:3)
[1] 1 2 3
Levels: 1 2 3
> factor(1:3, levels=1:5)
[1] 1 2 3
Levels: 1 2 3 4 5
> factor(1:3, labels=c("A", "B", "C"))
[1] A B C
Levels: A B C
> factor(1:5, exclude=4)
[1] 1 2 3 NA 5
Levels: 1 2 3 5
17
> ff <- factor(c(2, 4), levels=2:5)
> ff
[1] 2 4
Levels: 2 3 4 5
> levels(ff)
[1] "2" "3" "4" "5"
Matriz. Una matriz es realmente un vector con un atributo adicional (dim) el cual a su vez es un
vector numérico de longitud 2, que define el número de filas y columnas de la matriz. Una
matriz se puede crear con la función matrix:
La opción byrow indica si los valores en data deben llenar las columnas sucesivamente
(por defecto) o las filas (if TRUE). La opción dimnames permite asignar nombres a las
filas y columnas.
Otra manera de crear una matriz es dando los valores apropiados al atributo dim (que ini-
cialmente tiene valor NULL):
18
Marco de datos. Hemos visto que un marco de datos (’data.frame’) se crea de manera implı́cita
con la función read.table; también es posible hacerlo con la función data.frame.
Los vectores incluidos como argumentos deben ser de la misma longitud, o si uno de ellos
es más corto que los otros, es “reciclado” un cierto número de veces:
> x <- 1:4; n <- 10; M <- c(10, 35); y <- 2:4
> data.frame(x, n)
x n
1 1 10
2 2 10
3 3 10
4 4 10
> data.frame(x, M)
x M
1 1 10
2 2 35
3 3 10
4 4 35
> data.frame(x, y)
Error in data.frame(x, y) :
arguments imply differing number of rows: 4, 3
Si se incluye un factor en un marco de datos, el factor debe ser de la misma longitud que el
vector (o vectores). Es posible cambiar el nombre de las columnas con data.frame(A1=x,
A2=n). También se pueden dar nombres a las filas con la opción row.names que debe
ser, por supuesto, un vector de modo caracter con longitud igual al número de lı́neas en el
marco de datos. Finalmente, note que los marcos de datos tienen un atributo similar al dim
de las matrices.
Lista. Una lista se crea de manera similar a un marco de datos con la función list. No existe nin-
guna limitación en el tipo de objetos que se pueden incluir. A diferencia de data.frame(),
los nombres de los objetos no se toman por defecto; tomando los vectores x y y del ejemplo
anterior:
[[2]]
[1] 2 3 4
> L2
$A
[1] 1 2 3 4
$B
[1] 2 3 4
> names(L1)
19
NULL
> names(L2)
[1] "A" "B"
Series de tiempo. La función ts crea un objeto de clase "ts" (serie de tiempo) a partir de un
vector (serie de tiempo única) o una matriz (serie multivariada). Las opciones que caracte-
rizan un objeto de este tipo son:
20
Feb 1961 6 6 9
Mar 1961 2 3 3
Apr 1961 8 5 4
May 1961 4 9 3
Jun 1961 4 6 13
Jul 1961 4 2 6
Aug 1961 11 6 4
Sep 1961 6 5 7
Oct 1961 6 5 7
Nov 1961 5 5 7
Dec 1961 8 5 2
Expresión. Los objetos de clase expresión juegan un papel fundamental en R. Una expresión es
una serie de caracteres que hace sentido para R. Todos los comandos válidos son expresio-
nes. Cuando se escribe un comando directamente en el teclado, este es evaluado por R y
ejecutado si es válido. En muchos casos, es útil construir una expresión sin evaluarla: es-
to es lo que la función expression hace. Por supuesto, es posible evaluar la expresión
posteriormente con eval().
Las expresiones se pueden usar, entre otras cosas, para incluir ecuaciones en gráficos (p. 34).
Una expresión se puede crear desde una variable de tipo caracter. Algunas funciones utilizan
expresiones como argumentos; por ejemplo D() que calcula derivadas parciales:
Para el lector debe ser obvio que las diferencias entre algunos tipos de objetos son pequeñas;
por lo tanto debe ser relativamente fácil convertir el tipo de un objeto cambiando algunos de sus
atributos. Tal conversión se puede realizar usando una función as.algo. R (versión 1.5.1) tiene
en su paquete base, 77 funciones de este tipo, asi que no ahondaremos mucho más en este tema.
El resultado de esta conversión depende obviamente de los atributos del objeto convertido.
Generalmente, las conversiones siguen reglas muy intuitivas. La siguiente tabla resume la situación
para la conversión de diferentes tipos.
21
Conversión a Función Reglas
numérico as.numeric FALSE → 0
TRUE → 1
"1", "2", . . . → 1, 2, . . .
”A”, . . . → NA
lógico as.logical 0 → FALSE
otros números → TRUE
"FALSE", "F" → FALSE
"TRUE", "T" → TRUE
otros caracteres → NA
caracter as.character 1, 2, . . . → "1", "2", . . .
FALSE → "FALSE"
TRUE → "TRUE"
Existen funciones que convierten entre diferentes clases de objetos (as.matrix, as.data.frame,
as.ts, as.expression, . . . ). Estas funciones pueden afectar atributos diferentes al tipo du-
rante la conversión. De nuevo, los resultados de la conversión son generalmente intuitivos. Una
situación frecuente es la conversión de factores a valores numéricos. En este caso, R realiza la
conversión usando la codificación numérica de los niveles del factor (no los valores literales del
factor):
Para realizar la conversión manteniendo los valores literales del factor, primero se debe con-
vertir a caracter y despues a numérico.
> as.numeric(as.character(fac))
[1] 1 10
Este procedimiento puede ser bastante útil si en un archivo una variable numérica también
tiene valores no-numéricos. Vimos anteriormente que en esta situación read.table() leerá la
columna como un factor por defecto.
3.5.3. Operadores
Previamente mencionamos que existen tres tipos de operadores en R10 . Esta es la lista.
22
Operadores
Aritméticos Comparativos Lógicos
+ adición < menor que ! x NO lógico
- substracción > mayor que x & y Y lógico
* multiplicación <= menor o igual que x && y id.
/ división >= mayor o igual que x | y O lógico
ˆ potencia == igual x || y id.
%% módulo != diferente de xor(x, y) O exclusivo
%/ % división de enteros
23
> x(1)
Error: couldn’t find function "x"
La indexación se puede usar para suprimir una o mas filas o columnas. Por ejemplo, x[-1,
] suprime la primera fila, y x[-c(1, 15), ] hará lo mismo con la primera y la quinceava
filas.
Para vectores, matrices y otros arreglos, es posible acceder los valores de un elemento usando
como ı́ndice una expresión comparativa:
Un uso práctico de la indexación lógica es por ejemplo, la posibilidad de seleccionar los núme-
ros pares de un vector de enteros:
Por lo tanto, el sistema de indexación utiliza los valores lógicos devueltos por los operadores
comparativos. Estos valores se pueden calcular con anterioridad y pueden ser reciclados si es
necesario:
24
3.5.5. Accediendo a los valores de un objeto con nombres
Hemos visto en múltiples ocasiones el concepto de nombres. Los nombres son atributos, y
existen diferentes tipos (names, colnames, rownames, dimnames). Nos limitaremos por ahora a
algunas nociones muy simples sobre los nombres, particularmente lo que concierne a su utilización
para acceder a elementos de un objeto.
Si los elementos de un objeto tienen nombres, se pueden extraer usándolos como ı́ndices.
Al realizar la extracción de esta manera los atributos del objeto original se mantienen intactos.
Por ejemplo, si un marco de datos DF contiene las variables x, y, y z, el comando DF["x"]
extraerá un marco de datos que solamente contendrá x; DF[c(‘‘x’’, ‘‘y’’)] extraerá un
marco de datos con ambas variables. Esto funciona con listas si los elementos en la misma tienen
nombres.
Como el lector se habrá dado cuenta, el ı́ndice utilizado aqui es un vector de modo caracter.
Al igual que los vectores numéricos o lógicos descritos previamente, este vector se puede definir
previamente y ser utilziado después para la extracción.
Para extraer un vector o un factor de un marco de datos se puede usar el sı́mbolo $ (e.g.,
DF$x). Este procedimiento también es válido para listas.
Es posible utilziar un editor gráfico similar a una hoja de cálculo para editar un objeto numéri-
co. Por ejemplo, si X es una matriz, el comando data.entry(X) abrirá un editor gráfico que le
permitirá cambiar los valores en la matriz o adcionar nuevas columnas y/o filas.
La función data.entry modifica directamente el objeto dado como argumento sin nece-
sidad de asignar su resultado. Por el contrario, la función de devuelve una lista con los objetos
dados como argumentos y posiblemente modificados. Este resultado es mostrado en pantalla por
defecto, pero como en muchas otras funciones, puede ser asignado a otro objeto.
Los detalles del uso del editor de datos dependen del sistema operativo (no está aún imple-
mentado en todas las plataformas).
Existen muchas funciones en R para manipular datos. Hemos visto la más sencila, c que
concatena los objetos listados entre paréntesis. Por ejemplo:
Se pueden adicionar vectores con diferentes longitudes; en este caso el vector más corto se
recicla. Ejemplos:
25
> z
[1] 2 4 4 6
> x <- 1:3
> y <- 1:2
> z <- x + y
Warning message:
longer object length
is not a multiple of shorter object length in: x + y
> z
[1] 2 4 4
El número de funciones disponibles en R es demasiado grande para ser listado en este docu-
mento. Se pueden encontrar todas las funciones matemáticas simples (log, exp, log10, log2,
sin, cos, tan, asin, acos, atan, abs, sqrt, . . . ), funciones especiales (gamma, digamma,
beta, besselI, . . . ), ası́ como diversas funciones ut́iles en estadı́stica. Algunas de estas funcio-
nes se detallan en la siguiente tabla.
Estas funciones devuelven un solo valor (o un vector de longitud 1), a excepción de range()
que retorna un vector de longitud 2, y var(), cov(), y cor() que pueden devolver matrices.
Las siguientes funciones pueden devolver vectores más complejos:
26
round(x, n) redondea los elementos de x a n cifras decimales
rev(x) invierte el orden de los elementos en x
sort(x) ordena los elementos de x en orden ascendente; para hacerlo en orden descendente:
rev(sort(x))
rank(x) alinea los elementos de x
log(x, base) calcula el logaritmo de x en base "base"
scale(x) si x es una matriz, centra y reduce los datos; si se desea centrar solamente uti-
lizar scale=FALSE, para reducir solamente usar center=FALSE (por defecto
center=TRUE, scale=TRUE)
pmin(x,y,...) un vector en el que el iavo elemento es el mı́nimo de x[i], y[i], . . .
pmax(x,y,...) igual que el anterior pero para el máximo
cumsum(x) un vector en el que el iavo elemento es la suma desde x[1] a x[i]
cumprod(x) igual que el anterior pero para el producto
cummin(x) igual que el anterior pero para el mı́nimo
cummax(x) igual que el anterior pero para el máximo
match(x, y) devuelve un vector de la misma longitud que x con los elementos de x que están en y (NA
si no)
which(x == a) devuelve un vector de los ı́ndices de x si la operación es (TRUE) (en este ejemplo, los valores
de i para los cuales x[i] == a). El argumento de esta función debe ser una variable de
tipo lógico
choose(n, k) calcula el número de combinaciones de k eventos en n repeticiones = n!/[(n − k)!k!]
na.omit(x) elimina las observaciones con datos ausentes (NA) (elimina la fila correspondiente si x es
una matriz o un marco de datos)
na.fail(x) devuelve un mensaje de error si x contiene por lo menos un NA
unique(x) si x es un vector o un marco de datos, devuelve un objeto similar pero suprimiendo elemen-
tos duplicados
table(x) devuelve una tabla con el número de diferentes valores de x (tı́picamente para enteros o
factores)
subset(x, ...) devuelve una selección de x con respecto al criterio (..., tı́picamente comparaciones:
x$V1 <10); si x es un marco de datos, la opción select proporciona las variables que
se mantienen (o se ignoran con -)
sample(x, size) remuestrea al azar y sin reemplazo size elementos en el vector x; la opción replace =
TRUE permite remuestrear con reemplazo
R posee facilidades para manipular y hacer operaciones con matrices. Las funciones rbind()
y cbind() unen matrices con respecto a sus filas o columnas respectivamente:
27
El operador para el producto de dos matrices es ‘ %* %’. Por ejemplo, considerando las dos
matrices m1 y m2:
La transposición de una matriz se realiza con la función t; esta función también funciona con
marcos de datos.
La función diag se puede usar para extraer o modificar la diagonal de una matriz o para
construir una matriz diagonal:
> diag(m1)
[1] 1 1
> diag(rbind(m1, m2) %*% cbind(m1, m2))
[1] 2 2 8 8
> diag(m1) <- 10
> m1
[,1] [,2]
[1,] 10 1
[2,] 1 10
> diag(3)
[,1] [,2] [,3]
[1,] 1 0 0
[2,] 0 1 0
[3,] 0 0 1
> v <- c(10, 20, 30)
> diag(v)
[,1] [,2] [,3]
[1,] 10 0 0
[2,] 0 20 0
[3,] 0 0 30
> diag(2.1, nr = 3, nc = 5)
[,1] [,2] [,3] [,4] [,5]
[1,] 2.1 0.0 0.0 0 0
[2,] 0.0 2.1 0.0 0 0
[3,] 0.0 0.0 2.1 0 0
R también posee algunas fucniones para cálculos con matrices. Mencionamos aqui solve()
para invertir una matriz, qr() para descomposición, eigen() para calcular valores y vectores
propios, y svd() para descomposición singular.
28
4. Haciendo gráficas en R
R ofrece una increible variedad de gráficos. Para darse una idea, escriba el comando demo(graphics).
No nos es posible detallar aqui todas las posibilidades de R en términos de generación de gráficas.
Cada función gráfica en R tiene un enorme número de opciones permitiendo una gran flexibilidad
en la producción de gráficos y el uso de cualquier otro paquete gráfico palidece en comparación.
El modus operandi de las funciones gráficas es sustancialmente diferente del esquema esboza-
do al principio de este documento. Particularmente, el resultado de una función gráfica no puede
ser asignado a un objeto11 sino que es enviado a un dispositivo gráfico. Un dispositivo gráfico es
una ventana gráfica o un archivo.
Existen dos tipos de funciones gráficas: las funciones de graficación de alto nivel que crean
una nueva gráfica y las funciones de graficación de bajo nivel que agregan elementos a una gráfica
ya existente. Las gráficas se producen con respecto a parámetros gráficos que están definidos por
defecto y pueden ser modificados con la función par.
Primero veremos como manejar gráficos y dispositivos gráficos; después veremos en detalle
algunas funciones gráficas y sus parámetros. Veremos ejemplos prácticos del uso de estas fun-
cionalidades en la producción de gráficos. Finalmente, veremos los paquetes grid y lattice cuyo
funcionamiento es diferente a las funciones gráficas ‘normales’ de R.
Al ejecutarse una función gráfica, R abre una ventana para mostrar el gráfico si no hay ningún
dispositivo abierto. Un dispostivo gráfico se puede abrir con la función apropiada. El tipo de dis-
positivos gráficos disponibles depende del sistema operativo. Las ventanas gráficas se llaman X11
bajo Unix/Linux, windows bajo Windows y macintosh bajo Mac. En Unix/Linux y Windows
se puede abrir una nueva ventana gráfica con el comando x11() ya que en Windows existe un
alias apuntando a windows(). Dispositivos gráficos que son archivos se pueden abrir con una
función que depende del tipo de archivo que se quiere crear: postscript(), pdf(), png(),
. . . La lista de dispositivos gráficos disponibles se obtiene con el comando ?device.
El último dispositivo en ser abierto, se convierte en el dispositivo activo sobre el cual se di-
bujan (o escriben) las gráficas generadas. La función dev.list() muestra una lista con los
dispositivos abiertos:
Los números corresponden al número del dispositivo respectivo. Este se puede usar para cam-
biar el dispositivo activo. Para saber cual es el dispositivo activo:
> dev.cur()
pdf
4
trices.
29
> dev.set(3)
X11
3
La función dev.off() cierra el dispositivo; por defecto se cierra el dispositivo activo, de
lo contrario el correspondiente al número pasado en la función. R muestra el número del nuevo
dispositivo activo:
> dev.off(2)
X11
3
> dev.off()
pdf
4
divide el dispositivo en dos partes que se pueden seleccionar con screen(1) o screen(2);
erase.screen() borra la última gráfica dibujada. Una parte de un dispositivo se puede dividir
a su vez en partes más pequeñas con split.screen() permitiendo la posibilidad de configu-
raciones complejas.
Estas funciones son incompatibles con otras similares (como layout() o coplot()) y
no se deben usar con multiples dispositivos gráficos. Su uso se debe limitar por ejemplo, a la
exploración gráfica de datos.
La función layout divide el dispositivo activo en varias partes donde se colocarán las gráficas
de manera sucesiva. Esta función tiene como argumento principal una matriz con números enteros
indicando el número de las sub-ventanas. Por ejemplo, para dividir el dispositivo en cuatro partes
iguales:
Tambien es posible crear esta matriz previamente permitiendo una mejor visualización de la
manera como se va a dividir el dispositivo:
Para visualizar la partición creada, se puede usar la función layout.show con el número de
sub-ventanas como argumento. En este ejemplo tenemos:
30
1 3
>layout.show(4)
2 4
Los siguientes ejemplos demuestran algunas de las posibilidades ofrecidas por layout().
1 4
>layout(matrix(1:6, 3, 2))
>layout.show(6) 2 5
3 6
>layout(matrix(1:6, 2, 3)) 1 3 5
>layout.show(6)
2 4 6
>layout(m) 3
>layout.show(3) 2
En ninguno de estos ejemplos hemos usado la opción byrow de matrix() (leer por fi-
las), y por lo tanto las sub-ventanas se numeran a lo largo de las columnas; se puede especificar
matrix(..., byrow=TRUE) para numerar las sub-ventanas a lo largo de las filas. Los núme-
ros en la matriz se pueden dar en cualquier orden, por ejemplo matrix(c(2, 1, 4, 3), 2,
2).
Por defecto, layout() divide el dispositivo en dimensiones regulares: esto se puede mo-
dificar con las opciones widths y heights. Estas dimensiones se dan de manera relativa12 .
Ejemplos:
31
>m <- matrix(1:4, 2, 2)
1 3
>layout(m, widths=c(1, 3),
heights=c(3, 1))
2 4
>layout.show(4)
2
>m <- matrix(c(1,1,2,1),2,2)
>layout(m, widths=c(2, 1), 1
heights=c(1, 2))
>layout.show(2)
Finalmente, los números en la matriz pueden ser ceros dando la posibilidad de realizar arreglos
complejos (o inclusive esotéricos).
2
>m <- matrix(0:3, 2, 2)
>layout(m, c(1, 3), c(1, 3))
1 3
>layout.show(3)
32
fourfoldplot(x) utilizando cuartos de cı́rculos, visualiza la asociación entre dos variables dicotómicas
para diferentes poblaciones (x debe ser un arreglo de dim=c(2, 2, k), o una matriz
de dim=c(2, 2) si k = 1)
assocplot(x) Gráfica ‘amigable’ de Cohen mostrando desviaciones de independencia de filas y co-
lumnas en una tabla de contingencia de dos dimensiones
mosaicplot(x) gráfico ‘mosaico’ de los residuales de una regresión log-lineal de una tabla de contin-
gencia
pairs(x) si x es una matriz o un marco de datos, dibuja todas las posibles gráficas bivariadas
entre las columnas de x
plot.ts(x) si x es un objeto de clase "ts", grafica x con respecto al tiempo. x puede ser multiva-
riada pero las series deben tener la misma frecuencia y fechas
ts.plot(x) igual a la anterior pero si x es multivariado, las series pueden tener diferentes fechas
pero la misma frecuencia
hist(x) histograma de las frecuencias de x
barplot(x) histograma de los valores de x
qqnorm(x) cuartiles de x con respecto a lo esperado bajo una distribución normal
qqplot(x, y) cuartiles de y con respecto a los cuartiles de x
contour(x, y, gráfico de contornos (los datos son interpolados para dibujar las curvas), x y y deben
z) ser vectores, z debe ser una matriz tal que dim(z)=c(length(x), length(y))
(x y y pueden ser omitidos)
filled.contour igual al anterior, pero las áreas entre contornos están coloreadas, y se dibuja una leyenda
(x, y, z) de colores
image(x, y, z) igual al anterior pero con colores (se grafican los datos crudos)
persp(x, y, z) igual al anterior pero en perspectiva (se grafican los datos crudos)
stars(x) si x es una matriz o un marco de datos, dibuja una gráfica con segmentos o una estrella,
donde cada fila de x es representada por una estrella, y las columnas son las longitudes
de los segmentos
symbols(x, y, dibuja, en las coordenadas dadas por x y y, sı́mbolos (cı́rculos, cuadrados, rectángu-
...) los, estrellas, termómetros o cajas) cuyos tamaños, colores . . . son especificados con
argumentos adicionales
termplot(mod.obj) gráfico de los efectos (parciales) de un modelo de regresión (mod.obj)
Las opciones y argumentos para cada una de estas opciones se pueden encontrar en la ayuda
incorporada en R. Algunas de estas opciones son idénticas para varias funciones gráficas; éstas
son las principales (con sus valores por defecto):
add=FALSE si es TRUE superpone el gráfico en el ya existente (si existe)
axes=TRUE si es FALSE no dibuja los ejes ni la caja del gráfico
type="p" especifica el tipo de gráfico; "p": puntos, "l": lı́neas, "b": puntos co-
nectados por lı́neas, .o": igual al anterior, pero las lı́neas están sobre los
puntos, "h": lı́neas verticales, "s": escaleras, los datos se representan
como la parte superior de las lı́neas verticales, "S": igual al anterior pe-
ro los datos se representan como la parte inferior de las lı́neas verticales
xlim=, ylim= especifica los lı́mites inferiores y superiores de los ejes; por ejemplo con
xlim=c(1, 10) o xlim=range(x)
xlab=, ylab= tı’tulos en los ejes; deben ser variables de tipo caracter
main= tı́tulo principal; debe ser de tipo caracter
sub= sub-tı́tulo (escrito en una letra más pequeña)
33
points(x, y) agrega puntos (se puede usar la opción type=)
lines(x, y) igual a la anterior pero con lı́neas
text(x, y, agrega texto dado por labels en las coordenadas (x,y); un uso tı́pico: plot(x, y,
labels, ...) type="n"); text(x, y, names)
mtext(text, agrega texto dado por text en el margen especificado por side (ver axis() más
side=3, line=0, abajo); line especifica la linea del área de graficado
...)
segments(x0, dibuja una lı́nea desde el punto (x0,y0) hasta el punto (x1,y1)
y0, x1, y1)
arrows(x0, y0, igual al anterior pero con flechas desde (x0,y0) si code=2, al punto (x1,y1) si
x1, y1, angle= code=1, o en ambos si code=3; angle controla el ángulo desde la base de la flecha
30, code=2) hasta la punta de la misma
abline(a,b) dibuja una lı́nea con pendiente b e intercepto a
abline(h=y) dibuja una lı́nea horizontal en la ordenada y
abline(v=x) dibuja una lı́nea vertical en la abcisa x
abline(lm.obj) dibuja la lı́nea de regresión dada por lm.obj (ver sección 5)
rect(x1, y1, dibuja un rectángulo donde las esquinas izquierda, derecha, superior e inferior están
x2, y2) dadas por x1, x2, y1, y y2, respectivamente
polygon(x, y) dibuja un polı́gono uniendo los puntos dados por x y y
legend(x, y, agrega la leyenda en el punto (x,y) con sı́mbolos dados por legend
legend)
title() agrega un tı́tulo y opcionalmente un sub-tı́tulo
axis(side, agrega un eje en la parte inferior (side=1), izquierda (2), superior (3), o derecha (4);
vect) vect (opcional) da la abscisa (u ordenada) donde se deben dibujar los marcadores
(‘tick marks’) del eje
rug(x) dibuja los datos x en el eje x como pequeñas lı́neas verticales
locator(n, devuelve las coordenadas (x, y) después que el usuario a hecho click n veces en el gráfico
type="n", ...) con el ratón; también dibuja sı́mbolos (type="p") o lı́neas (type="l") con respecto
a parámetros gráficos opcionales (...); por defecto no se dibuja nada (type="n")
identify(x, similar a locator() con la diferencia que imprime en la gráfica el valor de x (u
...) opcionalmente de una leyenda especificada en la opción labels=) más cercano al
punto donde se hizo click. Util para identificar puntos en la gráfica que están asociados
con nombres.
1
p=
1 + e−(βx+α)
Para incluir una variable en una expresión, se pueden utilizar las funciones substitute y
as.expression; por ejemplo para incluir el valor de R2 (calculado anteriomente y guardado
en un objeto Rsquared):
R2 = 0,9856298
34
Para ver solo tres cifras decimales, podemos modificar el código de la siguiente manera:
R2 = 0,986
R2 = 0,986
> par(bg="yellow")
dará como resultado que todos los gráficos subsecuentes tendrán el fondo de color amarillo. Exis-
ten 68 parámetros gráficos y algunos tienen funciones muy similares. La lista completa de paráme-
tros gráficos se puede ver con ?par; en la siguiente tabla ilustramos solo los más usados.
35
adj controla la justificación del texto (0 justificado a la izquierda, 0.5 centrado, 1 justificado a la derecha)
bg especifica el color del fondo (ej. : bg=r̈ed", bg=b̈lue", . . . La lista de los 657 colores disponibles se
puede ver con colors())
bty controla el tipo de caja que se dibuja alrededor del gráfico: ö", "l", "7", c̈", ü" o "]" (la caja se
parece a su respectivo caracater); si bty="n" no se dibuja la caja
cex un valor que controla el tamaño del texto y sı́mbolos con respecto al valor por defecto; los siguientes
parámetros tienen el mismo control para números en los ejes, cex.axis, tı́tulos en los ejes, cex.lab,
el tı́tulo principal, cex.main, y el subtı́tulo, cex.sub
col controla el color de los sı́mbolos; como en cex estos son: col.axis, col.lab, col.main y
col.sub
font un entero que conrola el estilo del texto (1: normal, 2: cursiva, 3: negrilla, 4: negrilla cursiva); como
en cex existen: font.axis, font.lab, font.main y font.sub
las un entero que controla la orientación de los caracteres en los ejes (0: paralelo a los ejes, 1: horizontal,
2: perpendicular a los ejes, 3: vertical)
lty un entero o caracter que controla el tipo de las lı́neas; (1: sólida, 2: quebrada, 3: punteada, 4: punto-
lı́nea, 5: lı́nea larga-corta, 6: dos lı́neas cortas), o una secuencia de hasta 8 caracteres (entre "0" y
"9") que especifica alternativamente la longitud en puntos o pixeles, de los elementos dibujados y los
blancos; por ejemplo lty="44" tendrá el mismo efecto que lty=2
lwd un número que controla la anchura de las lı́neas
mar un vector con 4 valores numéricos que controla el espacio entre los ejes y el borde de la gráfica en la for-
ma c(inferior, izquierda, superior, derecha); los valores por defecto son c(5.1,
4.1, 4.1, 2.1)
mfcol un vector del tipo c(nr,nc) que divide la ventana gráfica como una matriz con nr filas y nc colum-
nas; las gráficas se dibujan sucesivamente en las columnas (véase la sección 4.1.2)
mfrow igual al anterior, pero las gráficas se dibujan en las filas (ver sección 4.1.2)
pch controla el tipo de sı́mbolo, ya sea un entero entre 1 y 25, o un caracter entre ¨ " (Fig. 2)
ps un entero que controla el tamaño (en puntos) de textos y sı́mbolos
pty un caracter que especifica el tipo de región a graficar, "s": cuadrada, "m": máxima
tck un valor que especifica la longitud de los marcadores de eje como una fracción de la altura o anchura
máxima del gráfico; si tck=1 se dibuja una rejilla
tcl un valor que especifica la longitud de los marcadores de eje como una fracción de la altura de una lı́nea
de texto (por defecto tcl=-0.5)
xaxt si xaxt="n" el eje x se coloca pero no se muesttra (util en conjunción con axis(side=1, ...))
yaxt if yaxt="n" el eje y se coloca pero no se muesttra (util en conjunción con axis(side=2, ...))
La gráfica que queremos visualizar se puede obtener con plot(); simplemente se escribe el
comando:
> plot(x, y)
y la gráfica será visible en el dispositivo gráfico activo. El resultado se puede ver en la Fig. 3. Por
defecto, R dibuja gráficas de una manera “inteligente”: los espacios entre los marcadores de los
ejes, la ubicación de las etiquetas en los ejes, etc, son calculados automáticamente de tal manera
que la gráfica resultante sea lo mas legible posible.
Sin embargo, el usuario puede cambiar la manera como se presenta la gráfica, por ejemplo,
para ajustarse a un estilo editorial pre-definido o para darle un toque personal para una charla. La
36
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
* ? X a
Figura 2: Los sı́mbolos gráficos en R (pch=1:25). Los colores se obtuvieron con las opciones
col="blue", bg=" yellow"; la segunda opción tiene efecto solo sobre los sı́mbolos 21–
25. Se puede usar cualquier caracter (pch="*", "?", ".", . . . ).
0.5
0.0
y
−0.5
−1.0
37
Cómo personalizar un gráfico en R
−1
−2
−2 −1 0 1 2
manera más simple de cambiar un gráfico es a través de la adición de opciones que permiten modi-
ficar los argumentos dados por defecto. En nuestro ejemplo, podemos modificar significativamente
la figura de la siguiente manera:
El resultado se ve en la Fig. 4. Miremos con detalle cada una de las opciones utilizadas. Prime-
ro, xlab y ylab cambian los tı́tulos de los ejes, que por defecto son los nombres de las variables.
Ahora, xlim y ylim nos permiten definir los lı́mites en ambos ejes13 . El parámetro gráfico pch
es utilizado como una opción: pch=22 especifica un cuadrado con contorno y fondo de diferentes
colores dados respectivamente por col and bg. La tabla de parámetros gráficos explica el signi-
ficado de las modificaciones hechas por bty, tcl, las and cex. Finalmente, adicionamos un
tı́tulo con main.
Los parámetros gráficos y las funciones de graficación de bajo nivel nos permiten modificar
aún más la presentación de un gráfico. Como vimos anteriormente, algunos parámetros gráficos
no se pueden pasar como argumentos en funciones como plot. Modificaremos algunos de estos
parámetros con par(), y por ende es necesario escribir varios comandos. Cuando se cambian
los parámetros gráficos es útil guardar sus valores iniciales previamente para poder restaurarlos
posteriormente. Aqui están los comandos utilzados para obtener la Fig. 5.
13 Por defecto, R agrega 4 % a cada lado del lı́mite del eje. Este comportamiento se puede alterar modificando los
parámetros gráficos xaxs="i" y yaxs="i" (se pueden pasar como opciones a plot()).
38
Cómo personalizar un gráfico en R (bis)
2
−1
−2
−2 −1 0 1 2
Miremos con detalle las acciones resultantes de estos comandos. Primero, los parámetros gráfi-
cos por defecto se copian en una lista llamada opar. Modificaremos tres parámetros: bg el color
del fondo, col.axis el color de los números en los ejes, y mar los tamaños de los márgenes en
los bordes del gráfico. La gráfica se dibuja de una manera muy similar a como se hizo la Fig. 4.
las modificaciones de los márgenes permiten utilizar el espacio alrededor del área de graficado.
El tı́tulo se añade con la función de bajo nivel title lo que permite agregar algunos parámetros
como argumentos sin alterar el resto de la gráfica. Finalmente, los parámetros gráficos iniciales se
restauran con el último comando.
Ahora, control total! En la Fig. 5, R todavı́a determina algunas cosas tales como el número
de marcadores en los ejes, o el espacio entre el tı́tulo y el área de graficado. Veremos ahora como
controlar totalmente la presentación de la gráfica. La estrategia que vamos a usar aqui es dibujar
primero una gráfica en blanco con plot(..., type="n"), y despues agregar puntos, ejes,
etiquetas, etc., con funciones de graficación de bajo nivel. Incluiremos algunas novedades como
cambiar el color del área de graficado. Los comandos se encuentran a continuación y la gráfica
resultante se puede ver en la Fig. 6.
39
Otros diez números Cómo personalizar un gráfico en R (ter)
−1
−2 0 2
Diez números al azar
Como se hizo anteriormente los parámetros gráficos por defecto se guardan y el color del
fondo y las márgenes se modifican. La gráfica se dibuja con type="n" para no colocar los
puntos, xlab=“”,ylab=“” para no escribir tı́tulos en los ejes, y xaxt="n", yaxt="n" para
no dibujar los ejes. Esto resulta en que se dibuja solamente la caja alrededor del área de graficado
y se definen los ejes con respecto a xlim y ylim. Note que hubiéramos podido usar la opción
axes=FALSE pero en ese caso, ni los ejes ni la caja hubieran sido dibujados.
Los elementos se adicionan en la región de la gráfica con la ayuda de las funciones de bajo
nivel. Antes de agregar los puntos el color dentro del área de graficado se cambia con rect(): el
tamaño del rectángulo se escoge de tal manera que es substanciamente más grande que el área de
graficado.
Los puntos se dibujan con points() utilizando un nuevo sı́mbolo. Los ejes se agregan
con axis(): el vector especificado como segundo argumento determina las coordenadas de los
marcadores de los ejes. La opción labels=FALSE especifica que no se deben escribir anota-
ciones con los marcadores. Esta opción también acepta un vector de tipo caracter, por ejemplo
labels=c(‘‘A’’, ‘‘B’’, ‘‘C’’).
El tı́tulo se adiciona con title(), pero el tipo de letra se cambia ligeramente. Las anotacio-
nes en los ejes se escriben con mtext() (texto marginal). El primer argumento de esta función es
un vector tipo caracter que proporciona el texto a ser escrito. La opción line indica la distancia
al área de graficado (por defecto line=0), y at la coordenada. La segunda llamada a mtext()
utiliza el valor por defecto de side (3). Las otras dos llamadas a mtext() pasan un vector
numérico como el primer argumento: esto será convertido a un caracter.
40
Las gráficas producidas por grid o lattice no se pueden combinar o mezclar con aquellas
producidas por las funciones vistas anteriormente, porque estos paquetes usan un novedoso modo
gráfico15 . Este nuevo modo tiene su propio sistema de parámetros gráficos muy distinto a lo que
hemos visto hasta ahora. Sin embargo, es posible usar ambos modos gráficos en la misma sesión
y en el mismo dispositivo gráfico.
Desde un punto de vista práctico, grid contiene todas las funciones necesarias para el modo
gráfico, mientras que lattice contiene las funciones más comúnmente usadas.
La mayor parte de las funciones en lattice toman una fórmula como argumento principal; por
ejemplo, y ˜x16 . La fórmula y ˜x | z significa que la gráfica de y con respecto a x será di-
bujada como diferentes gráficas con respecto a los valores de z.
La siguiente tabla resume las funciones principales en lattice. La fórmula proporcionada como
argumento es la tı́picamente utilizada, pero todas estas funciones aceptan fórmulas condicionales
(y ˜x | z) como argumento principal; como se verá en los ejemplos más abajo, en este último
caso se crea una gráfica múltiple con respecto a los valores de z.
Algunas funciones en lattice tienen el mismo nombre que algunas de las funciones gráficas en
el paquete base. Estas últimas se “esconden” cuando lattice es cargado en memoria.
Veamos algunos ejemplos para ilustrar algunos aspectos de lattice. Primero, el paquete debe
ser cargado en memoria con el comando library(lattice) para poder acceder sus funcio-
nes.
Empecemos con los gráficos de funciones de densidad. Estos gráficos se pueden hacer simple-
mente con el comando densityplot(˜x) resultando en una curva con la función de densidad
empı́rica donde los puntos corresponden a las observaciones en el eje x (similar a rug()). Nues-
tro ejemplo será ligeramente más complicado con la superposición en cada gráfico de las curvas
de densidad empı́ricas con las curvas esperadas bajo una distribución normal. Es necesario usar el
argumento panel que define lo que se dibujará en cada gráfica. Los comandos son:
41
−4 −2 0 2 4
n = 35 n = 40 n = 45
0.8
0.6
0.4
0.2
0
n = 20 n = 25 n = 30
0.8
0.6
Density
0.4
0.2
0
n=5 n = 10 n = 15
0.8
0.6
0.4
0.2
0
−4 −2 0 2 4 −4 −2 0 2 4
x
Las primeras tres lı́neas generan una muestra aleatoria tomada de una distribución normal,
la cual es submuestras de tamaño 5, 10, 15, . . . , y 45. Después viene la llamada a la función
densityplot() lo que produce una gráfica para cada sub-muestra. panel toma una fucnión
como argumento. En este ejemplo, hemos definido una función que llama a otras dos funciones
predefinidas en lattice: panel.densityplot que dibuja la función de densidad empı́rica, y
panel.mathdensity que dibuja la función de densidad teórica asumiendo una distribución
normal. La función panel.densityplot es llamada por defecto si no se especifica ningún
argumento en panel: el comando densityplot(˜x | y) hubiera dado como resultado la
misma Fig. 7 pero sin las curvas azules.
Los siguientes ejemplos utilizan algunas de las conjuntos de datos disponibles en R: la loca-
lización de 1000 eventos sı́smicos cerca de las islas Fiji, y algunas medidas florales tomadas para
tres especies de iris.
Fig. 8 muestra la localización geográfica de los eventos sı́smicos con respecto a su profundi-
dad. Los comandos necesarios para crear esta gráfica son:
data(quakes)
mini <- min(quakes$depth)
maxi <- max(quakes$depth)
int <- ceiling((maxi - mini)/9)
inf <- seq(mini, maxi, int)
quakes$depth.cat <- factor(floor(((quakes$depth - mini) / int)),
labels=paste(inf, inf + int, sep="-"))
42
−40 −30 −20 −10
472−544 544−616 616−688
185
180
175
170
165
256−328 328−400 400−472
185
long
180
175
170
165
40−112 112−184 184−256
185
180
175
170
165
−40 −30 −20 −10 −40 −30 −20 −10
lat
El primer comando carga los datos de quakes en memoria. Los siguientes 5 comandos crean
un factor dividiendo la profundidad (variable depth) en 9 intervalos equivalentes (con el mismo
rango): los niveles del factor son enumerados con el lı́mite superior e inferior de estos intervalos.
Una vez definidas las variables, se llama a la función xyplot con su fórmula apropiada y un
argumento data indicando donde se deben buscar las variables17 .
Con los datos en iris, la sobreposición entre diferentes especies es lo suficientemente baja y
permite diferenciar claramente entre ellas en la figura (Fig. 9). Los comandos son:
data(iris)
xyplot(
Petal.Length ˜ Petal.Width, data = iris, groups=Species,
panel = panel.superpose,
type = c("p", "smooth"), span=.75,
key = list(x=0.15, y=0.85,
points=list(col=trellis.par.get()[["superpose.symbol"]]$col[1:3],
pch = 1),
text = list(levels(iris$Species)))
)
La llamada a la función xyplot es un poco más compleja aqui que en los ejemplos ante-
riores, y utiliza varias opciones que veremos en detalle. La opción groups, como su nombre lo
sugiere, define grupos que serán usados por otras opciones. Vimos anteriormente la opción panel
que define como se respresentarán los diferentes grupos en la gráfica: aqui usamos una función
predefinida panel.superpose para superponer los grupos en la misma gráfica. No pasamos
opciones a panel.superpose, asi que se usarán los colores por defecto para distinguir los
grupos. La opción type, como en plot(), especifica como representar los datos, pero aqui se
pueden especificar varios argumentos en un vector: "p" para dibujar puntos y "smooth" para
dibujar una curva que se ajuste a los datos con un grado de flexibilidad especificado por span.
La opción key agrega una leyenda a la gráfica; la sintaxis es un poco complicada aqui, pero esto
17 plot() no puede tomar argumentos tipo data; la localización de las variables se debe especificar explı́citamente.
43
7 o
o o
o
o
o
o o o
6 setosa o o
o o
versicolor o o o
o o o
virginica o o
o
o o
o
o
o o
o o
o o
o o o o o o o
5 o
o
o
o
o o
o
o o
o o o o
o o o
o o o o
Petal.Length
o o o
o
o o o
o o
4 o o
o o
o
o
o
o
o
o
o
3 o
2 o o
o o o o
o o o
o o o o
o o o
o o o
o
o
1 o
será simplificado en versiones futuras de lattice a algo similar a la función legend utilizada en
las gráficas estándar de R. key toma una lista como argumento: x y y indican la localización de
la leyenda (si las coordenadas son omitidas la leyenda se coloca fuera de la región de dibujo);
points especifica el tipo de sı́mbolo, el cual es extraido de las definiciones por defecto (por eso
la expresión ligeramente complicada); y text proporciona el texto de la leyenda, el cual es por
supuesto, el nombre de la especie.
Veremos ahora la función splom con los mismos datos en iris. Los siguientes comandos
se usaron para producir la Fig. 10:
splom(
˜iris[1:4], groups = Species, data = iris, xlab = "",
panel = panel.superpose,
key = list(columns = 3,
points = list(col=trellis.par.get()[["superpose.symbol"]]$col[1:3],
pch = 1),
text = list(c("Setosa", "Versicolor", "Virginica")))
)
Esta vez, el argumento principal es una matriz (las primeras cuatro columnas de iris). El
resultado es un conjunto de todos los posibles gráficos bi-variados entre las columnas de la matriz,
como en la función estándar pairs. Por defecto, splom agrega el texto “Scatter Plot Matrix”
bajo el eje x: para evitar esto, usamos la opción xlab=“”. Las otras opciones son similares a las
del ejemplo anterior con excepción de columns = 3 en key que se especifica ası́, para que la
leyenda se presente en tres columnas.
La Fig. 10 se pudo haber hecho con pairs(), pero esta última no puede hacer gráficas
condicionales como la de la Fig. 11. El código es relativamente simple:
Ya que las sub-gráficas son relativamente pequeñas, agregamos dos opciones para mejorar la
legibilidad de la figura: pscales = 0 omite los marcadores en los ejes (todas las sub-gráficas
se dibujan en la misma escala), y los nombres de las variables se re-definieron para mostrarlos en
dos lı́neas ("\n" codifica un caracter de retorno a nueva lı́nea).
44
Setosa Versicolor Virginica
o oo o o o o o o ooo 2.5
ooooo o
o oo ooo o
o o o
ooooo
ooooo oo 1.5 2 2.5
ooo oo oooo oo o
o o
oooo o oooooo ooo 2
oo
oooooooo ooo
oo o
o
oo
o oo o oo
oo
o
oooooooo
o o o o o ooo
ooo
ooooo
o oooooo
o o
oooo ooo
ooooooooo
o o oooooo
ooo
ooo
o o ooo
oooo
oo
ooooo o 1.5
ooo
oo oooooo o ooo
oo
ooo
o oo ooooooo Petal.Width
ooo o ooooo oooo
o ooo oooooo
oooooo 1
oo ooo ooo o ooo 0.5
oo o oo
oo oo o o o oo o ooo
oooo
oo
ooooo
o
oo
oooo
o oooo o oo
oo
oo
oooooo
ooo ooo ooooo
oo
oooo 0 0.5 1
0
o
o ooo o 7 oooo
ooooooo ooo oo o o o 4 5 6 7 o o
ooooo oo oo o oo o
ooo
ooo
ooo
6 o oooo o
o o
oo
o
o
o
o
o
oo
ooo o oo oooo
oo o o
oo oo
oooooo
ooooo o o
oooo o oo o
oo
oo
5 oooooo
o ooo oo
o oooo
o
oo oo
ooo oo oooo
oo
oo
o
oo
oooo ooo
oo
ooooooo
o
oo
o
oo oooooo oo oooo 4 Petal.Length 4 o
ooo o
o
oo oo
oo o ooooo o oo o
o
3
oooo o
ooo o
oo oo 2 o o
ooooooooo o ooo oo oo o 1 2 3 4 oo
oo
oooo
ooo
oooooooo oo o o oo
o o ooo oo 1 oo
ooo
o 4.5 o o
o
o o 3.5 4 4.5 oo oo
oo 4 oo o oo
ooo oo oooo oo ooo oo
o oooo
oo o o 3.5 o o
oo
o
oo
oo o o
oo
oo
o
o o
ooo ooo oooo
oo oooooooo ooo oo oooo oooo
o o
o o oo ooo
ooooooo ooo Sepal.Width ooo
ooo oooo
oooo oooooo o oo ooo
o ooo oo
oo ooooooo
oo o
oo
oo
oooo oo
ooo oo o 3 ooooooo o oo oo
oooo
ooooooo
oooo
o ooooo o
oo
o
oo oo
ooo
o
oooo
o
o o
o oo o
oo
oo
o ooo ooo
oo oooo o o
oooo oooo oo
ooo o o o o o ooo
oooo oo
o o ooooo o
oo o oooo oo o o 2.5 oooooooo o oo o oo
oo o o oooo o
o oo o o ooo o o o o oo o o o
o
oo
o
o 2 2.5 3 2 o o
8 o
7 8 ooo o o ooooo o o
oooo
ooo oo oo o o
7 ooo
o
oo
o o oooooo oo o oo
o oooo
o
oo
o
o
o oo
o oooooo oo
ooooo
o oo
oo oo o
oo
o o o
o o
ooo
ooo
o
oo oooo
oo
o oo o
oo o o o o
o o o oo ooo
Sepal.Length6 o ooo
oo
ooo
ooo oo o ooooo
o
ooo
ooooo
oo
oooo
oo o o
o
o
oo oo o
o
ooo
ooooo ooo
o
o
oo
o o ooo ooo
ooo o ooo ooo o oo oo o
ooo
o
oo oo
oooo
o
o o oo
oo o o oo ooo
o oo
oooooooo
oo o oooo
o o
oooo o o
oo o oooo oooo oo oo
oo
o o
5 o ooo oo
ooo o
oo ooo
oo o
oo
o
oooo
oo
o
o
oo
oo
oo o o
oo
oo
o
oo
o
oooo o
oo o
5 6 o ooo
ooo oo oo
oo
o
oo
oo oo ooo
virginica
Petal
Length
Sepal
Width
Sepal
Length
setosa versicolor
Petal Petal
Length Length
Sepal Sepal
Width Width
Sepal Sepal
Length Length
45
Min Max
setosa versicolor virginica
Petal.Width
Petal.Length
Sepal.Width
Sepal.Length
Min Max Min Max
Tal como con los gráficos, es imposible ahondar en detalles acerca de todas las posibilidades
ofrecidas por R para realizar análisis estadı́sticos. Mi meta en esta sección es proporcionar una
visión muy general para que el lector se lleve una idea de las caracterı́sticas de R para realizar
análisis de todo tipo.
Con expeción de las funciones en los paquetes grid y lattice, todas la funciones que hemos vis-
to hasta ahora están localizadas en el paquete base. Algunas de las funciones para análisis de datos
están en base pero la gran mayorı́a de los métodos estadı́sticos disponibles en R están distribui-
dos como paquetes packages. Algunos de estos paquetes vienen instalados junto con base, otros
están dentro del grupo recommended ya que usan métodos comunmente utilizados en estadśtica,
y finalmente mucho otros paquetes están dentro del grupo contributed y debe ser instalados por el
usuario.
Para introducir el método general de realizar análisis de datos en R, empezaremos con un
ejemplo simple que requiere solamente el paquete base . Posteriomente explicaremos algunos
conceptos como fórmulas y funciones genéricas, que son útiles independientemente del tipo de
análisis realizado. Concluiremos con un mirada rápida a los diferentes paquetes.
46
contingencia como argumento en vez de una fórmula18 . Para ver como hacer análisis de varianza
tomemos unos datos que vienen incluidos con R: InsectSprays (insecticidas). Se probaron en
el campo 6 diferentes tipos de insecticidas utilizando el número de insectos como la variable de
respuesta. Cada insecticida se probó 12 veces, para un total de 72 observaciones. No haremos aqui
una exploración gráfica de los datos, sino que nos enfocaremos en un análisis de varianza simple
de la variable de respuesta como función del insecticida usado. Después de cargar los datos en
memoria con la función data, el análisis se realiza con la función aov (después de transformar
la respuesta):
> data(InsectSprays)
> aov.spray <- aov(sqrt(count) ˜ spray, data = InsectSprays)
> aov.spray
Call:
aov(formula = sqrt(count) ˜ spray, data = InsectSprays)
Terms:
spray Residuals
Sum of Squares 88.43787 26.05798
Deg. of Freedom 5 66
Recordemos que escribir el nombre del objeto como un comando, es similar al comando
print(aov.spray). Para ver una representación gráfica de los resultados podemos usar plot()
o termplot(). Antes de escribir plot(aov.spray) dividiremos la ventana gráfica en cua-
tro partes de tal manera que las cuatro gráficas diagnósticas se dibujan en la misma ventana. Los
comandos son:
18 El paquete MASS tiene la función loglm que permite una interface de fórmula a loglin.
47
> opar <- par()
> par(mfcol = c(2, 2))
> plot(aov.spray)
> par(opar)
> termplot(aov.spray, se=TRUE, partial.resid=TRUE, rug=TRUE)
Standardized residuals
Residuals vs Fitted Scale−Location plot
1.5
27 39
27 39 25
Residuals
1.0
0.0
−1.5
0.0
25
39
25
0
−2
25
−2 −1 0 1 2 0 20 40 60
1
0
−1
−2
−3
A B C D E F
InsectSprays
Figura 14: Representación gráfica de los resultados de la función aov usando termplot().
5.2. Fórmulas
El uso de fórmulas es un elemento clave en análisis estadı́stico con R: la nomenclatura uilizada
es la misma para (casi) todas las funciones. Una fórmula se escribe tı́picamente como y ˜modelo
donde y es la variable dependiente (o de respuesta) y modelo es un conjunto de términos para los
cuales es necesario estimar una serie de parámetros. Estos términos se representan con sı́mbolos
aritméticos pero tienen un significado muy particular en R.
48
a+b efectos de a y b
X si X es una matriz, especifica un efecto aditivo para cada una de las co-
lumnas; por ejemplo X[,1]+X[,2]+...+X[,ncol(X)]; algunas
de las columnas se pueden seleccionar con ı́ndices numéricos (por ej.,
X[,2:4])
a:b efecto interactivo entre a y b
a*b efectos aditivos e interactivos entre a y b (idéntico a a+b+a:b)
poly(a, n) polinomios de a hasta grado n
ˆn incluye todas las itnteracciones hasta el nivel n, por ej., (a+b+c)ˆ2 es
idéntico a a+b+c+a:b+a:c+b:c
b %in % a los efectos de b están anidados en a (idéntico a a+a:b o a/b)
a-b remueve el efecto de b, por ejemplo: (a+b+c)ˆ2-a:b es idéntico a
a+b+c+a:c+b:c
-1 y˜x-1 regresión a través del origen (igual para y˜x+0 o 0+y˜x)
1 y˜1 ajusta un modelo sin efectos (solo el intercepto)
offset(...) agrega un efecto al modelo sin estimar los parámetros (e.g.,
offset(3*x))
Vemos que los operadores aritméticos en las fórmulas tienen significados muy diferentes a
los que tendrı́an en expresiones regulares. Por ejemplo la fórmula y˜x1+x2 define el modelo
y = β1 x1 + β2 x2 + α, en vez de (si el operador + tuviera su significado usual) y = β(x1 + x2 ) + α.
Para incluir operaciones aritméticas en una fórmula se puede usar la función I(): la fórmula
y˜I(x1+x2) define el modelo y = β(x1 + x2 ) + α. De manera similar, para definir el modelo
y = β1 x + β2 x2 + α, usaremos la fórmula y ˜poly(x, 2) (y no y ˜x + xˆ2).
Para análisis de varianza, aov() accepta una sintaxis particular para definir efectos aleatorios.
Por ejemplo, y ˜a + Error(b) significa los efectos aditivos de un término fijo (a) y uno
aleatorio (b).
49
print devuelve un corto resúmen
summary devuelve un resúmen detallado
df.residual devuelve el número de grados de libertad
coef devuelve los coeficientes estimados (algunas veces con sus errores estándar)
residuals devuelve los residuales
deviance devuelve la devianza
fitted devuelve los valores ajustados
logLik calcula el logaritmo de la verosimilitud y el número de parámetros
AIC calcula el criterio de información de Akaike o AIC (depende de logLik())
Funciones como aov o lm devuelven una lista donde los diferentes elementos corresponden
a los resultados del análisis. Si tomamos nuestro ejemplo con el análisis de varianza de los datos
en InsectSprays, podemos ver la estructura del objeto devuelto por aov():
Otra manera de ver esta estructura es visualizando los nombres del objeto:
> names(aov.spray)
[1] "coefficients" "residuals" "effects"
[4] "rank" "fitted.values" "assign"
[7] "qr" "df.residual" "contrasts"
[10] "xlevels" "call" "terms"
[13] "model"
> aov.spray$coefficients
(Intercept) sprayB sprayC sprayD
3.7606784 0.1159530 -2.5158217 -1.5963245
sprayE sprayF
-1.9512174 0.2579388
summary() también crea una lista la cual, en el caso de aov(), es simplemente una tabla
de pruebas:
> str(summary(aov.spray))
List of 1
$ :Classes anova and ‘data.frame’: 2 obs. of 5 variables:
..$ Df : num [1:2] 5 66
..$ Sum Sq : num [1:2] 88.4 26.1
..$ Mean Sq: num [1:2] 17.688 0.395
..$ F value: num [1:2] 44.8 NA
..$ Pr(>F) : num [1:2] 0 NA
- attr(*, "class")= chr [1:2] "summary.aov" "listof"
> names(summary(aov.spray))
NULL
50
> apropos("ˆsummary")
[1] "summary" "summary.aov"
[3] "summary.aovlist" "summary.connection"
[5] "summary.data.frame" "summary.default"
[7] "summary.factor" "summary.glm"
[9] "summary.glm.null" "summary.infl"
[11] "summary.lm" "summary.lm.null"
[13] "summary.manova" "summary.matrix"
[15] "summary.mlm" "summary.packageStatus"
[17] "summary.POSIXct" "summary.POSIXlt"
[19] "summary.table"
Podemos ver la diferencia para este método comparando como actúa en una regresión lineal
vs. un análisis de varianza:
Los objetos devueltos por aov(), lm(), summary(), . . . son listas, pero no se visualizan
como las listas “comunes y corrientes” que hemos visto anteriormente. De hecho, son métodos
print de estos objetos (recuerde que escribir el nombre de un objeto como un comando equivale
a usar print()):
> apropos("ˆprint")
[1] "print.pairwise.htest" "print.power.htest"
[3] "print" "print.anova"
[5] "print.aov" "print.aovlist"
[7] "print.atomic" "print.by"
[9] "print.coefmat" "print.connection"
[11] "print.data.frame" "print.default"
[13] "print.density" "print.difftime"
[15] "print.dummy.coef" "print.dummy.coef.list"
[17] "print.factor" "print.family"
[19] "print.formula" "print.ftable"
[21] "print.glm" "print.glm.null"
[23] "print.hsearch" "print.htest"
[25] "print.infl" "print.integrate"
[27] "print.libraryIQR" "print.listof"
[29] "print.lm" "print.lm.null"
[31] "print.logLik" "print.matrix"
[33] "print.mtable" "print.noquote"
51
[35] "print.octmode" "print.ordered"
[37] "print.packageIQR" "print.packageStatus"
[39] "print.POSIXct" "print.POSIXlt"
[41] "print.recordedplot" "print.rle"
[43] "print.SavedPlots" "print.simple.list"
[45] "print.socket" "print.summary.aov"
[47] "print.summary.aovlist" "print.summary.glm"
[49] "print.summary.glm.null" "print.summary.lm"
[51] "print.summary.lm.null" "print.summary.manova"
[53] "print.summary.table" "print.table"
[55] "print.tables.aov" "print.terms"
[57] "print.ts" "print.xtabs"
Todos estos métodos de print permiten cierta visualización dependiendo del análisis.
La siguiente tabla muestra algunas funciones genéricas que realizan análisis suplementarios a
un objeto resultante de un análisis, donde el argumento principal es este último, pero en algunos
casos se necesitan argumentos adicionales (por ejemplo, para predict o update).
add1 prueba sucesivamente todos los términos que se pueden adicionar a un modelo
drop1 prueba sucesivamente todos los términos que se pueden remover de un modelo
step selecciona un modelo con AIC (llama a add1 y a drop1)
anova calcula una tabla de análisis de varianza o devianza de uno o varios modelos
predict calcula los valores predichos para datos nuevos de un modelo ya ajustado
update re-ajusta un modelo con una nueva fórmula o nuevos datos
Existen tambien varias funciones utilitarias que extraen información de un objeto modelo o
fórmula, tal como alias() que encuentra los términos linealmente dependientes en un modelo
lineal especificado por una fórmula.
Finalmente, existen funciones gráficas como plot que hacen gráficos diagnósticos, o termplot
(ver el ejemplo anterior), aunque esta última no es estrictamente genérica, pues llama a predict().
5.4. Paquetes
La siguiente tabla muestra los paquetes que se distribuyen con base. Con excepción de ctest
que se carga en memoria cuando R comienza, cada paquete puede ser utilizado después de haber
sido cargado:
> library(eda)
> library(help=eda)
o navegando la ayuda en formato html. La información relativa a cada función se puede acceder
como lo vimos anteriormente (p. 7).
52
Paquete Descripción
ctest pruebas clásicas (Fisher, ‘Student’, Wilcoxon, Pearson, Bartlett, Kolmogorov-
Smirnov, . . . )
eda métodos descritos en “Exploratory Data Analysis” por Tukey (solo ajuste li-
neal robusto y ajuste de medianas)
lqs regresión resistente y estimación de covarianza
methods definición de métodos y clases para objetos en R y herramientas de programa-
ción
modreg regresión moderna (alisamiento y regresión local)
mva análisis multivariado
nls regresión no-lineal
splines representaciones polinómicas
stepfun funciones de distribución empı́ricas
tcltk funciones para hacer interfase desde R a elementos de interfase gráfica Tcl/Tk
tools herramientas para desarrollo y administración de paquetes
ts análisis de series temporales
Paquete Descripción
boot métodos de remuestreo y “bootstraping”
class métodos de clasificación
cluster métodos de agregación
foreign funciones para leer datos en diferentes formatos (S3, Stata, SAS, Minitab,
SPSS, Epi Info)
KernSmooth métodos para suavización nuclear y estimación de densidad (incluyendo
núcleos bivariados)
MASS contiene muchas funciones, herramientas y datos de las librerı́as de “Modern
Applied Statistics with S-PLUS” por Venables & Ripley
mgcv modelos aditivos generalizados
nlme modelos lineales y non-linear con efectos mixos
nnet redes neuronales y modelos multinomiales log-lineales
rpart particionamiento recursivo
spatial análisis espaciales (“kriging”, covarianza espacial, . . . )
survival análisis de sobrevivencia
El procedimiento para instalar un paquete depende del sistema operativo y de la manera como
se instaló R: desde el código fuente o archivos binarios pre-compilados. Si es esta útlima, es reco-
mendado usar los paquetes pre-compilados disponibles en el sitio CRAN. En Windows, el archivo
20 http://cran.r-project.org/src/contrib/PACKAGES.html
53
binario Rgui.exe tiene el menú “Packages” que permite la instalación de paquetes directamente
desde el disco duro o a través de internet desde la página web CRAN.
Si R fue compilado localmente, se puede instalar un paquete directamente desde el código
fuente el cual es distribuido normalmente como un archivo ‘.tar.gz’. Por ejemplo, si queremos
instalar el paquete gee, primero es necesario bajar desde el internet el archivo gee 4.13-6.tar.gz
(el número 4.13-6 indica la versión del paquete; generalmente solo existe una versión disponible
en CRAN). Después es necesario escribir lo siguiente desde el sistema (no desde R):
R INSTALL gee_4.13-6.tar.gz
> update.packages()
esto chequea las versiones de los paquete instalados en el sistema y los compara con los disponibles
en CRAN (este comando se puede llamar desde el menú “Packages” en Windows). De esta manera,
el usuario puede actualizar sus paquetes con las versiones más recientes.
y <- numeric(length(x))
for (i in 1:length(x)) if (x[i] == b) y[i] <- 0 else y[i] <- 1
for (i in 1:length(x)) {
y[i] <- 0
...
}
if (x[i] == b) {
y[i] <- 0
...
}
54
Otra posibilidad es ejecutar una instrucción siempre y cuando se cumpla una cierta condición:
Sin embargo, este tipo de bucles y estructuras se pueden evitar gracias a una caracterı́stica
clave en R: vectorización. La vectorización hace los bucles implı́citos en las expresiones y ya lo
hemos visto en muchos casos. Por ejemplo, consideremos la suma de dos vectores:
> z <- x + y
Esta suma se hubiera podido escribir como un bucle, como se hace en muchos otros lenguajes:
En este caso, es necesario crear con anterioridad el vector z por la necesidad de indexar los
elementos. Es fácil ver que este bucle explı́cito solo funciona si x y y son de la misma longitud:
es necesario alterar el programa si esto es falso, mientras que la primera situación funcionará en
todos los casos.
Las ejecuciones condicionales (if ... else) se pueden evitar con el uso de indexación
lógica; volviendo al ejemplo anterior:
También existen varias funciones del tipo “apply” que evitan el uso de bucles. apply()
actúa sobre las filas o columnas de una matriz, y su sintaxis es apply(X, MARGIN, FUN,
...), donde X es una matriz, MARGIN indica si se van a usar las filas (1), las columnas (2), or
ambas (c(1, 2)), FUN es una función (o un operador, pero en este caso debe especificarse en
corchetes) a ser aplicada, y ... son posibles argumentos opcionales de FUN. Veamos un ejemplo
simple.
La función lapply() actúa sobre una lista: su sintaxis es similar a la de apply y devuelve
una lista.
Call:
55
FUN(formula = X[[1]])
Coefficients:
(Intercept) x
31.683 5.377
[[2]]
Call:
FUN(formula = X[[2]])
Coefficients:
(Intercept) poly(x, 2)1 poly(x, 2)2
4.9330 1.2181 -0.6037
La función sapply() es una variación más flexible de lapply() que puede tomar un
vector o una matriz como argumento principal, y devuelve los resultados en una manera más
amigable, generalmente en una tabla.
El carcater ‘#’ se usa para agregar comentarios al programa y R los ignora durante la ejecución.
El problema del primer programa es que se puede volver bastante largo si queremos agregar
otras especies. Más aún, algunos comandos se ejecutan varias veces, y por lo tanto, se pueden
agrupar y ejecutar juntos cambiando algunos argumentos. La estrategia que usamos aqui es po-
ner estos argumentos en vectores de tipo caracter, y después usar indexación para acceder a los
diferentes valores.
56
file <- c("Swal.dat" , "Wren.dat", "Dunn.dat")
for(i in 1:length(species)) {
data <- read.table(file[i]) # leer los datos
plot(data$V1, data$V2, type="l")
title(species[i]) # agregar un titulo
}
Note que el argumento file[i] no se pone entre comillas en read.table() ya que este
argumento ya es de tipo caracter.
Ahora nuestro programa es mucho más compacto. Es más fácil agregar otras especies ya que
los nombres de las mismas están en vectores al principio del programa.
Los programas anteriores funcionarán correctamente siempre y cuando los archivos de datos
‘.dat’ estén localizados en el directorio de trabajo de R; de lo contrario el usuario debe cambiar
el directorio de trabajo o especificar la dirección completa en el programa (por ejemplo: file
<-”C:/data/Swal.dat"). Si el programa está en el archivo Mybirds.R, es necesario primero
cargarlo en memoria:
> source("Mybirds.R")
Para que esta función pueda ser ejecutada, primero es necesario cargarla en memoria, y esto se
puede hacer de varias maneras. Las lı́neas de la función se pueden escribir directamente desde el
teclado, como cualquier otro comando, o ser copiada y pegada a un editor de texto. Si la función
está guardada en un archivo ASCII, se puede cargar con source() como cualquier otro progra-
ma. Si el usuario desea que su función sea cargada cada vez que comienza R, se puede guardar en
un archivo especial llamado “espacio de trabajo” (del inglés ‘workspace’) .RData que será carga-
do en memoria automáticamente si se encuentra en el directorio de trabajo de R. Otra posibilidad
es configurar el archivo ‘.Rprofile’ o ‘Rprofile’ (ver ?Startup para más detalles). Finalmente,
es posible crear un paquete, pero no discutiremos esta alternativa aqui (vea el manual “Writing R
Extensions”).
Una vez la función es cargada se puede ejecutar con un solo comando como por ejemplo,
mifun("swallow", "Swal.dat"). Por lo tanto, tenemos ahora una tercera versión de
nuestro programa:
57
layout(matrix(1:3, 3, 1))
mifun("swallow", "Swal.dat")
mifun("wren", "Wrenn.dat")
mifun("dunnock", "Dunn.dat")
También podemos usar sapply() creando una cuarta versión del programa:
layout(matrix(1:3, 3, 1))
species <- c("swallow", "wren", "dunnock")
file <- c("Swal.dat" , "Wren.dat", "Dunn.dat")
sapply(species, mifun, file)
El nombre x no está definido dentro de foo(), asi que R buscará x dentro del ámbito cir-
cundante, e imprimirá su valor (de lo contrario, se genera un mensaje de error y la ejecución se
cancela).
Si x es utilizado como el nombre de un objeto dentro de la función, el valor de x en el ambiente
global (externo a la función) no cambia.
> x <- 1
> foo2 <- function() { x <- 2; print(x) }
> foo2()
[1] 2
> x
[1] 1
Esta vez print() usa el objeto x definido dentro de su ambiente, es decir el ambiente de
foo2.
La palabra “circundante” utilizada arriba es importante. En nuestras dos funciones ejemplo
existen dos ambientes: uno global y el otro local a cada una de las funciones foo o foo2. Si
existen tres o más ambientes anidados, la búsqueda de objetos se hace progresivamente desde un
ambiente dado al ambiente circundante a este, y asi sucesivamente hasta llegar el ambiente global.
Existen dos maneras de especificar argumentos a una función: por sus posiciones o por sus
nombres (también llamados argumentos marcados). Por ejemplo, consideremos una función con
tres argumentos:
foo() se puede ejecutar sin isar los nombres arg1, . . . , si los objetos correspondientes están
colocados en la posición correcta; por ejemplo: foo(x, y, z). Sin embargo, la posición no
tiene ninguna importancia si se utilizan los nombres de los argumentos, por ejemplo, foo(arg3
= z, arg2 = y, arg1 = x). Otra rasgo importante de las funciones en R es la posibilidad
de usar valores por defecto en la definición. Por ejemplo:
58
foo <- function(arg1, arg2 = 5, arg3 = FALSE) {...}
Manuales. R trae varios manuales que se instalan por defecto en R HOME/doc/manual/ (donde
R HOME donde R está instalado). Todos estos manuales están en inglés:
Los archivos pueden estar en diferentes formatos (pdf, html, texi, . . . ) dependiendo del tipo
de instalación.
FAQ. R también viene con su propio FAQ (Preguntas más frecuentes) localizadas en el directo-
rio R HOME/doc/html/. La versión de este R-FAQ es actualizada regularmente en el sitio
CRAN: http://cran.r-project.org/doc/FAQ/R-FAQ.html.
59
Recursos en lı́nea El sitio CRAN y la página web de R contienen varios documentos, recur-
sos bibliográficos y enlaces a otros sitios. También se puede encontrar aqui una lista de
publicaciones (libros y artı́culos) sobre R y métodos estadı́sticos en general21 , y algunos
documentos y tutoriales escritos por usuarios de R22 .
Listas de correo. Existen tres listas de discusión en R; para suscribirse, mande un mensaje o lea
los archivos en http://www.R-project.org/mail.html.
La lista de discusión general ‘r-help’ es una fuente interesante de información para usuarios
de R (las otras dos listas están dedicadas a anuncios de nuevas versiones, nuevos paquetes,
. . . , y programadores). Muchos usuarios han enviado funciones y programas a ‘r-help’ los
cuales pueden encontrarse en los archivos de la lista. Si se encuentra algún problema con R,
es importante proceder en el siguiente orden antes de enviar un mensaje a ‘r-help’:
R News. La revista electrónica R News tiene como objetivo llenar un vacı́o entre las listas de
discusión electrónicas y publicaciones cientı́ficas tradicionales. El primer número fué pu-
blicado en enero 2001, y se producen tres números por año. Kurt Hornik y Friedrich Leisch
son los editores24 .
Citando R en una publicación. Finalmente, si usted menciona a R en una publicación, debe citar
el artı́culo original:
Ihaka R. & Gentleman R. 1996. R: a language for data analysis and graphics.
Journal of Computational and Graphical Statistics 5: 299–314.
21 http://www.R-project.org/doc/bib/R-publications.html
22 http://cran.r-project.org/other-docs.html.Aquı́ se pueden encontrar dos manuales más de R escritos en español.
23 Las direcciones de estos sitios se encuentran en http://cran.r-project.org/search.html
24 http://cran.r-project.org/doc/Rnews/
60
El tutorial de
Python
Autor original: Guido van Rossum
Editor original: Fred L. Drake, Jr.
http://python.org.ar/pyar/Tutorial
Septiembre 2009
Abriendo tu apetito 8
Invocando al intérprete 10
Pasaje de argumentos 11
Modo interactivo 11
El intérprete y su entorno 12
Manejo de errores 12
Números 15
Cadenas de caracteres 18
Listas 24
La sentencia if 28
La sentencia for 28
La función range() 29
La sentencia pass 30
Definiendo funciones 31
Estructuras de datos 40
La instrucción del 45
Tuplas y secuencias 46
Conjuntos 47
Diccionarios 48
Técnicas de iteración 49
Módulos 52
Módulos estándar 56
La función dir() 56
Paquetes 58
Entrada y salida 63
El módulo pickle 69
Errores y excepciones 71
Errores de sintaxis 71
Excepciones 71
Manejando excepciones 72
Levantando excepciones 75
Clases 79
Objetos clase 82
Objetos instancia 83
Objetos método 84
Algunas observaciones 85
Herencia 86
Herencia múltiple 87
Variables privadas 88
Cambalache 89
Iteradores 90
Generadores 92
Expresiones generadoras 92
Matemática 96
Acceso a Internet 96
Fechas y tiempos 97
Compresión de datos 97
Medición de rendimiento 98
Control de calidad 98
Plantillas 101
Multi-hilos 103
Registrando 104
El intérprete de Python y la extensa biblioteca estándar están a libre disposición en forma binaria y de
código fuente para las principales plataformas desde el sitio web de Python, http://www.python.org/, y
puede distribuirse libremente. El mismo sitio contiene también distribuciones y enlaces de muchos
módulos libres de Python de terceros, programas y herramientas, y documentación adicional.
El intérprete de Python puede extenderse fácilmente con nuevas funcionalidades y tipos de datos
implementados en C o C++ (u otros lenguajes accesibles desde C). Python también puede usarse como
un lenguaje de extensiones para aplicaciones personalizables.
Este tutorial introduce de manera informal al lector a los conceptos y características básicas del lenguaje y
el sistema de Python. Es bueno tener un interprete de Python a mano para experimentar, sin embargo
todos los ejemplos están aislados, por lo tanto el tutorial puede leerse estando desconectado.
Para una descripción de los objetos y módulos estándar, mira la Referencia de la Biblioteca de Python. El
Manual de Referencia de Python provee una definición más formal del lenguaje. Para escribir extensiones
en C o C++, lee Extendiendo e Integrando el Intérprete de Python y la Referencia de la API Python/C. Hay
también numerosos libros que tratan a Python en profundidad.
Este tutorial no pretende ser exhaustivo ni tratar cada una de las características, o siquiera las
características más usadas. En cambio, introduce la mayoría de las características más notables de
Python, y te dará una buena idea del gusto y estilo del lenguaje. Luego de leerlo, serás capaz de leer y
escribir módulos y programas en Python, y estarás listo para aprender más de los variados módulos de la
biblioteca de Python descriptos en la Referencia de la Biblioteca de Python.
7
Abriendo tu apetito
Si trabajás mucho con computadoras, eventualmente encontrarás que te gustaría automatizar alguna
tarea. Por ejemplo, podrías desear realizar una búsqueda y reemplazo en un gran número de archivos de
texto, o renombrar y reorganizar un montón de archivos con fotos de una manera compleja. Tal vez
quieras escribir alguna pequeña base de datos personalizada, o una aplicación especializada con interfaz
gráfica, o un juego simple.
Si sos un desarrollador de software profesional, tal vez necesites trabajar con varias bibliotecas de
C/C++/Java pero encuentres que se hace lento el ciclo usual de escribir/compilar/testear/recompilar. Tal
vez estás escribiendo una batería de pruebas para una de esas bibliotecas y encuentres que escribir el
código de testeo se hace una tarea tediosa. O tal vez has escrito un programa al que le vendría bien un
lenguaje de extensión, y no quieres diseñar/implementar todo un nuevo lenguaje para tu aplicación.
Podrías escribir un script (o programa) en el interprete de comandos o un archivo por lotes de Windows
para algunas de estas tareas, pero los scripts se lucen para mover archivos de un lado a otro y para
modificar datos de texto, no para aplicaciones con interfaz de usuario o juegos. Podrías escribir un
programa en C/C++/Java, pero puede tomar mucho tiempo de desarrollo obtener al menos un primer
borrador del programa. Python es más fácil de usar, está disponible para sistemas operativos Windows,
Mac OS X y Unix, y te ayudará a realizar tu tarea más velozmente.
Python es fácil de usar, pero es un lenguaje de programación de verdad, ofreciendo mucho mucho mayor
estructura y soporte para programas grandes que lo que lo que pueden ofrecer los scripts de Unix o
archivos por lotes. Por otro lado, Python ofrece mucho más chequeo de error que C, y siendo un lenguaje
de muy alto nivel, tiene tipos de datos de alto nivel incorporados como arreglos de tamaño flexible y
diccionarios. Debido a sus tipos de datos más generales Python puede aplicarse a un dominio de
problemas mayor que Awk o incluso Perl, y aún así muchas cosas siguen siendo al menos igual de fácil
en Python que en esos lenguajes.
Python te permite separar tu programa en módulos que pueden reusarse en otros programas en Python.
Viene con una gran colección de módulos estándar que puedes usar como base de tus programas, o
como ejemplos para empezar a aprender a programar en Python. Algunos de estos módulos proveen
cosas como entrada/salida a archivos, llamadas al sistema, sockets, e incluso interfaces a sistemas de
interfaz gráfica de usuario como Tk.
Python es un lenguaje interpretado, lo cual puede ahorrarte mucho tiempo durante el desarrollo ya que no
es necesario compilar ni enlazar. El intérprete puede usarse interactivamente, lo que facilita experimentar
con características del lenguaje, escribir programas descartables, o probar funciones cuando se hace
desarrollo de programas de abajo hacia arriba. Es también una calculadora de escritorio práctica.
Python permite escribir programas compactos y legibles. Los programas en Python son típicamente más
cortos que sus programas equivalentes en C, C++ o Java por varios motivos:
• los tipos de datos de alto nivel permiten expresar operaciones complejas en una sola instrucción
• la agrupación de instrucciones se hace por snagría en vez de llaves de apertura y cierre
8
• no es necesario declarar variables ni argumentos.
Python es extensible: si ya sabes programar en C es fácil agregar una nueva función o módulo al
intérprete, ya sea para realizar operaciones críticas a velocidad máxima, o para enlazar programas Python
con bibliotecas que tal vez sólo estén disponibles en forma binaria (por ejemplo bibliotecas gráficas
específicas de un fabricante). Una vez que estés realmente entusiasmado, podés enlazar el intérprete
Python en una aplicación hecha en C y usarlo como lenguaje de extensión o de comando para esa
aplicación.
Por cierto, el lenguaje recibe su nombre del programa de televisión de la BBC "Monty Python's Flying
Circus" y no tiene nada que ver con reptiles. Hacer referencias a sketches de Monty Python en la
documentación no sólo esta permitido, ¡sino que también está bien visto!
Ahora que ya estás emocionado con Python, querrás verlo en más detalle. Como la mejor forma de
aprender un lenguaje es usarlo, el tutorial te invita a que juegues con el intérprete de Python a medida que
vas leyendo.
En el próximo capítulo se explicará la mecánica de uso del intérprete. Esta es información bastante
mundana, pero es esencial para poder probar los ejemplos que aparecerán más adelante.
El resto del tutorial introduce varias características del lenguaje y el sistema Python a través de ejemplos,
empezando con expresiones, instrucciones y tipos de datos simples, pasando por funciones y módulos, y
finalmente tocando conceptos avanzados como excepciones y clases definidas por el usuario.
9
Usando el intérprete de Python
Invocando al intérprete
Por lo general, el intérprete de Python se instala en file:/usr/local/bin/python en las máquinas dónde está
disponible; poner /usr/local/bin en el camino de búsqueda de tu intérprete de comandos Unix hace
posible iniciarlo ingresando la orden:
python
...en la terminal. Ya que la elección del directorio dónde vivirá el intérprete es una opción del proceso de
instalación, puede estar en otros lugares; consultá a tu Gurú Python local o administrador de sistemas.
(Por ejemplo, /usr/local/python es una alternativa popular).
set path=%path%;C:\python26
Se puede salir del intérprete con estado de salida cero ingresando el carácter de fin de archivo
(Control-D en Unix, Control-Z en Windows) en el prompt primario. Si esto no funciona, se puede salir
del intérprete ingresando: import sys; sys.exit().
Las características para editar líneas del intérprete no son muy sofisticadas. En Unix, quien instale el
intérprete tendrá habilitado el soporte para la biblioteca GNU readlines, que añade una edición interactiva
más elaborada e historia. Tal vez la forma más rápida de detectar si las características de edición están
presentes es ingresar Control-P en el primer prompt de Python que aparezca. Si se escucha un beep, las
características están presentes; ver Apéndice tut-interacting para una introducción a las teclas. Si no pasa
nada, o si aparece ^P, estas características no están disponibles; solo vas a poder usar backspace para
borrar los caracteres de la línea actual.
La forma de operar del intérprete es parecida a la línea de comandos de Unix: cuando se la llama con la
entrada estándar conectada a una terminal lee y ejecuta comandos en forma interactiva; cuando es
llamada con un nombre de archivo como argumento o con un archivo como entrada estándar, lee y
ejecuta un script del archivo.
Una segunda forma de iniciar el intérprete es python -c comando [arg] ..., que ejecuta las
sentencias en comando, similar a la opción -c de la línea de comandos. Ya que las sentencias de Python
suelen tener espacios en blanco u otros caracteres que son especiales en la línea de comandos, es
normalmente recomendado citar comando entre comillas dobles.
Algunos módulos de Python son también útiles como scripts. Pueden invocarse usando python -m
module [arg] ..., que ejecuta el código de module como si se hubiese ingresado su nombre completo
en la línea de comandos.
10
Notá que existe una diferencia entre python file y python <file. En el último caso, la entrada
solicitada por el programa, como en llamadas a input() y raw_input(), son satisfechas desde file. Ya
que este archivo ya fue leído hasta el final por el analizador antes de que el programa empiece su
ejecución, se encontrará el fin de archivo enseguida. En el primer caso (lo que usualmente vas a querer)
son satisfechas por cualquier archivo o dispositivo que esté conectado a la entrada estándar del intérprete
de Python.
Cuando se usa un script, a veces es útil correr primero el script y luego entrar al modo interactivo. Esto se
puede hacer pasándole la opción -i antes del nombre del script. (Esto no funciona si el script es leído
desde la entrada estándar, por la misma razón explicada en el párrafo anterior).
Pasaje de argumentos
Cuando son conocidos por el intérprete, el nombre del script y los argumentos adicionales son entonces
pasados al script en la variable sys.argv, una lista de cadenas de texto. Su longitud es al menos uno;
cuando ningún script o argumentos son pasados, sys.argv[0] es una cadena vacía. Cuando se pasa
el nombre del script con '-' (lo que significa la entrada estándar), sys.argv[0] vale '-'. Cuando se
usa -c command, sys.argv[0] vale '-c'. Cuando se usa -m module, sys.argv[0] toma el valor del
nombre completo del módulo. Las opciones encontradas luego de -c command o -m module no son
consumidas por el procesador de opciones de Python pero de todas formas almacenadas en sys.argv
para ser manejadas por el comando o módulo.
Modo interactivo
Se dice que estamos usando el intérprete en modo interactivo, cuando los comandos son leídos desde
una terminal. En este modo espera el siguiente comando con el prompt primario, usualmente tres signos
mayor-que (>>>); para las líneas de continuación espera con el prompt secundario, por defecto tres
puntos (...). Antes de mostrar el prompt primario, el intérprete muestra un mensaje de bienvenida
reportando su número de versión y una nota de copyright:
python
Python 2.6 (#1, Feb 28 2007, 00:02:06)
Type "help", "copyright", "credits" or "license" for more information.
>>>
Las líneas de continuación son necesarias cuando queremos ingresar un constructor multilínea. Como en
el ejemplo, mirá la sentencia if:
>>> el_mundo_es_plano = 1
>>> if el_mundo_es_plano:
... print u"¡Tené cuidado de no caerte!"
...
¡Tené cuidado de no caerte!
11
El intérprete y su entorno
Manejo de errores
Cuando ocurre un error, el intérprete imprime un mensaje de error y la traza del error. En el modo
interactivo, luego retorna al prompt primario; cuando la entrada viene de un archivo, el programa termina
con código de salida distinto a cero luego de imprimir la traza del error. (Las excepciones manejadas por
una clausula except en una sentencia try no son errores en este contexto). Algunos errores son
incondicionalmente fatales y causan una terminación con código de salida distinto de cero; esto se debe a
inconsistencias internas o a que el intérprete se queda sin memoria. Todos los mensajes de error se
escriben en el flujo de errores estándar; las salidas normales de comandos ejecutados se escriben en la
salida estándar.
Al ingresar el caracter de interrupción (por lo general Control-C o DEL) en el prompt primario o secundario,
se cancela la entrada y retorna al prompt primario. 1 Tipear una interrupción mientras un comando se
están ejecutando lanza la excepción KeyboardInterrupt, que puede ser manejada con una sentencia
try.
#! /usr/bin/env python
...al principio del script y dándole al archivo permisos de ejecución (asumiendo que el intérprete están en
la variable de entorno PATH del usuario). #! deben ser los primeros dos caracteres del archivo. En
algunas plataformas, la primer línea debe terminar al estilo Unix ('\n'), no como en Windows ('\r\n').
Notá que el caracter numeral '#' se usa en Python para comenzar un comentario.
$ chmod +x myscript.py
12
Codificación del código fuente
Es posible utilizar una codificación distinta a ASCII en el código fuente de Python. La mejor forma de
hacerlo es poner otro comentario especial enseguida después de la línea con #! para definir la
codificación:
Con esa declaración, todos los caracteres en el archivo fuente serán traducidos utilizando la codificación
encoding, y será posible escribir directamente cadenas de texto literales Unicode en la codificación
seleccionada. La lista de posibles codificaciones se puede encontrar en la Referencia de la Biblioteca de
Python, en la sección sobre codecs.
Por ejemplo, para escribir literales Unicode, incluyendo el símbolo de la moneda Euro, se puede usar la
codificación ISO-8859-15, en la que el símbolo Euro tiene el valor 164. Este script imprimirá el valor 8364
(el código Unicode correspondiente al símbolo Euro) y luego saldrá:
moneda = u"€"
print ord(moneda)
Si tu editor tiene soporte para guardar archivos como UTF-8 con marca de orden de byte UTF-8 (también
conocida como BOM), podés usar eso en lugar de la declaración de codificación. IDLE lo soporta si se
activa Options/General/Default Source Encoding/UTF-8. Notá que esto no funciona en
versiones antiguas de Python (2.2 y anteriores), ni por el sistema operativo en scripts con la línea con #!
(solo usado en sistemas Unix).
Usando UTF-8 (ya sea mediante BOM o la declaración de codificación), los caracteres de la mayoría de
los idiomas del mundo pueden ser usados simultáneamente en cadenas de texto o comentarios. No se
soporta usar caracteres no-ASCII en identificadores. Para mostrar todos estos caracteres de forma
apropiada, tu editor debe reconocer que el archivo es UTF-8, y debe usar una tipografía que soporte todos
los caracteres del archivo.
Este archivo es solo leído en las sesiones interactivas del intérprete, no cuando Python lee comandos de
un script ni cuando file:/dev/tty se explicita como una fuente de comandos (que de otro modo se comporta
como una sesión interactiva). Se ejecuta en el mismo espacio de nombres en el que los comandos
interactivos se ejecutan, entonces los objetos que define o importa pueden ser usados sin cualificaciones
en la sesión interactiva. En este archivo también podés cambiar los prompts sys.ps1 y sys.ps2.
13
Si querés leer un archivo de inicio adicional desde el directorio actual, podés programarlo en el archivo de
inicio global usando algo como if os.path.isfile('.pythonrc.py'):
execfile('.pythonrc.py'). Si querés usar el archivo de inicio en un script, tenés que hacer lo
siguiente en forma explícita en el script:
import os
nombrearchivo = os.environ.get('PYTHONSTARTUP')
if nombrearchivo and os.path.isfile(nombrearchivo):
execfile(nombrearchivo)
14
Una introducción informal a Python
En los siguientes ejemplos, las entradas y salidas son distinguidas por la presencia o ausencia de los
prompts (`>>>` and `...`): para reproducir los ejemplos, debés escribir todo lo que esté después del
prompt, cuando este aparezca; las líneas que no comiencen con el prompt son las salidas del intérprete.
Tené en cuenta que el prompt secundario que aparece por si sólo en una línea de un ejemplo significa que
debés escribir una línea en blanco; esto es usado para terminar un comando multilínea.
Muchos de los ejemplos de este manual, incluso aquellos ingresados en el prompt interactivo, incluyen
comentarios. Los comentarios en Python comienzan con el carácter numeral, #, y se extienden hasta el
final físico de la línea. Un comentario quizás aparezca al comienzo de la línea o seguidos de espacios
blancos o código, pero sin una cadena de caracteres. Un carácter numeral dentro de una cadena de
caracteres es sólo un carácter numeral. Ya que los comentarios son para aclarar código y no son
interpretados por Python, pueden omitirse cuando se escriben ejemplos.
Algunos ejemplos:
Números
El intérprete actúa como una simple calculadora; podés ingrsar una expresión y este escribirá los valores.
La sintaxis es sencilla: los operadores +, -, * y / funcionan como en la mayoría de los lenguajes (por
ejemplo, Pascal o C); los paréntesis pueden ser usados para agrupar. Por ejemplo:
>>> 2+2
4
>>> # Este es un comentario
... 2+2
4
>>> 2+2 # y un comentario en la misma línea que el código
4
>>> (50-5*6)/4
5
15
>>> # La división entera retorna redondeado al piso:
... 7/3
2
>>> 7/-3
-3
El signo igual (=) es usado para asignar un valor a una variable. Luego, ningún resultado es mostrado
antes del próximo prompt:
>>> ancho = 20
>>> largo = 5*9
>>> ancho * largo
900
>>> x = y = z = 0 # Cero a x, y, y z
>>> x
0
>>> y
0
>>> z
0
Las variables deben estar "definidas" (con un valor asignado) antes de que puedan usarse, o un error
ocurrirá:
Se soporta completamente los números de punto flotante; las operaciones con mezclas en los tipos de los
operandos convierten los enteros a punto flotante:
Los números complejos también están soportados; los números imaginarios son escritos con el sufijo de
j o J. Los números complejos con un componente real que no sea cero son escritos como
(real+imagj), o pueden ser escrito con la función complex(real, imag).
>>> 1j * 1J
(-1+0j)
16
>>> 1j * complex(0,1)
(-1+0j)
>>> 3+1j*3
(3+3j)
>>> (3+1j)*3
(9+3j)
>>> (1+2j)/(1+1j)
(1.5+0.5j)
Los números complejos son siempre representados como dos números de punto flotante, la parte real y la
imaginaria. Para extraer estas partes desde un número complejo z, usá z.real y z.imag.
>>> a=1.5+0.5j
>>> a.real
1.5
>>> a.imag
0.5
La función de conversión de los punto flotante y enteros (float(), int() y long()) no funciona para
números complejos; aquí no hay una forma correcta de convertir un número complejo a un número real.
Usá abs(z) para obtener esta magnitud (como un flotante) o z.real para obtener la parte real.
>>> a=3.0+4.0j
>>> float(a)
...
TypeError: can't convert complex to float
>>> a.real
3.0
>>> a.imag
4.0
>>> abs(a) # sqrt(a.real**2 + a.imag**2)
5.0
>>>
En el modo interactivo, la última expresión impresa es asignada a la variable _. Esto significa que cuando
estés usando Python como una calculadora de escritorio, es más fácil seguir calculando, por ejemplo:
17
Esta variable debería ser tratada como de sólo lectura por el usuario. No le asignes explícitamente un
valor; crearás una variable local independiente con el mismo nombre enmascarando la variable con el
comportamiento mágico.
Cadenas de caracteres
Además de números, Python puede manipular cadenas de texto, las cuales pueden ser expresadas de
distintas formas. Pueden estar encerradas en comillas simples o dobles:
Las cadenas de texto literales pueden contener múltiples líneas de distintas formas. Las líneas continuas
se pueden usar, con una barra invertida como el último carácter de la línea para indicar que la siguiente
línea es la continuación lógica de la línea:
print hola
Notá que de todas formas se necesita embeber los salto de líneas con \n; la nueva línea que sigue a la
barra invertida final es descartada. Este ejemplo imprimiría:
O, las cadenas de texto pueden ser rodeadas en un par de comillas triples: """ o '''. No se necesita
escapar los finales de línea cuando se utilizan comillas triples, pero serán incluidos en la cadena.
print """
Uso: algo [OPTIONS]
18
-h Muestra el mensaje de uso
-H nombrehost Nombre del host al cual conectarse
"""
Si se hace de la cadena de texto una cadena "cruda", la secuencia \n no es convertida a salto de línea,
pero la barra invertida al final de la línea y el carácter de nueva línea en la fuente, ambos son incluidos en
la cadena como datos. Así, el ejemplo:
print hola
...imprimirá:
El interprete imprime el resultado de operaciones entre cadenas de la misma forma en que son tecleadas
como entrada: dentro de comillas, y con comillas y otros caracteres raros escapados con barras invertidas,
para mostrar el valor preciso. La cadena de texto es encerrada con comillas dobles si contiene una comilla
simple y no comillas dobles, sino es encerrada con comillas simples. (La declaración print, descrita
luego, puede ser usado para escribir cadenas sin comillas o escapes).
Las cadenas de texto pueden ser concatenadas (pegadas juntas) con el operador + y repetidas con *:
Dos cadenas de texto juntas son automáticamente concatenadas; la primer línea del ejemplo anterior
podría haber sido escrita palabra = 'Ayuda' 'A'; esto solo funciona con dos literales, no con
expresiones arbitrarias:
19
SyntaxError: invalid syntax
Las cadenas de texto se pueden indexar; como en C, el primer carácter de la cadena tiene el índice 0. No
hay un tipo de dato para los caracteres; un carácter es simplemente una cadena de longitud uno. Como en
Icon, se pueden especificar subcadenas con la notación de rebanadas: dos índices separados por dos
puntos.
>>> palabra[4]
'a'
>>> palabra[0:2]
'Ay'
>>> palabra[2:4]
'ud'
Los índices de las rebanadas tienen valores por defecto útiles; el valor por defecto para el primer índice es
cero, el valor por defecto para el segundo índice es la longitud de la cadena a rebanar.
A diferencia de las cadenas de texto en C, en Python no pueden ser modificadas. Intentar asignar a una
posición de la cadena es un error:
Sin embargo, crear una nueva cadena con contenido combinado es fácil y eficiente:
20
Los índices degenerados en las rebanadas son manejados bien: un índice muy largo es reemplazado por
la longitud de la cadena, un límite superior más chico que el límite menor retorna una cadena vacía.
>>> palabra[1:100]
'yudaA'
>>> palabra[10:]
''
>>> palabra[2:1]
''
Los índices pueden ser números negativos, para empezar a contar desde la derecha. Por ejemplo:
Pero notá que -0 es en realidad lo mismo que 0, ¡por lo que no cuenta desde la derecha!
Los índices negativos fuera de rango son truncados, pero esto no funciona para índices de un solo
elemento (no rebanada):
>>> palabra[-100:]
'AyudaA'
>>> palabra[-10] # error
Traceback (most recent call last):
File "<stdin>", line 1, in ?
IndexError: string index out of range
Una forma de recordar cómo funcionan las rebanadas es pensar en los índices como puntos entre
caracteres, con el punto a la izquierda del primer carácter numerado en 0. Luego, el punto a la derecha del
último carácter de una cadena de n caracteres tienen índice n, por ejemplo:
+---+---+---+---+---+---+
| A | y | u | d | a | A |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
La primer fila de números da la posición de los índices 0...6 en la cadena; la segunda fila da los
correspondientes índices negativos. La rebanada de i a j consiste en todos los caracteres entre los puntos
21
etiquetados i y j, respectivamente.
Para índices no negativos, la longitud de la rebanada es la diferencia de los índices, si ambos entran en
los límites. Por ejemplo, la longitud de palabra[1:3] es 2.
>>> s = 'supercalifrastilisticoespialidoso'
>>> len(s)
33
Ver también
typesseq
Las cadenas de texto y la cadenas de texto Unicode descritas en la siguiente sección son ejemplos de
tipos secuencias, y soportan las operaciones comunes para esos tipos.
string-methods
Tanto las cadenas de texto normales como las cadenas de texto Unicode soportan una gran cantidad
de métodos para transformaciones básicas y búsqueda.
new-string-formatting
Aquí se da información sobre formateo de cadenas de texto con str.format().
string-formatting
Aquí se describe con más detalle las operaciones viejas para formateo usadas cuando una cadena de
texto o una cadena Unicode están a la izquierda del operador %.
Unicode tiene la ventaja de tener un número ordinal para cada carácter usado tanto en textos modernos
como antiguos. Previamente, había sólo 256 ordinales posibles para los caracteres en scripts. Los textos
eran típicamente asociados a un código que relaciona los ordinales a caracteres en scripts. Esto lleva a
mucha confusión, especialmente al internacionalizar software. Unicode resuelve estos problemas
definiendo una sola codificación para todos los scripts.
Crear cadenas Unicode en Python es tan simple como crear cadenas de texto normales:
La 'u' al frente de la comilla indica que se espera una cadena Unicode. Si querés incluir caracteres
especiales en la cadena, podés hacerlo usando una forma de escapar caracteres Unicode provista por
22
Python. El siguiente ejemplo muestra cómo:
>>> u'Hola\u0020Mundo!'
u'Hola Mundo!'
La secuencia de escape \u0020 indica que se debe insertar el carácter Unicode con valor ordinal 0x0020
(el espacio en blanco) en la posición dada.
Otros caracteres son interpretados usando su respectivo valor ordinal como ordinales Unicode. Si tenés
cadenas de texto literales en la codificación estándar Latin-1 que es muy usada en países occidentales,
encontrarás conveniente que los primeros 256 caracteres de Unicode son los mismos primeros 256
caracteres de Latin-1.
También existe un modo crudo para expertos, del mismo modo que con las cadenas de texto normales.
Debés anteponer 'ur' a la comilla inicial para que Python use el modo de escape crudo de Unicode. Solo
se aplicará la conversión \uXXXX si hay un número impar de barras invertidas frente a la 'u'.
>>> ur'Hola\u0020Mundo!'
u'Hola Mundo!'
>>> ur'Hola\\u0020Mundo!'
u'Hola\\\\u0020Mundo!'
El modo crudo es útil principalmente cuando tenés que insertar muchas barras invertidas, como puede
suceder al trabajar con expresiones regulares.
Además de estas codificaciones estándar, Python provee muchas más formas de crear cadenas de texto
Unicode en las bases de codificaciones conocidas.
Para convertir una cadena Unicode en una cadena de 8-bit utilizando un código en particular, los objetos
Unicode tienen un método encode() que toma un argumento, el nombre del código. Se prefieren los
nombres en minúsculas para los nombres de los códigos.
>>> u"äöü".encode('utf-8')
'\xc3\xa4\xc3\xb6\xc3\xbc'
23
Si tenés datos en un código en particular y querés producir la cadena Unicode correspondiente, podés
usar la función unicode() con el nombre del código como segundo argumento.
Listas
Python tiene varios tipos de datos compuestos, usados para agrupar otros valores. El más versátil es la
lista, la cual puede ser escrita como una lista de valores separados por coma (ítems) entre corchetes. No
es necesario que los ítems de una lista tengan todos el mismo tipo.
Como los índices de las cadenas de texto, los índices de las listas comienzan en 0, y las listas pueden ser
rebanadas, concatenadas y todo lo demás:
>>> a[0]
'pan'
>>> a[3]
1234
>>> a[-2]
100
>>> a[1:-1]
['huevos', 100]
>>> a[:2] + ['carne', 2*2]
['pan', 'huevos', 'carne', 4]
>>> 3*a[:3] + ['Boo!']
['pan', 'huevos', 100, 'pan', 'huevos', 100, 'pan', 'huevos', 100, 'Boo!']
A diferencia de las cadenas de texto, que son inmutables, es posible cambiar un elemento individual de
una lista:
>>> a
['pan', 'huevos', 100, 1234]
>>> a[2] = a[2] + 23
>>> a
['pan', 'huevos', 123, 1234]
También es posible asignar a una rebanada, y esto incluso puede cambiar la longitud de la lista o vaciarla
totalmente:
24
>>> a
[1, 12, 123, 1234]
>>> # Borrar algunos:
... a[0:2] = []
>>> a
[123, 1234]
>>> # Insertar algunos:
... a[1:1] = ['bruja', 'xyzzy']
>>> a
[123, 'bruja', 'xyzzy', 1234]
>>> # Insertar (una copia de) la misma lista al principio
>>> a[:0] = a
>>> a
[123, 'bruja', 'xyzzy', 1234, 123, 'bruja', 'xyzzy', 1234]
>>> # Vaciar la lista: reemplazar todos los items con una lista vacía
>>> a[:] = []
>>> a
[]
Es posible anidar listas (crear listas que contengan otras listas), por ejemplo:
>>> q = [2, 3]
>>> p = [1, q, 4]
>>> len(p)
3
>>> p[1]
[2, 3]
>>> p[1][0]
2
>>> p[1].append('extra') # Ver seccion 5.1
>>> p
[1, [2, 3, 'extra'], 4]
>>> q
[2, 3, 'extra']
Notá que en el último ejemplo, p[1] y q ¡realmente hacen referencia al mismo objeto! Volveremos a la
semántica de los objetos más adelante.
25
Primeros pasos hacia la programación
Por supuesto, podemos usar Python para tareas más complicadas que sumar dos y dos. Por ejemplo,
podemos escribir una subsecuencia inicial de la serie de Fibonacci así:
26
• La declaración print escribe el valor de la o las expresiones que se le pasan. Difiere de
simplemente escribir la expresión que se quiere mostrar (como hicimos antes en los ejemplos de la
calculadora) en la forma en que maneja múltiples expresiones y cadenas. Las cadenas de texto
son impresas sin comillas, y un espacio en blanco es insertado entre los elementos, así podés
formatear cosas de una forma agradable:
>>> i = 256*256
>>> print 'El valor de i es', i
El valor de i es 65536
>>> a, b = 0, 1
>>> while b < 1000:
... print b,
... a, b = b, a+b
...
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
Notá que el intérprete inserta un salto de línea antes de imprimir el próximo prompt si la última
línea no estaba completa.
27
Más herramientas para control de flujo
Además de la sentencia while que acabamos de introducir, Python soporta las sentencias de control de
flujo que podemos encontrar en otros lenguajes, con algunos cambios.
La sentencia if
Tal vez el tipo más conocido de sentencia sea el if. Por ejemplo:
Puede haber cero o más bloques elif, y el bloque else es opcional. La palabra reservada 'elif' es
una abreviación de 'else if', y es útil para evitar un sangrado excesivo. Una secuencia if ... elif ...
elif ... sustituye las sentencias switch o case encontradas en otros lenguajes.
La sentencia for
La sentencia for en Python difiere un poco de lo que uno puede estar acostumbrado en lenguajes como
C o Pascal. En lugar de siempre iterar sobre una progresión aritmética de números (como en Pascal) o
darle al usuario la posibilidad de definir tanto el paso de la iteración como la condición de fin (como en C),
la sentencia for de Python itera sobre los ítems de cualquier secuencia (una lista o una cadena de
texto), en el orden que aparecen en la secuencia. Por ejemplo:
28
defenestrado 12
No es seguro modificar la secuencia sobre la que se está iterando en el lazo (esto solo es posible para
tipos de secuencias mutables, como las listas). Si se necesita modificar la lista sobre la que se está
iterando (por ejemplo, para duplicar ítems seleccionados) se debe iterar sobre una copia. La notación de
rebanada es conveniente para esto:
>>> for x in a[:]: # hacer una copia por rebanada de toda la lista
... if len(x) > 6: a.insert(0, x)
...
>>> a
['defenestrado', 'ventana', 'gato', 'ventana', 'defenestrado']
La función range()
Si se necesita iterar sobre una secuencia de números, es apropiado utilizar la función integrada range().
Genera una lista conteniendo progresiones aritméticas:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
El valor final dado nunca es parte de la lista; range(10) genera una lista de 10 valores, los índices
correspondientes para los ítems de una secuencia de longitud 10. Es posible hacer que el rango empiece
con otro número, o especificar un incremento diferente (incluso negativo; algunas veces se lo llama
'paso'):
Para iterar sobre los índices de una secuencia, podés combinar range() y len() así:
En la mayoría de los casos, sin embargo, conviene usar la función enumerate(), mirá tut-loopidioms.
29
Las sentencias break, continue, y else en
lazos
La sentencia break, como en C, termina el lazo for o while más anidado.
La sentencia continue, también tomada prestada de C, continua con la próxima iteración del lazo.
Las sentencias de lazo pueden tener una cláusula else que es ejecutada cuando el lazo termina, luego
de agotar la lista (con for) o cuando la condición se hace falsa (con while), pero no cuando el lazo es
terminado con la sentencia break. Se ejemplifica en el siguiente lazo, que busca números primos:
La sentencia pass
La sentencia pass no hace nada. Se puede usar cuando una sentencia es requerida por la sintáxis pero
el programa no requiere ninguna acción. Por ejemplo:
30
Otro lugar donde se puede usar pass es como una marca de lugar para una función o un cuerpo
condicional cuando estás trabajando en código nuevo, lo cual te permite pensar a un nivel de abstracción
mayor. El pass se ignora silenciosamente:
Definiendo funciones
Podemos crear una función que escriba la serie de Fibonacci hasta un límite determinado:
La palabra reservada def se usa para definir funciones. Debe seguirle el nombre de la función y la lista
de parámetros formales entre paréntesis. Las sentencias que forman el cuerpo de la función empiezan en
la línea siguiente, y deben estar con sangría.
La primer sentencia del cuerpo de la función puede ser opcionalmente una cadena de texto literal; esta es
la cadena de texto de documentación de la función, o docstring. (Podés encontrar más acerca de
docstrings en la sección tut-docstrings.)
Hay herramientas que usan las docstrings para producir automáticamente documentación en línea o
imprimible, o para permitirle al usuario que navegue el código en forma interactiva; es una buena práctica
incluir docstrings en el código que uno escribe, por lo que se debe hacer un hábito de esto.
La ejecución de una función introduce una nueva tabla de símbolos usada para las variables locales de la
función. Más precisamente, todas las asignaciones de variables en la función almacenan el valor en la
tabla de símbolos local; así mismo la referencia a variables primero mira la tabla de símbolos local, luego
en la tabla de símbolos local de las funciones externas, luego la tabla de símbolos global, y finalmente la
tabla de nombres predefinidos. Así, no se les puede asignar directamente un valor a las variables globales
dentro de una función (a menos se las nombre en la sentencia global), aunque si pueden ser
referenciadas.
Los parámetros reales (argumentos) de una función se introducen en la tabla de símbolos local de la
función llamada cuando esta es ejecutada; así, los argumentos son pasados por valor (dónde el valor es
31
siempre una referencia a un objeto, no el valor del objeto). 2 Cuando una función llama a otra función, una
nueva tabla de símbolos local es creada para esa llamada.
La definición de una función introduce el nombre de la función en la tabla de símbolos actual. El valor del
nombre de la función tiene un tipo que es reconocido por el interprete como una función definida por el
usuario. Este valor puede ser asignado a otro nombre que luego puede ser usado como una función. Esto
sirve como un mecanismo general para renombrar:
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89
Viniendo de otros lenguajes, podés objetar que fib no es una función, sino un procedimiento, porque no
devuelve un valor. De hecho, técnicamente hablando, los procedimientos sí retornan un valor, aunque uno
aburrido. Este valor se llama None (es un nombre predefinido). El intérprete por lo general no escribe el
valor None si va a ser el único valor escrito. Si realmente se quiere, se puede verlo usando print:
>>> fib(0)
>>> print fib(0)
None
Es simple escribir una función que retorne una lista con los números de la serie de Fibonacci en lugar de
imprimirlos:
32
• La sentencia result.append(b) llama a un método del objeto lista result. Un método es una
función que 'pertenece' a un objeto y se nombra obj.methodname, dónde obj es algún objeto
(puede ser una expresión), y methodname es el nombre del método que está definido por el tipo
del objeto. Distintos tipos definen distintos métodos. Métodos de diferentes tipos pueden tener el
mismo nombre sin causar ambigüedad. (Es posible definir tipos de objetos propios, y métodos,
usando clases, mirá tut-classes). El método append() mostrado en el ejemplo está definido para
objetos lista; añade un nuevo elemento al final de la lista. En este ejemplo es equivalente a
result = result + [b], pero más eficiente.
Este ejemplo también introduce la palabra reservada in, la cual prueba si una secuencia contiene o no un
determinado valor.
Los valores por omisión son evaluados en el momento de la definición de la función, en el ámbito de la
definición, entonces:
33
i = 5
def f(arg=i):
print arg
i = 6
f()
...imprimirá 5.
Advertencia importante: El valor por omisión es evaluado solo una vez. Existe una diferencia cuando el
valor por omisión es un objeto mutable como una lista, diccionario, o instancia de la mayoría de las clases.
Por ejemplo, la siguiente función acumula los argumentos que se le pasan en subsiguientes llamadas:
print f(1)
print f(2)
print f(3)
Imprimirá:
[1]
[1, 2]
[1, 2, 3]
Si no se quiere que el valor por omisión sea compartido entre subsiguientes llamadas, se pueden escribir
la función así:
34
print "-- Gran plumaje tiene el", tipo
print "-- Esta", estado, "!"
loro(1000)
loro(accion='EXPLOTARRRRR', tension=1000000)
loro('mil', estado='boca arriba')
loro('un millon', 'rostizado', 'saltar')
En general, una lista de argumentos debe tener todos sus argumentos posicionales seguidos por los
argumentos nombrados, dónde las palabras claves deben ser elegidas entre los nombres de los
parámetros formales. No es importante si un parámetro formal tiene un valor por omisión o no. Ningún
argumento puede recibir un valor más de una vez (los nombres de parámetros formales correspondientes
a argumentos posicionales no pueden ser usados como palabras clave en la misma llamada). Aquí hay un
ejemplo que falla debido a esta restricción:
Cuando un parámetro formal de la forma **nombre está presente al final, recibe un diccionario (ver
typesmapping) conteniendo todos los argumentos nombrados excepto aquellos correspondientes a un
parámetro formal. Esto puede ser combinado con un parámetro formal de la forma *nombre (descripto en
la siguiente sección) que recibe una tupla conteniendo los argumentos posicionales además de la lista de
parámetros formales. (*nombre debe ocurrir antes de **nombre). Por ejemplo, si definimos una función
así:
35
for c in claves:
print c, ":", palabrasclaves[c]
-- ¿Tiene Limburger ?
-- Lo siento, nos quedamos sin Limburger
Es muy liquido, sr.
Realmente es muy muy liquido, sr.
----------------------------------------
cliente : Juan Garau
vendedor : Miguel Paez
puesto : Venta de Queso Argentino
Se debe notar que el método sort() de la lista de nombres de argumentos nombrados es llamado antes
de imprimir el contenido del diccionario palabrasclaves; si esto no se hace, el orden en que los
argumentos son impresos no está definido.
36
>>> range(3, 6) # llamada normal con argumentos separados
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args) # llamada con argumentos desempaquetados de una lista
[3, 4, 5]
Del mismo modo, los diccionarios pueden entregar argumentos nombrados con el operador **::
37
Cadenas de texto de documentación
Hay convenciones emergentes sobre el contenido y formato de las cadenas de texto de documentación.
La primer línea debe ser siempre un resumen corto y conciso del propósito del objeto. Para ser breve, no
se debe mencionar explícitamente el nombre o tipo del objeto, ya que estos están disponibles de otros
modos (excepto si el nombre es un verbo que describe el funcionamiento de la función). Esta línea debe
empezar con una letra mayúscula y terminar con un punto.
Si hay más líneas en la cadena de texto de documentación, la segunda línea debe estar en blanco,
separando visualmente el resumen del resto de la descripción. Las líneas siguientes deben ser uno o más
párrafos describiendo las convenciones para llamar al objeto, efectos secundarios, etc.
El analizador de Python no quita el sangrado de las cadenas de texto literales multi-líneas, entonces las
herramientas que procesan documentación tienen que quitarlo si así lo desean. Esto se hace mediante la
siguiente convención. La primer línea que no está en blanco siguiente a la primer línea de la cadena
determina la cantidad de sangría para toda la cadena de documentación. (No podemos usar la primer
línea ya que generalmente es adyacente a las comillas de apertura de la cadena y el sangrado no se nota
en la cadena de texto). Los espacios en blanco "equivalentes" a este sangrado son luego quitados del
comienzo de cada línea en la cadena. No deberían haber líneas con una sangría menor, pero si las hay
todos los espacios en blanco del comienzo deben ser quitados. La equivalencia de espacios en blanco
debe ser verificada luego de la expansión de tabs (a 8 espacios, normalmente).
38
Para Python, PEP 8 se erigió como la guía de estilo a la que más proyectos adhirieron; promueve un estilo
de codificación fácil de leer y visualmente agradable. Todos los desarrolladores Python deben leerlo en
algún momento; aquí están extraídos los puntos más importantes:
• Usar sangrías de 4 espacios, no tabs.
4 espacios son un buen compromiso entre una sangría pequeña (permite mayor nivel de
sangrado)y una sangría grande (más fácil de leer). Los tabs introducen confusión y es mejor
dejarlos de lado.
• Recortar las líneas para que no superen los 79 caracteres.
Esto ayuda a los usuarios con pantallas pequeñas y hace posible tener varios archivos de código
abiertos, uno al lado del otro, en pantallas grandes.
• Usar líneas en blanco para separar funciones y clases, y bloques grandes de código dentro de
funciones.
• Cuando sea posible, poner comentarios en una sola línea.
• Usar docstrings.
• Usar espacios alrededor de operadores y luego de las comas, pero no directamente dentro de
paréntesis: a = f(1, 2) + g(3, 4).
• Nombrar las clases y funciones consistentemente; la convención es usar NotacionCamello para
clases y minusculas_con_guiones_bajos para funciones y métodos. Siempre usá self
como el nombre para el primer argumento en los métodos (mirá tut-firstclasses para más
información sobre clases y métodos).
• No usar codificaciones estrafalarias si se espera usar el código en entornos internacionales. ASCII
plano funciona bien en la mayoría de los casos.
2 En realidad, llamadas por referencia de objeto sería una mejor descripción, ya que si se pasa
un objeto mutable, quien realiza la llamada verá cualquier cambio que se realice sobre el
mismo (por ejemplo ítems insertados en una lista).
39
Estructuras de datos
Este capítulo describe algunas cosas que ya aprendiste en más detalle, y agrega algunas cosas nuevas
también.
list.append(x)
Agrega un ítem al final de la lista; equivale a a[len(a):] = [x].
list.extend(L)
Extiende la lista agregándole todos los ítems de la lista dada; equivale a a[len(a):] = L.
list.insert(i,x)
Inserta un ítem en una posición dada. El primer argumento es el índice del ítem delante del cual se
insertará, por lo tanto a.insert(0, x) inserta al principio de la lista, y a.insert(len(a), x)
equivale a a.append(x).
list.remove(x)
Quita el primer ítem de la lista cuyo calor sea x. Es un error si no existe tal ítem.
list.pop([i])
Quita el ítem en la posición dada de la lista, y lo devuelve. Si no se especifica un índice, a.pop()
quita y devuelve el último ítem de la lista. (Los corchetes que encierran a i en la firma del método
denotan que el parámetro es opcional, no que deberías escribir corchetes en esa posición. Verás esta
notación con frecuencia en la Referencia de la Biblioteca de Python.)
list.index(x)
Devuelve el índice en la lista del primer ítem cuyo valor sea x. Es un error si no existe tal ítem.
list.count(x)
Devuelve el número de veces que x aparece en la lista.
list.sort()
Ordena los ítems de la lista, in situ.
list.reverse()
Invierte los elementos de la lista, in situ.
40
>>> a.append(333)
>>> a
[66.25, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.25, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.25]
>>> a.sort()
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]
41
Usando listas como colas
También puedes usar una lista convenientemente como una cola, donde el primer elemento añadido es el
primer elemento retirado ("primero en entrar, primero en salir"). Para agregar un ítem al final de la cola,
use append(). Para retirar un ítem del frente de la pila, use pop() con 0 como índice. Por ejemplo:
filter(funcion, secuencia) devuelve una secuencia con aquellos ítems de la secuencia para los
cuales funcion(item) es verdadero. Si secuencia es un string o tuple, el resultado será del
mismo tipo; de otra manera, siempre será list. Por ejemplo, para calcular unos números primos:
map(funcion, secuencia) llama a funcion(item) por cada uno de los ítems de la secuencia y
devuelve una lista de los valores retornados. Por ejemplo, para calcular unos cubos:
Se puede pasar más de una secuencia; la función debe entonces tener tantos argumentos como
secuencias haya y es llamada con el ítem correspondiente de cada secuencia (o None si alguna
secuencia es más corta que otra). Por ejemplo:
42
>>> map(add, sec, sec)
[0, 2, 4, 6, 8, 10, 12, 14]
reduce(funcion, secuencia) devuelve un único valor que se construye llamando a la función binaria
funcion con los primeros dos ítems de la secuencia, entonces con el resultado y el siguiente ítem, y así
sucesivamente. Por ejemplo, para calcular la suma de los números de 1 a 10:
Si sólo hay un ítem en la secuencia, se devuelve su valor; si la secuencia está vacía, se lanza una
excepción.
Un tercer argumento puede pasarse para indicar el valor inicial. En este caso el valor inicial se devuelve
para una secuencia vacía, y la función se aplica primero al valor inicial y el primer ítem de la secuencia,
entonces al resultado y al siguiente ítem, y así sucesivamente. Por ejemplo,
No uses la definición de este ejemplo de sum(): ya que la sumatoria es una necesidad tan común, se
provee una función integrada sum(secuencia) que funciona exactamente así.
Cada lista por comprensión consiste de una expresión seguida por una cláusula for, luego cero o más
cláusulas for o if. El resultado será una lista que resulta de evaluar la expresión en el contexto de las
cláusulas for y if que sigan. Si la expresión evalua a una tupla, debe encerrarse entre paréntesis.
>>> frutafresca = [' banana', ' mora de Logan ', 'maracuya ']
>>> [arma.strip() for arma in frutafresca]
['banana', 'mora de Logan', 'maracuya']
>>> vec = [2, 4, 6]
>>> [3*x for x in vec]
43
[6, 12, 18]
>>> [3*x for x in vec if x > 3]
[12, 18]
>>> [3*x for x in vec if x < 2]
[]
>>> [[x,x**2] for x in vec]
[[2, 4], [4, 16], [6, 36]]
>>> [x, x**2 for x in vec] # error - se requieren paréntesis para tuplas
...
[x, x**2 for x in vec]
^
SyntaxError: invalid syntax
>>> [(x, x**2) for x in vec]
[(2, 4), (4, 16), (6, 36)]
>>> vec1 = [2, 4, 6]
>>> vec2 = [4, 3, -9]
>>> [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54]
>>> [x+y for x in vec1 for y in vec2]
[6, 5, -7, 8, 7, -5, 10, 9, -3]
>>> [vec1[i]*vec2[i] for i in range(len(vec1))]
[8, 12, -54]
Las listas por comprensión son mucho más flexibles que map() y pueden aplicarse a expresiones
complejas y funciones anidadas:
Considera el siguiente ejemplo de una matriz de 3x3 como una lista que contiene tres listas, una por fila:
>>> mat = [
... [1, 2, 3],
... [4, 5, 6],
... [7, 8, 9],
... ]
Ahora, si quisieras intercambiar filas y columnas, podrías usar una lista por comprensión:
44
>>> print [[fila[i] for fila in mat] for i in [0, 1, 2]]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Para evitar aprensión cuando se anidan lista por comprensión, lee de derecha a izquierda.
Una versión más detallada de este retazo de código muestra el flujo de manera explícita:
En el mundo real, deberías preferir funciones predefinidas a declaraciones con flujo complejo. La función
zip() haría un buen trabajo para este caso de uso:
>>> zip(*mat)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
La instrucción del
Hay una manera de quitar un ítem de una lista dado su índice en lugar de su valor: la instrucción del.
Esta es diferente del método pop(), el cual devuelve un valor. La instrucción del también puede usarse
para quitar secciones de una lista o vaciar la lista completa (lo que hacíamos antes asignando una lista
vacía a la sección). Por ejemplo:
>>> del a
Hacer referencia al nombre a de aquí en más es un error (al menos hasta que se le asigne otro valor).
Veremos otros usos para del más adelante.
45
Tuplas y secuencias
Vimos que las listas y cadenas tienen propiedades en común, como el indizado y las operaciones de
seccionado. Estas son dos ejemplos de datos de tipo secuencia (ver typesseq). Como Python es un
lenguaje en evolución, otros datos de tipo secuencia pueden agregarse. Existe otro dato de tipo secuencia
estándar: la tupla.
Una tupla consiste de un número de valores separados por comas, por ejemplo:
Como puedes ver, en la salida las tuplas siempre se encierran entre paréntesis, para que las tuplas
anidadas puedan interpretarse correctamente; pueden ingresarse con o sin paréntesis, aunque a menudo
los paréntesis son necesarios de todas formas (si la tupla es parte de una expresión más grande).
Las tuplas tienen muchos usos. Por ejemplo: pares ordenados (x, y), registros de empleados de una base
de datos, etc. Las tuplas, al igual que las cadenas, son inmutables: no es posible asignar a los ítems
individuales de una tupla (aunque puedes simular bastante ese efecto mediante seccionado y
concatenación). También es posible crear tuplas que contengan objetos mutables como listas.
Un problema particular es la construcción de tuplas que contengan 0 o 1 ítem: la sintaxis presenta algunas
peculiaridades para estos casos. Las tuplas vacías se construyen mediante un par de paréntesis vacío;
una tupla con un ítem se construye poniendo una coma a continuación del valor (no alcanza con encerrar
un único valor entre paréntesis). Feo, pero efectivo. Por ejemplo:
>>> vacia = ()
>>> singleton = 'hola', # <-- notar la coma al final
>>> len(vacia)
0
>>> len(singleton)
1
>>> singleton
('hola',)
46
>>> x, y, z = t
Conjuntos
Python también incluye un tipo de dato para conjuntos. Un conjunto es una colección no ordenada y sin
elementos repetidos. Los usos básicos de éstos incluyen verificación de pertenencia y eliminación de
entradas duplicadas. Los conjuntos también soportan operaciones matemáticas como la unión,
intersección, diferencia, y diferencia simétrica.
>>> # veamos las operaciones para las letras únicas de dos palabras
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # letras únicas en a
set(['a', 'r', 'b', 'c', 'd'])
>>> a - b # letras en a pero no en b
set(['r', 'b', 'd'])
>>> a | b # letras en a o en b
set(['a', 'c', 'b', 'd', 'm', 'l', 'r', 'z'])
>>> a & b # letras en a y en b
set(['a', 'c'])
>>> a ^ b # letras en a o b pero no en ambos
set(['b', 'd', 'm', 'l', 'r', 'z'])
47
Diccionarios
Otro tipo de dato útil incluído en Python es el diccionario (ver typesmapping). Los diccionarios se
encuentran a veces en otros lenguajes como "memorias asociativas" o "arreglos asociativos". A diferencia
de las secuencias, que se indexan mediante un rango numérico, los diccionarios se indexan con claves,
que pueden ser cualquier tipo inmutable; las cadenas y números siempre pueden ser claves. Las tuplas
pueden usarse como claves si solamente contienen cadenas, números o tuplas; si una tupla contiene
cualquier objeto mutable directa o indirectamente, no puede usarse como clave. No podés usar listas
como claves, ya que las listas pueden modificarse usando asignación por índice, asignación por sección, o
métodos como append() y extend().
Lo mejor es pensar en un diccionario como un conjunto no ordenado de pares clave: valor, con el
requerimiento de que las claves sean únicas (dentro de un diccionario en particular). Un par de llaves
crean un diccionario vacío: {}. Colocar una lista de pares clave:valor separados por comas entre las
llaves añade pares clave:valor iniciales al diccionario; esta también es la forma en que los diccionarios se
presentan en la salida.
Las operaciones principales sobre un diccionario son guardar un valor con una clave y extraer ese valor
dada la clave. También es posible borrar un par clave:valor con del. Si usás una clave que ya está en uso
para guardar un valor, el valor que estaba asociado con esa clave se pierde. Es un error extraer un valor
usando una clave no existente.
El método keys() de un diccionario devuelve una lista de todas las claves en uso de ese diccionario, en
un orden arbitrario (si la querés ordenada, simplemente usá el metodo sort() sobre la lista de claves).
Para verificar si una clave está en el diccionario, utilizá la palabra clave in.
El constructor dict() crea un diccionario directamente desde listas de pares clave-valor guardados
como tuplas. Cuando los pares siguen un patrón, se puede especificar de forma compacta la lista de pares
clave-valor por comprensión.
48
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}
>>> dict([(x, x**2) for x in (2, 4, 6)]) # use a list comprehension
{2: 4, 4: 16, 6: 36}
Más adelante en este tutorial, aprenderemos acerca de Expresiones Generadoras que están mejor
preparadas para la tarea de proveer pares clave-valor al constructor dict().
Cuando las claves son cadenas simples, a veces resulta más fácil especificar los pares usando
argumentos por palabra clave:
Técnicas de iteración
Cuando iteramos sobre diccionarios, se pueden obtener al mismo tiempo la clave y su valor
correspondiente usando el método iteritems().
Cuando se itera sobre una secuencia, se puede obtener el índice de posición junto a su valor
correspondiente usando la función enumerate().
Para iterar sobre dos o más secuencias al mismo tiempo, los valores pueden emparejarse con la función
zip().
49
Cual es tu objetivo? el santo grial.
Cual es tu color favorito? azul.
Para iterar sobre una secuencia en orden inverso, se especifica primero la secuencia al derecho y luego
se llama a la función reversed().
Para iterar sobre una secuencia ordenada, se utiliza la función sorted() la cual devuelve una nueva
lista ordenada dejando a la original intacta.
Los operadores de comparación in y not in verifican si un valor está (o no está) en una secuencia.
Los operadores is e is not comparan si dos objetos son realmente el mismo objeto; esto es
significativo sólo para objetos mutables como las listas. Todos los operadores de comparación tienen la
misma prioridad, la cual es menor que la de todos los operadores numéricos.
Las comparaciones pueden encadenarse. Por ejemplo, a < b == c verifica si a es menor que b y
además si b es igual a c.
Las comparaciones pueden combinarse mediante los operadores booleanos and y or, y el resultado de
una comparación (o de cualquier otra expresión booleana) puede negarse con not. Estos tienen
prioridades menores que los operadores de comparación; entre ellos not tiene la mayor prioridad y or la
menor, o sea que A and not B or C equivale a (A and (not B)) or C. Como siempre, los
paréntesis pueden usarse para expresar la composición deseada.
50
Los operadores booleanos and y or son los llamados operadores cortocircuito: sus argumentos se
evalúan de izquierda a derecha, y la evaluación se detiene en el momento en que se determina su
resultado. Por ejemplo, si A y C son verdaderas pero B es falsa, en A and B and C no se evalúa la
expresión C. Cuando se usa como un valor general y no como un booleano, el valor devuelto de un
operador cortocircuito es el último argumento evaluado.
Es posible asignar el resultado de una comparación u otra expresión booleana a una variable. Por
ejemplo,
Notá que en Python, a diferencia de C, la asignación no puede ocurrir dentro de expresiones. Los
programadores de C pueden renegar por esto, pero es algo que evita un tipo de problema común
encontrado en programas en C: escribir = en una expresión cuando lo que se quiere escribir es ==.
Observá que comparar objetos de diferentes tipos es legal. El resultado es determinístico pero arbitrario:
los tipos se ordenan por su nombre. Por lo tanto, una lista (list) siempre evalúa como menor que una
cadena (string).
Los tipos numéricos diferentes se comparan a su valor numérico, o sea 0 es igual a 0.0, etc. No confiar
demasiado en las reglas para comparar objetos de diferentes tipos; pueden cambiar en una versión futura
del lenguaje.
51
Módulos
Si salís del intérprete de Python y entrás de nuevo, las definiciones que hiciste (funciones y variables) se
pierden. Por lo tanto, si querés escribir un programa más o menos largo, es mejor que uses un editor de
texto para preparar la entrada para el interprete y ejecutarlo con ese archivo como entrada. Esto es
conocido como crear un guión, o script. Si tu programa se vuelve más largo, quizás quieras separarlo en
distintos archivos para un mantenimiento más fácil. Quizás también quieras usar una función útil que
escribiste desde distintos programas sin copiar su definición a cada programa.
Para soportar esto, Python tiene una manera de poner definiciones en un archivo y usarlos en un script o
en una instancia interactiva del intérprete. Tal archivo es llamado módulo; las definiciones de un módulo
pueden ser importadas a otros módulos o al módulo principal (la colección de variables a las que tenés
acceso en un script ejecutado en el nivel superior y en el modo calculadora).
Un módulo es una archivo conteniendo definiciones y declaraciones de Python. El nombre del archivo es
el nombre del módulo con el sufijo .py agregado. Dentro de un módulo, el nombre del mismo (como una
cadena) está disponible en el valor de la variable global __name__. Por ejemplo, usá tu editor de textos
favorito para crear un archivo llamado fibo.py en el directorio actual, con el siguiente contenido:
Ahora entrá al intérprete de Python e importá este módulo con la siguiente orden:
Esto no mete los nombres de las funciones definidas en fibo directamente en el espacio de nombres
actual; sólo mete ahí el nombre del módulo, fibo. Usando el nombre del módulo podés acceder a las
funciones:
>>> fibo.fib(1000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
52
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'
Cada módulo tiene su propio espacio de nombres, el que es usado como espacio de nombres global por
todas las funciones definidas en el módulo. Por lo tanto, el autor de un módulo puede usar variables
globales en el módulo sin preocuparse acerca de conflictos con una variable global del usuario. Por otro
lado, si sabés lo que estás haciendo podés tocar las variables globales de un módulo con la misma
notación usada para referirte a sus funciones, nombremodulo.nombreitem.
Los módulos pueden importar otros módulos. Es costumbre pero no obligatorio el ubicar todas las
declaraciones import al principio del módulo (o script, para el caso). Los nombres de los módulos
importados se ubican en el espacio de nombres global del módulo que hace la importación.
Hay una variante de la declaración import que importa los nombres de un módulo directamente al
espacio de nombres del módulo que hace la importación. Por ejemplo:
Esto no introduce en el espacio de nombres local el nombre del módulo desde el cual se está importando
(entonces, en el ejemplo, fibo no se define).
Hay incluso una variante para importar todos los nombres que un módulo define:
Esto importa todos los nombres excepto aquellos que comienzan con un subrayado (_).
53
Nota
Por razones de eficiencia, cada módulo se importa una vez por sesión del intérprete. Por lo tanto, si
modificás los módulos, tenés que reiniciar el intérprete -- o, si es sólo un módulo que querés probar
interactivamente, usá reload(), por ejemplo reload(nombremodulo).
...el código en el módulo será ejecutado, tal como si lo hubieses importado, pero con __name__ con el
valor de "__main__". Eso significa que agregando este código al final de tu módulo:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
...podés hacer que el archivo sea utilizable tanto como script, como módulo importable, porque el código
que analiza la linea de órdenes sólo se ejecuta si el módulo es ejecutado como archivo principal:
$ python fibo.py 50
1 1 2 3 5 8 13 21 34
Esto es frecuentemente usado para proveer al módulo una interfaz de usuario conveniente, o para
propósitos de prueba (ejecutar el módulo como un script ejecuta el juego de pruebas).
En realidad, los módulos se buscan en la lista de directorios dada por la variable sys.path, la cual se
inicializa con el directorio que contiene al script de entrada (o el directorio actual), PYTHONPATH, y el
54
directorio default dependiente de la instalación. Esto permite que los programas en Python que saben lo
que están haciendo modifiquen o reemplacen el camino de búsqueda de los módulos. Notar que como el
directorio que contiene el script que se ejecuta está en el camino de búsqueda, es importante que el script
no tenga el mismo nombre que un módulo estándar, o Python intentará cargar el script como un módulo
cuando ese módulo se importe. Esto generalmente será un error. Mirá la sección tut-standardmodules
para más información.
Normalmente, no necesitás hacer nada para crear el archivo spam.pyc. Siempre que se compile
satisfactoriamente el spam.py, se hace un intento de escribir la versión compilada al spam.pyc. No es un
error si este intento falla, si por cualquier razón el archivo no se escribe completamente el archivo
spam.pyc resultante se reconocerá como inválido luego. El contenido del archivo spam.pyc es
independiente de la plataforma, por lo que un directorio de módulos puede ser compartido por máquinas
de diferentes arquitecturas.
55
• El módulo compileall puede crear archivos .pyc (o archivos .pyo cuando se usa la opción
-O) para todos los módulos en un directorio.
Módulos estándar
Python viene con una biblioteca de módulos estándar, descrita en un documento separado, la Referencia
de la Biblioteca de Python (de aquí en más, "Referencia de la Biblioteca"). Algunos módulos se integran
en el intérprete; estos proveen acceso a operaciones que no son parte del núcleo del lenguaje pero que
sin embargo están integrados, tanto por eficiencia como para proveer acceso a primitivas del sistema
operativo, como llamadas al sistema. El conjunto de tales módulos es una opción de configuración el cual
también depende de la plataforma subyacente. Por ejemplo, el módulo winreg sólo se provee en
sistemas Windows. Un módulo en particular merece algo de atención: sys, el que está integrado en todos
los intérpretes de Python. Las variables sys.ps1 y sys.ps2 definen las cadenas usadas como cursores
primarios y secundarios:
Estas dos variables están solamente definidas si el intérprete está en modo interactivo.
La variable sys.path es una lista de cadenas que determinan el camino de búsqueda del intérprete para
los módulos. Se inicializa por omisión a un camino tomado de la variable de entorno PYTHONPATH, o a
un valor predefinido en el intérprete si PYTHONPATH no está configurada. Lo podés modificar usando las
operaciones estándar de listas:
La función dir()
La función integrada dir() se usa para encontrar qué nombres define un módulo. Devuelve una lista
ordenada de cadenas:
56
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__',
'__stdin__', '__stdout__', '_getframe', 'api_version', 'argv',
'builtin_module_names', 'byteorder', 'callstats', 'copyright',
'displayhook', 'exc_clear', 'exc_info', 'exc_type', 'excepthook',
'exec_prefix', 'executable', 'exit', 'getdefaultencoding', 'getdlopenflags',
'getrecursionlimit', 'getrefcount', 'hexversion', 'maxint', 'maxunicode',
'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache',
'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setdlopenflags',
'setprofile', 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout',
'version', 'version_info', 'warnoptions']
Sin argumentos, dir() lista los nombres que tenés actualmente definidos:
>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__doc__', '__file__', '__name__', 'a', 'fib', 'fibo', 'sys']
Notá que lista todos los tipos de nombres: variables, módulos, funciones, etc.
dir() no lista los nombres de las funciones y variables integradas. Si querés una lista de esos, están
definidos en el módulo estándar __builtin__:
57
'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter',
'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview',
'min', 'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range',
'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set',
'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
Paquetes
Los paquetes son una manera de estructurar los espacios de nombres de Python usando "nombres de
módulos con puntos". Por ejemplo, el nombre de módulo A.B designa un submódulo llamado B en un
paquete llamado A. Tal como el uso de módulos evita que los autores de diferentes módulos tengan que
preocuparse de los respectivos nombres de variables globales, el uso de nombres de módulos con puntos
evita que los autores de paquetes de muchos módulos, como NumPy o la Biblioteca de Imágenes de
Python (Python Imaging Library, o PIL), tengan que preocuparse de los respectivos nombres de módulos.
Suponete que querés designar una colección de módulos (un "paquete") para el manejo uniforme de
archivos y datos de sonidos. Hay diferentes formatos de archivos de sonido (normalmente reconocidos por
su extensión, por ejemplo: .wav, .aiff, .au), por lo que tenés que crear y mantener una colección
siempre creciente de módulos para la conversión entre los distintos formatos de archivos. Hay muchas
operaciones diferentes que quizás quieras ejecutar en los datos de sonido (como mezclarlos, añadir eco,
aplicar una función ecualizadora, crear un efecto estéreo artificial), por lo que ademas estarás escribiendo
una lista sin fin de módulos para realizar estas operaciones. Aquí hay una posible estructura para tu
paquete (expresados en términos de un sistema jerárquico de archivos):
58
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
Al importar el paquete, Python busca a través de los directorios en sys.path, buscando el subdirectorio
del paquete.
Los archivos __init__.py se necesitan para hacer que Python trate los directorios como que contienen
paquetes; esto se hace para prevenir directorios con un nombre común, como string, de esconder sin
intención a módulos válidos que se suceden luego en el camino de búsqueda de módulos. En el caso más
simple, __init__.py puede ser solamente un archivo vacío, pero también puede ejecutar código de
inicialización para el paquete o configurar la variable __all__, descrita luego.
Los usuarios del paquete pueden importar módulos individuales del mismo, por ejemplo:
import sound.effects.echo
Esto carga el submódulo sound.effects.echo. Debe hacerse referencia al mismo con el nombre
completo.
Esto también carga el submódulo echo, lo deja disponible sin su prefijo de paquete, por lo que puede
usarse así:
De nuevo, esto carga el submódulo echo, pero deja directamente disponible a la función echofilter():
Notá que al usar from package import item el ítem puede ser tanto un submódulo (o subpaquete)
del paquete, o algún otro nombre definido en el paquete, como una función, clase, o variable. La
declaración import primero verifica si el ítem está definido en el paquete; si no, asume que es un
módulo y trata de cargarlo. Si no lo puede encontrar, se genera una excepción ImportError.
Por otro lado, cuando se usa la sintaxis como import item.subitem.subsubitem, cada ítem excepto
el último debe ser un paquete; el mismo puede ser un módulo o un paquete pero no puede ser una clase,
función o variable definida en el ítem previo.
59
Importando * desde un paquete
Ahora, ¿qué sucede cuando el usuario escribe from sound.effects import *? Idealmente, uno
esperaría que esto de alguna manera vaya al sistema de archivos, encuentre cuales submódulos están
presentes en el paquete, y los importe a todos. Desafortunadamente, esta operación no funciona muy bien
en las plataformas Windows, donde el sistema de archivos no siempre tiene información precisa sobre
mayúsculas y minúsculas. En estas plataformas, no hay una manera garantizada de saber si el archivo
ECHO.PY debería importarse como el módulo echo, Echo o ECHO. (Por ejemplo, Windows 95 tiene la
molesta costumbre de mostrar todos los nombres de archivos con la primer letra en mayúsculas.) La
restricción de DOS de los nombres de archivos con la forma 8+3 agrega otro problema interesante para
los nombres de módulos largos.
La única solución es que el autor del paquete provea un índice explícito del paquete. La declaración
import usa la siguiente convención: si el código del __init__.py de un paquete define una lista
llamada __all__, se toma como la lista de los nombres de módulos que deberían ser importados cuando
se hace from package import *. Es tarea del autor del paquete mantener actualizada esta lista
cuando se libera una nueva versión del paquete. Los autores de paquetes podrían decidir no soportarlo, si
no ven un uso para importar * en sus paquetes. Por ejemplo, el archivo
sounds/effects/__init__.py podría contener el siguiente código:
Esto significaría que from sound.effects import * importaría esos tres submódulos del paquete
sound.
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
En este ejemplo, los módulos echo y surround se importan en el espacio de nombre actual porque están
definidos en el paquete sound.effects cuando se ejecuta la declaración from...import. (Esto
también funciona cuando se define __all__).
Notá que en general la práctica de importar * desde un módulo o paquete no se recomienda, ya que
frecuentemente genera un código con mala legibilidad. Sin embargo, está bien usarlo para ahorrar tecleo
en sesiones interactivas, y algunos módulos están diseñados para exportar sólo nombres que siguen
ciertos patrones.
60
Recordá que no está mal usar from paquete import submodulo_especifico! De hecho, esta
notación se recomienda a menos que el módulo que estás importando necesite usar submódulos con el
mismo nombre desde otros paquetes.
Cuando se estructuran los paquetes en subpaquetes (como en el ejemplo sound), podés usar import
absolutos para referirte a submódulos de paquetes hermanos. Por ejemplo, si el módulo
sound.filters.vocoder necesita usar el módulo echo en el paquete sound.effects, puede hacer
from sound.effects import echo.
Desde Python 2.5, además de los import relativos implícitos descritos arriba, podés escribir import
relativos explícitos con la declaración de la forma from module import name. Estos import relativos
explícitos usan puntos adelante para indicar los paquetes actual o padres involucrados en el import
relativo. En el ejemplo surround, podrías hacer:
Notá que ambos import, relativos explícitos e implícitos, se basan en el nombre del módulo actual. Ya
que el nombre del módulo principal es siempre "__main__", los módulos pensados para usarse como
módulo principal de una aplicación Python siempre deberían usar import absolutos.
Aunque esta característica no se necesita frecuentemente, puede usarse para extender el conjunto de
módulos que se encuentran en el paquete.
61
3 De hecho las definiciones de función son también 'declaraciones' que se 'ejecutan'; la
ejecución de una función a nivel de módulo mete el nombre de la función en el espacio de
nombres global.
62
Entrada y salida
Hay diferentes métodos de presentar la salida de un programa; los datos pueden ser impresos de una
forma legible por humanos, o escritos a un archivo para uso futuro. Este capítulo discutirá algunas de las
posibilidades.
Frecuentemente querrás más control sobre el formateo de tu salida que simplemente imprimir valores
separados por espacios. Hay dos maneras de formatear tu salida; la primera es hacer todo el manejo de
las cadenas vos mismo: usando rebanado de cadenas y operaciones de concatenado podés crear
cualquier forma que puedas imaginar. El módulo string contiene algunas operaciones útiles para
emparejar cadenas a un determinado ancho; estas las discutiremos en breve. La otra forma es usar el
método str.format().
Nos queda una pregunta, por supuesto: ¿cómo convertís valores a cadenas? Afortunadamente, Python
tiene maneras de convertir cualquier valor a una cadena: pasalos a las funciones repr() o str().
La función str() devuelve representaciones de los valores que son bastante legibles por humanos,
mientras que repr() genera representaciones que pueden ser leídas por el el intérprete (o forzarían un
SyntaxError si no hay sintáxis equivalente). Para objetos que no tienen una representación en particular
para consumo humano, str() devolverá el mismo valor que repr(). Muchos valores, como números o
estructuras como listas y diccionarios, tienen la misma representación usando cualquiera de las dos
funciones. Las cadenas y los números de punto flotante, en particular, tienen dos representaciones
distintas.
Algunos ejemplos:
63
>>> s = 'El valor de x es ' + repr(x) + ', y es ' + repr(y) + '...'
>>> print s
El valor de x es 32.5, y es 40000...
>>> # El repr() de una cadena agrega apóstrofos y barras invertidas
... hola = 'hola mundo\n'
>>> holas = repr(hola)
>>> print holas
'hola mundo\n'
>>> # El argumento de repr() puede ser cualquier objeto Python:
... repr((x, y, ('carne', 'huevos')))
"(32.5, 40000, ('carne', 'huevos'))"
64
(Notar que en el primer ejemplo, un espacio entre cada columna fue agregado por la manera en que
print trabaja: siempre agrega espacios entre sus argumentos)
Este ejemplo muestra el método rjust() de los objetos cadena, el cual ordena una cadena a la derecha
en un campo del ancho dado llenándolo con espacios a la izquierda. Hay métodos similares ljust() y
center(). Estos métodos no escriben nada, sólo devuelven una nueva cadena. Si la cadena de entrada
es demasiado larga, no la truncan, sino la devuelven intacta; esto te romperá la alineación de tus
columnas pero es normalmente mejor que la alternativa, que te estaría mintiendo sobre el valor. (Si
realmente querés que se recorte, siempre podés agregarle una operación de rebanado, como en
x.ljust(n)[:n].)
Hay otro método, zfill(), el cual rellena una cadena numérica a la izquierda con ceros. Entiende signos
positivos y negativos:
>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'
Las llaves y caracteres dentro de las mismas (llamados campos de formato) son reemplazadas con los
objetos pasados en el método str.format(). El número en las llaves se refiere a la posición del objeto
pasado en el método.
Si se usan argumentos nombrados en el método str.format(), sus valores serán referidos usando el
nombre del argumento.
65
Un ': y especificador de formato opcionales pueden ir luego del nombre del campo. Esto aumenta el
control sobre cómo el valor es formateado. El siguiente ejemplo trunca Pi a tres lugares luego del punto
decimal.
Pasando un entero luego del ':' causará que que el campo sea de un mínimo número de caracteres de
ancho. Esto es útil para hacer tablas lindas.
Si tenés una cadena de formateo realmente larga que no querés separar, podría ser bueno que puedas
hacer referencia a las variables a ser formateadas por el nombre en vez de la posición. Esto puede
hacerse simplemente pasando el diccionario y usando corchetes '[]' para acceder a las claves
Esto se podría también hacer pasando la tabla como argumentos nombrados con la notación '**'.
Esto es particularmente útil en combinación con la nueva función integrada vars(), que devuelve un
diccionario conteniendo todas las variables locales.
Para una completa descripción del formateo de cadenas con str.format(), mirá en formatstrings.
66
El valor de PI es aproximadamente 3.142.
Ya que str.format() es bastante nuevo, un montón de código Python todavía usa el operador %. Sin
embargo, ya que este viejo estilo de formateo será eventualmente eliminado del lenguaje, en general
debería usarse str.format().
El primer argumento es una cadena conteniendo el nombre de archivo. El segundo argumento es otra
cadena conteniendo unos pocos caracteres que describen la forma en que el archivo será usado. El modo
puede ser 'r' cuando el archivo será solamente leído, 'w' para sólo escribirlo (un archivo existente con
el mismo nombre será borrado), y 'a' abre el archivo para agregarle información; cualquier dato escrito
al archivo será automáticamente agregado al final. 'r+' abre el archivo tanto para leerlo como para
escribirlo. El argumento modo es opcional; si se omite se asume 'r'.
En Windows, agregando 'b' al modo abre al archivo en modo binario, por lo que también hay modos
como 'rb', 'wb', y 'r+b'. Windows hace una distinción entre archivos binarios y de texto; los
caracteres de fin de linea en los archivos de texto son automáticamente alterados levemente cuando los
datos son leídos o escritos. Esta modificación en bambalinas para guardar datos está bien con archivos de
texto ASCII, pero corromperá datos binarios como en archivos JPEG o EXE. Sé muy cuidadoso en usar el
modo binario al leer y escribir tales archivos. En Unix, no hay problema en agregarle una 'b' al modo,
por lo que podés usarlo independientemente de la plataforma para todos los archivos binarios.
Para leer el contenido de una archivo llamá a f.read(cantidad), el cual lee alguna cantidad de datos y
los devuelve como una cadena. cantidad es un argumento numérico opcional. Cuando se omite cantidad o
es negativo, el contenido entero del archivo será leido y devuelto; es tu problema si el archivo es el doble
de grande que la memoria de tu máquina. De otra manera, a lo sumo una cantidad de bytes son leídos y
devueltos. Si se alcanzó el fin del archivo, f.read() devolverá una cadena vacía ("").
>>> f.read()
'Este es el archivo entero.\n'
>>> f.read()
67
''
f.readline() lee una sola linea del archivo; el caracter de fin de linea (\n) se deja al final de la cadena,
y sólo se omite en la última linea del archivo si el mismo no termina en un fin de linea. Esto hace que el
valor de retorno no sea ambiguo; si f.readline() devuelve una cadena vacía, es que se alcanzó el fin
del archivo, mientras que una linea en blanco es representada por '\n', una cadena conteniendo sólo un
único fin de linea.
>>> f.readline()
'Esta es la primer linea del archivo.\n'
>>> f.readline()
'Segunda linea del archivo\n'
>>> f.readline()
''
f.readlines() devuelve una lista conteniendo todos las lineas de datos en el archivo. Si se da un
parámetro opcional size, lee esa cantidad de bytes del archivo y lo suficientemente más como para
completar una linea, y devuelve las lineas de eso. Esto se usa frecuentemente para permitir una lectura
por lineas de forma eficiente en archivos grandes, sin tener que cargar el archivo entero en memoria. Sólo
lineas completas serán devueltas.
>>> f.readlines()
['Esta es la primer linea del archivo.\n', 'Segunda linea del archivo\n']
Una forma alternativa a leer lineas es iterar sobre el objeto archivo. Esto es eficiente en memoria, rápido, y
conduce a un código más simple:
El enfoque alternativo es mucho más simple pero no permite un control fino. Como los dos enfoques
manejan diferente el buffer de lineas, no deberían mezclarse.
Para escribir algo más que una cadena, necesita convertirse primero a una cadena:
f.tell() devuelve un entero que indica la posición actual en el archivo, medida en bytes desde el
comienzo del archivo. Para cambiar la posición usá f.seek(desplazamiento, desde_donde). La
68
posición es calculada agregando el desplazamiento a un punto de referencia; el punto de referencia se
selecciona del argumento desde_donde. Un valor desde_donde de 0 mide desde el comienzo del archivo,
1 usa la posición actual del archivo, y 2 usa el fin del archivo como punto de referencia. desde_donde
puede omitirse, el default es 0, usando el comienzo del archivo como punto de referencia.
Cuando hayas terminado con un archivo, llamá a f.close() para cerrarlo y liberar cualquier recurso del
sistema tomado por el archivo abierto. Luego de llamar f.close(), los intentos de usar el objeto archivo
fallarán automáticamente.
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file
Es una buena práctica usar la declaración with cuando manejamos objetos archivo. Tiene la ventaja que
el archivo es cerrado apropiadamente luego de que el bloque termina, incluso si se generó una excepción.
También es mucho más corto que escribir los equivalentes bloques try-finally
Los objetos archivo tienen algunos métodos más, como isatty() y truncate() que son usados
menos frecuentemente; consultá la Referencia de la Biblioteca para una guía completa sobre los objetos
archivo.
El módulo pickle
Las cadenas pueden facilmente escribirse y leerse de un archivo. Los números toman algo más de
esfuerzo, ya que el método read() sólo devuelve cadenas, que tendrán que ser pasadas a una función
como int(), que toma una cadena como '123' y devuelve su valor numérico 123. Sin embargo,
cuando querés grabar tipos de datos más complejos como listas, diccionarios, o instancias de clases, las
cosas se ponen más complicadas.
69
En lugar de tener a los usuarios constantemente escribiendo y debugueando código para grabar tipos de
datos complicados, Python provee un módulo estándar llamado pickle. Este es un asombroso módulo
que puede tomar casi cualquier objeto Python (¡incluso algunas formas de código Python!), y convertirlo a
una representación de cadena; este proceso se llama picklear. Reconstruir los objetos desde la
representación en cadena se llama despicklear. Entre que se picklea y se despicklea, la cadena que
representa al objeto puede almacenarse en un archivo, o enviarse a una máquina distante por una
conexión de red.
Si tenés un objeto x, y un objeto archivo f que fue abierto para escritura, la manera más simple de
picklear el objeto toma una sola linea de código:
pickle.dump(x, f)
Para despicklear el objeto, si f es un objeto archivo que fue abierto para lectura:
x = pickle.load(f)
(Hay otras variantes de esto, usadas al picklear muchos objetos o cuando no querés escribir los datos
pickleados a un archivo; consultá la documentación completa para pickle en la Referencia de la
Biblioteca de Python.)
pickle es la manera estándar de hacer que los objetos Python puedan almacenarse y reusarse por otros
programas o por una futura invocación al mismo programa; el término técnico de esto es un objeto
persistente. Ya que pickle es tan ampliamente usado, muchos autores que escriben extensiones de
Python toman el cuidado de asegurarse que los nuevos tipos de datos, como matrices, puedan ser
adecuadamente pickleados y despickleados.
70
Errores y excepciones
Hasta ahora los mensajes de error no habían sido más que mencionados, pero si probaste los ejemplos
probablemente hayas visto algunos. Hay (al menos) dos tipos diferentes de errores: errores de sintaxis y
excepciones.
Errores de sintaxis
Los errores de sintaxis, también conocidos como errores de interpretación, son quizás el tipo de queja
más común que tenés cuando todavía estás aprendiendo Python:
El intérprete repite la línea culpable y muestra una pequeña 'flecha' que apunta al primer lugar donde se
detectó el error. Este es causado por (o al menos detectado en) el símbolo que precede a la flecha: en el
ejemplo, el error se detecta en el print, ya que faltan dos puntos (':') antes del mismo. Se muestran el
nombre del archivo y el número de línea para que sepas dónde mirar en caso de que la entrada venga de
un programa.
Excepciones
Incluso si la declaración o expresión es sintácticamente correcta, puede generar un error cuando se
intenta ejecutarla. Los errores detectados durante la ejecución se llaman excepciones, y no son
incondicionalmente fatales: pronto aprenderás cómo manejarlos en los programas en Python. Sin
embargo, la mayoría de las excepciones no son manejadas por los programas, y resultan en mensajes de
error como los mostrados aquí:
>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero
>>> 4 + spam*3
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
File "<stdin>", line 1, in ?
71
TypeError: cannot concatenate 'str' and 'int' objects
La última línea de los mensajes de error indica qué sucedió. Las excepciones vienen de distintos tipos, y
el tipo se imprime como parte del mensaje: los tipos en el ejemplo son: ZeroDivisionError,
NameError y TypeError. La cadena mostrada como tipo de la excepción es el nombre de la excepción
predefinida que ocurrió. Esto es verdad para todas las excepciones predefinidas del intérprete, pero no
necesita ser verdad para excepciones definidas por el usuario (aunque es una convención útil). Los
nombres de las excepciones estándar son identificadores incorporados al intérprete (no son palabras
clave reservadas).
La parte anterior del mensaje de error muestra el contexto donde la excepción sucedió, en la forma de un
trazado del error listando líneas fuente; sin embargo, no mostrará líneas leídas desde la entrada estándar.
Manejando excepciones
Es posible escribir programas que manejen determinadas excepciones. Mirá el siguiente ejemplo, que le
pide al usuario una entrada hasta que ingrese un entero válido, pero permite al usuario interrumpir el
programa (usando Control-C o lo que sea que el sistema operativo soporte); notá que una interrupción
generada por el usuario se señaliza generando la excepción KeyboardInterrupt.
Una declaración try puede tener más de un except, para especificar manejadores para distintas
excepciones. A lo sumo un manejador será ejecutado. Sólo se manejan excepciones que ocurren en el
72
correspondiente try, no en otros manejadores del mismo try. Un except puede nombrar múltiples
excepciones usando paréntesis, por ejemplo:
El último except puede omitir nombrar qué excepción captura, para servir como comodín. Usá esto con
extremo cuidado, ya que de esta manera es fácil ocultar un error real de programación. También puede
usarse para mostrar un mensaje de error y luego re-generar la excepción (permitiéndole al que llama,
manejar también la excepción):
import sys
try:
f = open('miarchivo.txt')
s = f.readline()
i = int(s.strip())
except IOError as (errno, strerror):
print "Error E/S ({0}): {1}".format(errno, strerror)
except ValueError:
print "No pude convertir el dato a un entero."
except:
print "Error inesperado:", sys.exc_info()[0]
raise
Las declaraciones try ... except tienen un bloque else opcional, el cual, cuando está presente, debe
seguir a los except. Es útil para aquel código que debe ejecutarse si el bloque try no genera una
excepción. Por ejemplo:
El uso de else es mejor que agregar código adicional en el try porque evita capturar accidentalmente
una excepción que no fue generada por el código que está protegido por la declaración try ... except.
Cuando ocurre una excepción, puede tener un valor asociado, también conocido como el argumento de la
excepción. La presencia y el tipo de argumento depende del tipo de excepción.
El except puede especificar una variable luego del nombre (o tupla) de excepción(es). La variable se
vincula a una instancia de excepción con los argumentos almacenados en instance.args. Por
conveniencia, la instancia de excepción define __getitem__() y __str__() para que se pueda
73
acceder o mostrar los argumentos directamente, sin necesidad de hacer referencia a .args.
Pero se recomienda no usar .args. En cambio, el uso preferido es pasar un único argumento a la
excepción (que puede ser una tupla se necesitan varios argumentos) y vincularlo al atributo message.
Uno también puede instanciar una excepción antes de generarla, y agregarle cualquier atributo que se
desee:
>>> try:
... raise Exception('carne', 'huevos')
... except Exception as inst:
... print type(inst) # la instancia de excepción
... print inst.args # argumentos guardados en .args
... print inst # __str__ permite imprimir args directamente
... x, y = inst # __getitem__ permite usar args directamente
... print 'x =', x
... print 'y =', y
...
<type 'exceptions.Exception'>
('carne', 'huevos')
('carne', 'huevos')
x = carne
y = huevos
Si una excepción tiene un argumento, este se imprime como la última parte (el 'detalle') del mensaje para
las excepciones que no están manejadas.
Los manejadores de excepciones no manejan solamente las excepciones que ocurren en el bloque try,
también manejan las excepciones que ocurren dentro de las funciones que se llaman (inclusive
indirectamente) dentro del bloque try. Por ejemplo:
74
Levantando excepciones
La declaración raise permite al programador forzar a que ocurra una excepción específica. Por ejemplo:
El argumento de raise es una clase o instancia de excepción a ser generada. Hay una sintaxis
alternativa que no se usa más, que separa los argumentos de clase y constructor; lo de arriba podría
escribirse como raise NameError, 'Hola'; ya que alguna vez era la única opción, esta forma es muy
usada en códigos viejos.
Si necesitás determinar cuando una excepción fue lanzada pero no querés manejarla, una forma
simplificada de la instrucción raise te permite relanzarla:
>>> try:
... raise NameError('Hola')
... except NameError:
... print u'Voló una excepción!'
... raise
...
Voló una excpeción!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: Hola
75
Ocurrió mi excepción, valor: 4
>>> raise MiError('oops!')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
__main__.MiError: 'oops!'
Las clases de Excepciones pueden ser definidas de la misma forma que cualquier otra clase, pero
usualmente se mantienen simples, a menudo solo ofreciendo un número de atributos con información
sobre el error que leerán los manejadores de la excepción. Al crear un módulo que puede lanzar varios
errores distintos, una práctica común es crear una clase base para excepciones definidas en ese módulo y
extenderla para crear clases excepciones específicas para distintas condiciones de error:
class Error(Exception):
"""Clase base para excepciones en el modulo."""
pass
class EntradaError(Error):
"""Excepcion lanzada por errores en las entradas.
Atributos:
expresion -- expresion de entrada en la que ocurre el error
mensaje -- explicacion del error
"""
class TransicionError(Error):
"""Lanzada cuando una operacion intenta una transicion de estado no
permitida.
Atributos:
previo -- estado al principio de la transicion
siguiente -- nuevo estado intentado
mensaje -- explicacion de porque la transicion no esta permitida
"""
def __init__(self, previo, siguiente, mensaje):
self.previo = previo
self.siguiente = siguiente
76
self.mensaje = mensaje
La mayoría de las excepciones son definidas con nombres que terminan en "Error", similares a los
nombres de las excepciones estándar.
Muchos módulos estándar definen sus propias excepciones para reportar errores que pueden ocurrir en
funciones propias. Se puede encontrar más información sobre clases en el capítulo tut-classes.
>>> try:
... raise KeyboardInterrupt
... finally:
... print 'Chau, mundo!'
...
Chau, mundo!
KeyboardInterrupt
Una cláusula finally siempre es ejecutada antes de salir de la declaración try, ya sea que una excepción
haya ocurrido o no. Cuando ocurre una excepción en la cláusula try y no fue manejada por una cláusula
except (o ocurrió en una cláusula except o else), es relanzada luego de que se ejecuta la cláusula
finally. finally es también ejecutada "a la salida" cuando cualquier otra cláusula de la declaración
try es dejada via break, continue or return. Un ejemplo más complicado (cláusulas except y
finally en la misma declaración try):
77
>>> divide("2", "1")
ejecutando la clausula finally
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
Como podés ver, la cláusula finally es ejecutada siempre. La excepción TypeError lanzada al dividir
dos cadenas de texto no es manejado por la cláusula except y por lo tanto es relanzada luego de que se
ejecuta la cláusula finally.
En aplicaciones reales, la cláusula finally es útil para liberar recursos externos (como archivos o
conexiones de red), sin importar si el uso del recurso fue exitoso.
El problema con este código es que deja el archivo abierto por un periodo de tiempo indeterminado luego
de que termine de ejecutarse. Esto no es un problema en scripts simples, pero puede ser un problema en
aplicaciones más grandes. La declaración with permite que objetos como archivos sean usados de una
forma que asegure que siempre se los libera rápido y en forma correcta.
with open("miarchivo.txt") as f:
for linea in f:
print linea
Luego de que la declaración sea ejecutada, el archivo f siempre es cerrado, incluso si se encuentra un
problema al procesar las líneas. Otros objetos que provean acciones de limpieza predefinidas lo indicarán
en su documentación.
78
Clases
El mecanismo de clases de Python agrega clases al lenguaje con un mínimo de nuevas sintaxis y
semánticas. Es una mezcla de los mecanismos de clase encontrados en C++ y Modula-3. Como es cierto
para los módulos, las clases en Python no ponen una barrera absoluta entre la definición y el usuario, sino
que más bien se apoya en la cortesía del usuario de no "forzar la definición". Sin embargo, se mantiene el
poder completo de las características más importantes de las clases: el mecanismo de la herencia de
clases permite múltiples clases base, una clase derivada puede sobreescribir cualquier método de su(s)
clase(s) base, y un método puede llamar al método de la clase base con el mismo nombre. Los objetos
pueden tener una cantidad arbitraria de datos.
En terminología de C++, todos los miembros de las clases (incluyendo los miembros de datos), son
públicos, y todas las funciones miembro son virtuales. Como en Modula-3, no hay atajos para hacer
referencia a los miembros del objeto desde sus métodos: la función método se declara con un primer
argumento explícito que representa al objeto, el cual se provee implícitamente por la llamada. Como en
Smalltalk, las clases mismas son objetos. Esto provee una semántica para importar y renombrar. A
diferencia de C++ y Modula-3, los tipos de datos integrados pueden usarse como clases base para que el
usuario los extienda. También, como en C++ pero a diferencia de Modula-3, la mayoría de los operadores
integrados con sintaxis especial (operadores aritméticos, de subíndice, etc.) pueden ser redefinidos por
instancias de la clase.
(Sin haber una terminología universalmente aceptada sobre clases, haré uso ocasional de términos de
Smalltalk y C++. Usaría términos de Modula-3, ya que su semántica orientada a objetos es más cercana a
Python que C++, pero no espero que muchos lectores hayan escuchado hablar de él).
79
conocimientos en este tema son útiles para cualquier programador Python avanzado.
Un espacio de nombres es una relación de nombres a objetos. Muchos espacios de nombres están
implementados en este momento como diccionarios de Python, pero eso no se nota para nada (excepto
por el desempeño), y puede cambiar en el futuro. Como ejemplos de espacios de nombres tenés: el
conjunto de nombres incluidos (funciones como abs(), y los nombres de excepciones integradas); los
nombres globales en un módulo; y los nombres locales en la invocación a una función. Lo que es
importante saber de los espacios de nombres es que no hay relación en absoluto entre los nombres de
espacios de nombres distintos; por ejemplo, dos módulos diferentes pueden tener definidos los dos una
función maximizar sin confusión; los usuarios de los módulos deben usar el nombre del módulo como
prefijo.
Por cierto, yo uso la palabra atributo para cualquier cosa después de un punto; por ejemplo, en la
expresión z.real, real es un atributo del objeto z. Estrictamente hablando, las referencias a nombres
en módulos son referencias a atributos: en la expresión modulo.funcion, modulo es un objeto módulo
y funcion es un atributo de éste. En este caso hay una relación directa entre los atributos del módulo y
los nombres globales definidos en el módulo: ¡están compartiendo el mismo espacio de nombres! 4
Los atributos pueden ser de sólo lectura, o de escritura. En el último caso es posible la asignación a
atributos. Los atributos de módulo pueden escribirse: modulo.la_respuesta = 42. Los atributos de
escritura se pueden borrar también con la instrucción del. Por ejemplo, del modulo.la_respuesta
va a eliminar el atributo la_respuesta del objeto con nombre modulo.
Los espacios de nombres se crean en diferentes momentos y con diferentes tiempos de vida. El espacio
de nombres que contiene los nombres incluidos se crea cuando se inicia el intérprete, y nunca se borra. El
espacio de nombres global de un módulo se crea cuando se lee la definición de un módulo; normalmente,
los espacios de nombres de módulos también duran hasta que el intérprete finaliza. Las instrucciones
ejecutadas en el nivel de llamadas superior del intérprete, ya sea desde un script o interactivamente, se
consideran parte del módulo llamado __main__, por lo tanto tienen su propio espacio de nombres global.
(Los nombres incluidos en realidad también viven en un módulo; este se llama __builtin__.)
El espacio de nombres local a una función se crea cuando la función es llamada, y se elimina cuando la
función retorna o lanza una excepción que no se maneje dentro de la función. (Podríamos decir que lo que
pasa en realidad es que ese espacio de nombres se "olvida".) Por supuesto, las llamadas recursivas
tienen cada una su propio espacio de nombres local.
Un alcance es una región textual de un programa en Python donde un espacio de nombres es accesible
directamente. "Accesible directamente" significa que una referencia sin calificar a un nombre intenta
encontrar dicho nombre dentro del espacio de nombres.
80
• el ámbito anteúltimo contiene los nombres globales del módulo actual
• el alcance exterior (donde se busca al final) es el espacio de nombres que contiene los
nombres incluidos
Si un nombre se declara como global, entonces todas las referencias y asignaciones al mismo van directo
al alcance intermedio que contiene los nombres globales del módulo. De otra manera, todas las variables
que se encuentren fuera del alcance interno son de sólo lectura (un intento de escribir a esas variables
simplemente crea una nueva variable en el alcance interno, dejando intacta la variable externa del mismo
nombre).
Habitualmente, el alcance local referencia los nombres locales de la función actual. Fuera de una función,
el alcance local referencia al mismo espacio de nombres que el alcance global: el espacio de nombres del
módulo. Las definiciones de clases crean un espacio de nombres más en el alcance local.
Es importante notar que los alcances se determinan textualmente: el alcance global de una función
definida en un módulo es el espacio de nombres de ese módulo, no importa desde dónde o con qué alias
se llame a la función. Por otro lado, la búsqueda de nombres se hace dinámicamente, en tiempo de
ejecución; sin embargo, la definición del lenguaje está evolucionando a hacer resolución de nombres
estáticamente, en tiempo de "compilación", ¡así que no te confíes de la resolución de nombres dinámica!
(De hecho, las variables locales ya se determinan estáticamente.)
Una peculiaridad especial de Python es que, si no hay una declaración global o nonlocal en efecto,
las asignaciones a nombres siempre van al alcance interno. Las asignaciones no copian datos, solamente
asocian nombres a objetos. Lo mismo cuando se borra: la instrucción del x quita la asociación de x del
espacio de nombres referenciado por el alcance local. De hecho, todas las operaciones que introducen
nuevos nombres usan el alcance local: en particular, las instrucciones import y las definiciones de
funciones asocian el módulo o nombre de la función al espacio de nombres en el alcance local. (La
instrucción global puede usarse para indicar que ciertas variables viven en el alcance global.)
class Clase:
<declaración-1>
.
.
.
<declaración-N>
81
Las definiciones de clases, al igual que las definiciones de funciones (instrucciones def) deben ejecutarse
antes de que tengan efecto alguno. (Es concebible poner una definición de clase dentro de una rama de
un if, o dentro de una función.)
En la práctica, las declaraciones dentro de una clase son definiciones de funciones, pero otras
declaraciones son permitidas, y a veces resultan útiles; veremos esto más adelante. Las definiciones de
funciones dentro de una clase normalmente tienen una lista de argumentos peculiar, dictada por las
convenciones de invocación de métodos; a esto también lo veremos más adelante.
Cuando se ingresa una definición de clase, se crea un nuevo espacio de nombres, el cual se usa como
alcance local; por lo tanto, todas las asignaciones a variables locales van a este nuevo espacio de
nombres. En particular, las definiciones de funciones asocian el nombre de las funciones nuevas allí.
Cuando una definición de clase se finaliza normalmente se crea un objeto clase. Básicamente, este objeto
envuelve los contenidos del espacio de nombres creado por la definición de la clase; aprenderemos más
acerca de los objetos clase en la sección siguiente. El alcance local original (el que tenía efecto justo
antes de que ingrese la definición de la clase) es restablecido, y el objeto clase se asocia allí al nombre
que se le puso a la clase en el encabezado de su definición (Clase en el ejemplo).
Objetos clase
Los objetos clase soportan dos tipos de operaciones: hacer referencia a atributos e instanciación.
Para hacer referencia a atributos se usa la sintaxis estándar de todas las referencias a atributos en
Python: objeto.nombre. Los nombres de atributo válidos son todos los nombres que estaban en el
espacio de nombres de la clase cuando ésta se creó. Por lo tanto, si la definición de la clase es así:
class MiClase:
"""Simple clase de ejemplo"""
i = 12345
def f(self):
return 'hola mundo'
...entonces MiClase.i y MiClase.f son referencias de atributos válidas, que devuelven un entero y
un objeto función respectivamente. Los atributos de clase también pueden ser asignados, o sea que
podés cambiar el valor de MiClase.i mediante asignación. __doc__ también es un atributo válido, que
devuelve la documentación asociada a la clase: "Simple clase de ejemplo".
La instanciación de clases usa la notación de funciones. Hacé de cuenta que el objeto de clase es una
función sin parámetros que devuelve una nueva instancia de la clase. Por ejemplo (para la clase de más
arriba):
x = MiClase()
...crea una nueva instancia de la clase y asigna este objeto a la variable local x.
La operación de instanciación ("llamar" a un objeto clase) crea un objeto vacío. Muchas clases necesitan
crear objetos con instancias en un estado inicial particular. Por lo tanto una clase puede definir un método
82
especial llamado __init__(), de esta forma:
def __init__(self):
self.datos = []
Cuando una clase define un método __init__(), la instanciación de la clase automáticamente invoca a
__init__() para la instancia recién creada. Entonces, en este ejemplo, una instancia nueva e
inicializada se puede obtener haciendo:
x = MiClase()
Por supuesto, el método __init__() puede tener argumentos para mayor flexibilidad. En ese caso, los
argumentos que se pasen al operador de instanciación de la clase van a parar al método __init__().
Por ejemplo,
Objetos instancia
Ahora, ¿Qué podemos hacer con los objetos instancia? La única operación que es entendida por los
objetos instancia es la referencia de atributos. Hay dos tipos de nombres de atributos válidos, atributos de
datos y métodos.
Los atributos de datos se corresponden con las "variables de instancia" en Smalltalk, y con las "variables
miembro" en C++. Los atributos de datos no necesitan ser declarados; tal como las variables locales son
creados la primera vez que se les asigna algo. Por ejemplo, si x es la instancia de MiClase creada más
arriba, el siguiente pedazo de código va a imprimir el valor 16, sin dejar ningún rastro:
x.contador = 1
while x.contador < 10:
x.contador = x.contador * 2
print x.contador
del x.contador
El otro tipo de atributo de instancia es el método. Un método es una función que "pertenece a" un objeto.
En Python, el término método no está limitado a instancias de clase: otros tipos de objetos pueden tener
métodos también. Por ejemplo, los objetos lista tienen métodos llamados append, insert, remove, sort, y
así sucesivamente. Pero, en la siguiente explicación, usaremos el término método para referirnos
exclusivamente a métodos de objetos instancia de clase, a menos que se especifique explícitamente lo
83
contrario.
Los nombres válidos de métodos de un objeto instancia dependen de su clase. Por definición, todos los
atributos de clase que son objetos funciones definen métodos correspondientes de sus instancias.
Entonces, en nuestro ejemplo, x.f es una referencia a un método válido, dado que MiClase.f es una
función, pero x.i no lo es, dado que MiClase.i no lo es. Pero x.f no es la misma cosa que
MiClase.f; es un objeto método, no un objeto función.
Objetos método
Generalmente, un método es llamado luego de ser vinculado:
x.f()
En el ejemplo MiClase, esto devuelve la cadena 'hola mundo'. Pero no es necesario llamar al método
justo en ese momento: x.f es un objeto método, y puede ser guardado y llamado más tarde. Por
ejemplo:
xf = x.f
while True:
print xf()
¿Qué sucede exactamente cuando un método es llamado? Debés haber notado que x.f() fue llamado
más arriba sin ningún argumento, a pesar de que la definición de función de f() especificaba un
argumento. ¿Qué pasó con ese argumento? Seguramente Python levanta una excepción cuando una
función que requiere un argumento es llamada sin ninguno, aún si el argumento no es utilizado...
De hecho, tal vez hayas adivinado la respuesta: lo que tienen de especial los métodos es que el objeto es
pasado como el primer argumento de la función. En nuestro ejemplo, la llamada x.f() es exactamente
equivalente a MiClase.f(x). En general, llamar a un método con una lista de n argumentos es
equivalente a llamar a la función correspondiente con una lista de argumentos que es creada insertando el
objeto del método antes del primer argumento.
Si aún no comprendés como funcionan los métodos, un vistazo a la implementación puede ayudar a
clarificar este tema. Cuando se hace referencia un atributo de instancia y no es un atributo de datos, se
busca dentro de su clase. Si el nombre denota un atributo de clase válido que es un objeto función, un
método objeto es creado, juntando (punteros a) el objeto instancia y el objeto función que ha sido
encontrado. Este objeto abstracto creado de esta unión es el objeto método. Cuando el objeto método es
llamado con una lista de argumentos, es nuevamente desempaquetado, una lista de argumentos nueva es
construida a partir del objeto instancia y la lista de argumentos original, y el objeto función es llamado con
esta nueva lista de argumentos.
84
Algunas observaciones
Los atributos de datos tienen preferencia sobre los métodos con el mismo nombre; para evitar conflictos
de nombre accidentales, que pueden causar errores difíciles de encontrar en programas grandes, es
prudente usar algún tipo de convención que minimice las posibilidades de dichos conflictos. Algunas
convenciones pueden ser poner los nombres de métodos con mayúsculas, prefijar los nombres de
atributos de datos con una pequeña cadena única (a lo mejor sólo un guión bajo), o usar verbos para los
métodos y sustantivos para los atributos.
A los atributos de datos los pueden hacer referencia tanto los métodos como los usuarios ("clientes")
ordinarios de un objeto. En otras palabras, las clases no se usan para implementar tipos de datos
abstractos puros. De hecho, en Python no hay nada que haga cumplir el ocultar datos; todo se basa en
convención. (Por otro lado, la implementación de Python, escrita en C, puede ocultar por completo detalles
de implementación y el control de acceso a un objeto si es necesario; esto se puede usar en extensiones
a Python escritas en C.)
Los clientes deben usar los atributos de datos con cuidado; éstos pueden romper invariantes que
mantienen los métodos si pisan los atributos de datos. Observá que los clientes pueden añadir sus propios
atributos de datos a una instancia sin afectar la validez de sus métodos, siempre y cuando se eviten
conflictos de nombres; de nuevo, una convención de nombres puede ahorrar un montón de dolores de
cabeza.
No hay un atajo para hacer referencia a atributos de datos (¡u otros métodos!) desde dentro de un método.
A mi parecer, esto en realidad aumenta la legibilidad de los métodos: no existe posibilidad alguna de
confundir variables locales con variables de instancia cuando repasamos un método.
A menudo, el primer argumento de un método se llama self (uno mismo). Esto no es nada más que una
convención: el nombre self no significa nada en especial para Python. Observá que, sin embargo, si no
seguís la convención tu código puede resultar menos legible a otros programadores de Python, y puede
llegar a pasar que un programa navegador de clases pueda escribirse de una manera que dependa de
dicha convención.
Cualquier objeto función que es un atributo de clase define un método para instancias de esa clase. No es
necesario que el la definición de la función esté textualmente dentro de la definición de la clase: asignando
un objeto función a una variable local en la clase también está bien. Por ejemplo:
class C:
f = f1
def g(self):
return 'hola mundo'
h = g
85
Ahora f, g y h son todos atributos de la clase C que hacen referencia a objetos función, y
consecuentemente son todos métodos de las instancias de C; h siendo exactamente equivalente a g.
Fijate que esta práctica normalmente sólo sirve para confundir al que lea un programa.
Los métodos pueden llamar a otros métodos de la instancia usando el argumento self:
class Bolsa:
def __init__(self):
self.datos = []
def agregar(self, x):
self.datos.append(x)
def dobleagregar(self, x):
self.agregar(x)
self.agregar(x)
Los métodos pueden hacer referencia a nombres globales de la misma manera que lo hacen las funciones
comunes. El alcance global asociado a un método es el módulo que contiene la definición de la clase. (La
clase misma nunca se usa como un alcance global.) Si bien es raro encontrar una buena razón para usar
datos globales en un método, hay muchos usos legítimos del alcance global: por lo menos, las funciones y
módulos importados en el alcance global pueden usarse por los métodos, al igual que las funciones y
clases definidas en él. Habitualmente, la clase que contiene el método está definida en este alcance
global, y en la siguiente sección veremos algunas buenas razones por las que un método querría hacer
referencia a su propia clase.
Todo valor es un objeto, y por lo tanto tiene una clase (también llamado su tipo). Ésta se almacena como
objeto.__class__.
Herencia
Por supuesto, una característica del lenguaje no sería digna del nombre "clase" si no soportara herencia.
La sintaxis para una definición de clase derivada se ve así:
class ClaseDerivada(ClaseBase):
<declaración-1>
.
.
.
<declaración-N>
El nombre ClaseBase debe estar definido en un alcance que contenga a la definición de la clase
derivada. En el lugar del nombre de la clase base se permiten otras expresiones arbitrarias. Esto puede
ser útil, por ejemplo, cuando la clase base está definida en otro módulo:
class ClaseDerivada(modulo.ClaseBase):
86
La ejecución de una definición de clase derivada procede de la misma forma que una clase base. Cuando
el objeto clase se construye, se tiene en cuenta a la clase base. Esto se usa para resolver referencias a
atributos: si un atributo solicitado no se encuentra en la clase, la búsqueda continúa por la clase base.
Esta regla se aplica recursivamente si la clase base misma deriva de alguna otra clase.
No hay nada en especial en la instanciación de clases derivadas: ClaseDerivada() crea una nueva
instancia de la clase. Las referencias a métodos se resuelven de la siguiente manera: se busca el atributo
de clase correspondiente, descendiendo por la cadena de clases base si es necesario, y la referencia al
método es válida si se entrega un objeto función.
Las clases derivadas pueden redefinir métodos de su clase base. Como los métodos no tienen privilegios
especiales cuando llaman a otros métodos del mismo objeto, un método de la clase base que llame a otro
método definido en la misma clase base puede terminar llamando a un método de la clase derivada que lo
haya redefinido. (Para los programadores de C++: en Python todos los métodos son en efecto
virtuales.)
Un método redefinido en una clase derivada puede de hecho querer extender en vez de simplemente
reemplazar al método de la clase base con el mismo nombre. Hay una manera simple de llamar al método
de la clase base directamente: simplemente llamás a ClaseBase.metodo(self, argumentos). En
ocasiones esto es útil para los clientes también. (Observá que esto sólo funciona si la clase base es
accesible como CalseBase en el alcance global.)
Herencia múltiple
Python también soporta una forma limitada de herencia múltiple. Una definición de clase con múltiples
clases base se ve así:
Para clases de estilo viejo la única regla es buscar en profundidad, de izquierda a derecha. Por lo tanto, si
un atributo no se encuentra en ClaseDerivada, se busca en Base1, luego (recursivamente) en las
clases base de Base1, y sólo si no se encuentra allí se lo busca en Base2, y así sucesivamente.
87
(A algunos la búsqueda en anchura, o sea, buscar en Base2 y Base3 antes que en las clases base de
Base1, les parece más natural. Sin embargo, para esto haría falta que sepas si un atributo en particular
de Base1 está de hecho definido en Base1 o en alguna de sus clases base antes de que puedas
entender las consecuencias de un conflicto de nombres con un atributo de Base2. La regla de buscar
primero en profundidad no hace diferencias entre atributos directos o heredados de Base1.)
Para las clases de estilo nuevo, el método de resolución de orden cambia dinámicamente para soportar
llamadas cooperativas a super(). Este enfoque es conocido en otros lenguajes con herencia múltiple
como "llámese al siguiente método" y es más poderoso que la llamada al superior que se encuentra en
lenguajes con sólo herencia simple.
Con las clases de estilo nuevo, se necesita el orden dinámico porque todos los casos de herencia múltiple
exhiben una o más relaciones en diamante (cuando se puede llegar al menos a una de las clases base
por distintos caminos desde la clase de más abajo). Por ejemplo, todas las clases de nuevo estilo heredan
de object, por lo tanto cualquier caso de herencia múltiple provee más de un camino para llegar a
object. Para que las clases base no sean accedidas más de una vez, el algoritmo dinámico hace lineal
el orden de búsqueda de manera que se preserve el orden de izquierda a derecha especificado en cada
clase, que se llame a cada clase base sólo una vez, y que sea monótona (lo cual significa que una clase
puede tener clases derivadas sin afectar el orden de precedencia de sus clases bases). En conjunto, estas
propiedades hacen posible diseñar clases confiables y extensibles con herencia múltiple. Para más
detalles mirá http://www.python.org/download/releases/2.3/mro/.
Variables privadas
Las variables "privadas" de instancia que no pueden accederse excepto desde dentro de un objeto, no
existen en Python. Sin embargo, hay una convención que se sigue en la mayoría del código Python: un
nombre prefijado con un guión bajo (por ejemplo, _spam) debería tratarse como una parte no pública de la
API (más allá de que sea una función, un método, o un dato). Debería considerarse un detalle de
implementación y que está sujeto a cambios sin aviso.
Ya que hay un caso de uso válido para los identificadores privados de clase (a saber: colisión de nombres
con nombres definidos en las subclases), hay un soporte limitado para este mecanismo. Cualquier
identificador con la forma __spam (al menos dos guiones bajos al principio, como mucho un guión bajo al
final) es textualmente reemplazado por _nombredeclase__spam, donde nombredeclase es el nombre
de clase actual al que se le sacan guiones bajos del comienzo (si los tuviera). Se modifica el nombre del
identificador sin importar su posición sintáctica, así que puede ser usado para definir instancias y variables
de clase privadas, métodos, variables guardadas en globales, y aún variables guardadas en instancias
privadas de esta clase en instancias de otras clases. Puede ocurrir que se trunque si el nombre
modificado queda con más de 255 caracteres. Fuera de las clases, o cuando el nombre de clase consiste
solo en guiones bajos, no se modifican los nombres de identificadores.
Hay que aclarar que las reglas de modificación de nombres están diseñadas principalmente para evitar
accidentes; es posible acceder o modificar una variable que es considerada como privada. Esto hasta
puede resultar útil en circunstancias especiales, tales como en el depurador.
88
Notar que el código pasado a exec, a eval() o a execfile() no considera que el nombre de clase de
la clase que invoca sea la clase actual; esto es similar al efecto de la sentencia global, efecto que es de
similar manera restringido a código que es compilado en conjunto. La misma restricción aplica a
getattr(), setattr() y delattr(), así como cuando se referencia a __dict__ directamente.
Cambalache
A veces es útil tener un tipo de datos similar al "registro" de Pascal o la "estructura" de C, que sirva para
juntar algunos pocos ítems con nombre. Una definición de clase vacía funcionará perfecto:
class Empleado:
pass
Algún código Python que espera un tipo abstracto de datos en particular puede frecuentemente recibir en
cambio una clase que emula los métodos de aquel tipo de datos. Por ejemplo, si tenés una función que
formatea algunos datos a partir de un objeto archivo, podés definir una clase con métodos read() y
readline() que obtengan los datos de alguna cadena en memoria intermedia, y pasarlo como
argumento.
Los objetos método de instancia tienen atributos también: m.im_self es el objeto instancia con el
método m(), y m.im_func es el objeto función correspondiente al método.
raise instancia
En la primera forma, instancia debe ser una instancia de Clase o de una clase derivada de ella. La
segunda forma es una abreviatura de:
89
Una clase en una cláusula except es compatible con una excepción si es de la misma clase o una clase
base suya (pero no al revés, una cláusula except listando una clase derivada no es compatible con una
clase base). Por ejemplo, el siguiente código imprimirá B, C, D en ese orden:
class B:
pass
class C(B):
pass
class D(C):
pass
Notar que si la cláusulas except fueran invertidas (dejando except B al principio), habría impreso B, B,
B; se dispara la primera cláusula except que coincide.
Cuando se imprime un mensaje de error para una excepción sin atrapar, se imprime el nombre de la clase
de la excepción, luego dos puntos y un espacio y finalmente la instancia convertida a un string usando la
función integrada str().
Iteradores
Es probable que hayas notado que la mayoría de los objetos contenedores pueden ser recorridos usando
una sentencia for:
90
Este estilo de acceso es limpio, conciso y conveniente. El uso de iteradores está impregnado y unifica a
Python. En bambalinas, la sentencia for llama a iter() en el objeto contenedor. La función devuelve
un objeto iterador que define el método next() que accede elementos en el contenedor de a uno por
vez. Cuando no hay más elementos, next() levanta una excepción StopIteration que le avisa al
bucle del for que hay que terminar. Este ejemplo muestra como funciona todo esto:
>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> it.next()
'a'
>>> it.next()
'b'
>>> it.next()
'c'
>>> it.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
it.next()
StopIteration
Habiendo visto la mecánica del protocolo de iteración, es fácil agregar comportamiento de iterador a tus
clases. Definí un método __iter__() que devuelva un objeto con un método next(). Si la clase define
next(), entonces alcanza con que __iter__() devuelva self:
91
s
Generadores
Los generadores son una simple y poderosa herramienta para crear iteradores. Se escriben como
funciones regulares pero usan la sentencia yield cuando quieren devolver datos. Cada vez que
next() es llamado, el generador continúa desde donde dejó (y recuerda todos los valores de datos y cual
sentencia fue ejecutada última). Un ejemplo muestra que los generadores pueden ser muy fáciles de
crear:
Todo lo que puede ser hecho con generadores también puede ser hecho con iteradores basados en
clases, como se describe en la sección anterior. Lo que hace que los generadores sean tan compactos es
que los métodos __iter__() y next() son creados automáticamente.
Otra característica clave es que las variables locales y el estado de la ejecución son guardados
automáticamente entre llamadas. Esto hace que la función sea más fácil de escribir y quede mucho más
claro que hacerlo usando variables de instancia tales como self.indice y self.datos.
Además de la creación automática de métodos y el guardar el estado del programa, cuando los
generadores terminan automáticamente levantan StopIteration. Combinadas, estas características
facilitan la creación de iteradores, y hacen que no sea más esfuerzo que escribir una función regular.
Expresiones generadoras
Algunos generadores simples pueden ser codificados concisamente como expresiones usando una
sintaxis similar a las listas por comprensión pero con paréntesis en vez de corchetes. Estas expresiones
se utilizan en situaciones donde el generador es usado inmediatamente por una función que lo contiene.
Las expresiones generadoras son más compactas pero menos versátiles que definiciones completas de
generadores, y tienden a utilizar menos memoria que las listas por comprensión equivalentes.
Ejemplos:
92
>>> sum(i*i for i in range(10)) # suma de cuadrados
285
4 Excepto por un detalle. Los objetos módulo tienen un atributo secreto de solo lectura llamado
__dict__ que devuelve el diccionario usado para implementar el espacio de nombres del
módulo; el nombre __dict__ es un atributo, pero no es un nombre global. Obviamente, esto
viola la abstracción de la implementación de espacios de nombres, y debe ser restringido a
cosas tales como depuradores post-mortem.
93
Pequeño paseo por la Biblioteca
Estándar
>>> import os
>>> os.system('time 0:02')
0
>>> os.getcwd() # devuelve el directorio de trabajo actual
'C:\\Python26'
>>> os.chdir('/server/accesslogs')
Asegurate de usar el estilo import os en lugar de from os import *. Esto evitará que os.open()
oculte a la función integrada open(), que trabaja bastante diferente.
Las funciones integradas dir() y help() son útiles como ayudas interactivas para trabajar con
módulos grandes como os:
>>> import os
>>> dir(os)
<devuelve una lista de todas las funciones del módulo>
>>> help(os)
<devuelve un manual creado a partir de las documentaciones del módulo>
Para tareas diarias de administración de archivos y directorios, el módulo shutil provee una interfaz de
más alto nivel que es más fácil de usar:
Comodines de archivos
El módulo glob provee una función para hacer listas de archivos a partir de búsquedas con comodines
en directorios:
94
Argumentos de linea de órdenes
Los programas frecuentemente necesitan procesar argumentos de linea de órdenes. Estos argumentos se
almacenan en el atributo argv del módulo sys como una lista. Por ejemplo, la siguiente salida resulta de
ejecutar python demo.py uno dos tres en la línea de órdenes:
El módulo getopt procesa sys.argv usando las convenciones de la función de Unix getopt(). El
módulo optparse provee un procesamiento más flexible de la linea de órdenes.
>>> import re
>>> re.findall(r'\bt[a-z]*', 'tres felices tigres comen trigo')
['tres', 'tigres', 'trigo']
>>> re.sub(r'(\b[a-z]+) \1', r'\1', 'gato en el el sombrero')
'gato en el sombrero'
Cuando se necesita algo más sencillo solamente, se prefieren los métodos de las cadenas porque son
más fáciles de leer y depurar.
95
Matemática
El módulo math permite el acceso a las funciones de la biblioteca C subyacente para la matemática de
punto flotante:
Acceso a Internet
Hay varios módulos para acceder a internet y procesar sus protocolos. Dos de los más simples son
urllib2 para traer data de URLs y smtplib para mandar correos:
96
>>> server.quit()
(Notá que el segundo ejemplo necesita un servidor de correo corriendo en la máquina local)
Fechas y tiempos
El módulo datetime ofrece clases para manejar fechas y tiempos tanto de manera simple como
compleja. Aunque se soporta aritmética sobre fechas y tiempos, el foco de la implementación es en la
extracción eficiente de partes para manejarlas o formatear la salida. El módulo también soporta objetos
que son conscientes de la zona horaria.
Compresión de datos
Los formatos para archivar y comprimir datos se soportan directamente con los módulos: zlib, gzip,
bz2, zipfile y tarfile.
97
>>> zlib.decompress(t)
'witch which has which witches wrist watch'
>>> zlib.crc32(s)
226805979
Medición de rendimiento
Algunos usuarios de Python desarrollan un profundo interés en saber el rendimiento relativo de las
diferentes soluciones al mismo problema. Python provee una herramienta de medición que responde esas
preguntas inmediatamente.
Por ejemplo, puede ser tentador usar la característica de empaquetamiento y desempaquetamiento de las
tuplas en lugar de la solución tradicional para intercambiar argumentos. El módulo timeit muestra
rapidamente una modesta ventaja de rendimiento:
En contraste con el fino nivel de granularidad del módulo timeit, los módulos profile y pstats
proveen herramientas para identificar secciones críticas de tiempo en bloques de código más grandes.
Control de calidad
Una forma para desarrollar software de alta calidad es escribir pruebas para cada función mientras se la
desarrolla, y correr esas pruebas frecuentemente durante el proceso de desarrollo.
El módulo doctest provee una herramienta para revisar un módulo y validar las pruebas integradas en
las cadenas de documentación (o docstring) del programa. La construcción de las pruebas es tan sencillo
como cortar y pegar una ejecución típica junto con sus resultados en los docstrings. Esto mejora la
documentación al proveer al usuario un ejemplo y permite que el módulo doctest se asegure que el
código permanece fiel a la documentación:
def promedio(valores):
"""Calcula la media aritmética de una lista de números.
98
import doctest
doctest.testmod() # valida automáticamente las pruebas integradas
El módulo unittest necesita más esfuerzo que el módulo doctest, pero permite que se mantenga en
un archivo separado un conjunto más comprensivo de pruebas:
import unittest
class TestFuncionesEstadisticas(unittest.TestCase):
def test_promedio(self):
self.assertEqual(promedio([20, 30, 70]), 40.0)
self.assertEqual(round(promedio([1, 5, 7]), 1), 4.3)
self.assertRaises(ZeroDivisionError, promedio, [])
self.assertRaises(TypeError, promedio, 20, 30, 70)
99
Pequeño paseo por la Biblioteca
Estándar - Parte II
Este segundo paseo cubre módulos más avanzados que facilitan necesidades de programación
complejas. Estos módulos raramente se usan en scripts cortos.
Formato de salida
El módulo repr provee una versión de repr() ajustada para mostrar contenedores grandes o
profundamente anidados, en forma abreviada:
El módulo pprint ofrece un control más sofisticado de la forma en que se imprimen tanto los objetos
predefinidos como los objetos definidos por el usuario, de manera que sean legibles por el intérprete.
Cuando el resultado ocupa más de una línea, el generador de "impresiones lindas" agrega saltos de línea
y sangrías para mostrar la estructura de los datos más claramente:
El módulo textwrap formatea párrafos de texto para que quepan dentro de cierto ancho de pantalla:
100
El módulo locale accede a una base de datos de formatos específicos a una cultura. El atributo
:var:`grouping` de la función :function:`format` permite una forma directa de formatear números con
separadores de grupo:
Plantillas
El módulo string incluye una clase versátil Template (plantilla) con una sintaxis simplificada apta para
ser editada por usuarios finales. Esto permite que los usuarios personalicen sus aplicaciones sin
necesidad de modificar la aplicación en sí.
El formato usa marcadores cuyos nombres se forman con $ seguido de identificadores Python válidos
(caracteres alfanuméricos y guión de subrayado). Si se los encierra entre llaves, pueden seguir más
caracteres alfanuméricos sin necesidad de dejar espacios en blanco. $$ genera un $:
El método substitute() lanza KeyError cuando no se suministra ningún valor para un marcador
mediante un diccionario o argumento por nombre. Para algunas aplicaciones los datos suministrados por
el usuario puede ser incompletos, y el método safe_substitute() puede ser más apropiado: deja los
marcadores inalterados cuando hay datos faltantes:
101
Las subclases de Template pueden especificar un delimitador propio. Por ejemplo, una utilidad de
renombrado por lotes para un visualizador de fotos puede escoger usar signos de porcentaje para los
marcadores tales como la fecha actual, el número de secuencia de la imagen, o el formato de archivo:
Las plantillas también pueden ser usadas para separar la lógica del programa de los detalles de múltiples
formatos de salida. Esto permite sustituir plantillas específicas para archivos XML, reportes en texto plano,
y reportes web en HTML.
import struct
102
inicio += 16
nomarch = datos[inicio:inicio+tam_nomarch]
inicio += tam_nomarch
extra = datos[inicio:inicio+tam_extra]
print nomarch, hex(crc32), tam_comp, tam_descomp
Multi-hilos
La técnica de multi-hilos (o multi-threading) permite desacoplar tareas que no tienen dependencia
secuencial. Los hilos se pueden usar para mejorar el grado de reacción de las aplicaciones que aceptan
entradas del usuario mientras otras tareas se ejecutan en segundo plano. Un caso de uso relacionado es
ejecutar E/S en paralelo con cálculos en otro hilo.
El código siguiente muestra cómo el módulo de alto nivel threading puede ejecutar tareas en segundo
plano mientras el programa principal continúa su ejecución:
class AsyncZip(threading.Thread):
def __init__(self, arch_ent, arch_sal):
threading.Thread.__init__(self)
self.arch_ent = arch_ent
self.arch_sal = arch_sal
def run(self):
f = zipfile.ZipFile(self.arch_sal, 'w', zipfile.ZIP_DEFLATED)
f.write(self.arch_ent)
f.close()
print u'Terminó zip en segundo plano de: ', self.arch_ent
El desafío principal de las aplicaciones multi-hilo es la coordinación entre los hilos que comparten datos u
otros recursos. A ese fin, el módulo threading provee una serie de primitivas de sincronización que
incluyen locks, eventos, variables de condición, y semáforos.
103
Aún cuando esas herramientas son poderosas, pequeños errores de diseño pueden resultar en problemas
difíciles de reproducir. La forma preferida de coordinar tareas es concentrar todos los accesos a un
recurso en un único hilo y después usar el módulo Queue para alimentar dicho hilo con pedidos desde
otros hilos. Las aplicaciones que usan objetos Queue.Queue para comunicación y coordinación entre
hilos son más fáciles de diseñar, más legibles, y más confiables.
Registrando
El módulo logging ofrece un sistema de registros (logs) completo y flexible. En su forma más simple, los
mensajes de registro se envían a un archivo o a sys.stderr:
import logging
logging.debug(u'Información de depuración')
logging.info(u'Mensaje informativo')
logging.warning(u'Atención: archivo de configuración %s no se encuentra',
'server.conf')
logging.error(u'Ocurrió un error')
logging.critical(u'Error crítico -- cerrando')
El sistema de registro puede configurarse directamente desde Python o puede cargarse la configuración
desde un archivo editable por el usuario para personalizar el registro sin alterar la aplicación.
Referencias débiles
Python realiza administración de memoria automática (cuenta de referencias para la mayoría de los
objetos, y garbage collection (recolección de basura) para eliminar ciclos). La memoria se libera poco
después de que la última referencia a la misma haya sido eliminada.
Esta estrategia funciona bien para la mayoría de las aplicaciones, pero ocasionalmente aparece la
necesidad de hacer un seguimiento de objetos sólo mientras están siendo usados por alguien más.
Desafortunadamente, el sólo hecho de seguirlos crea una referencia que los hace permanentes.
104
El módulo weakref provee herramientas para seguimiento de objetos que no crean una referencia.
Cuando el objeto no se necesita más, es eliminado automáticamente de una tabla de referencias débiles y
se dispara una retrollamada (callback). Comúnmente se usa para mantener una cache de objetos que son
caros de crear:
El módulo array provee un objeto array() (vector) que es como una lista que almacena sólo datos
homogéneos y de una manera más compacta. Los ejemplos a continuación muestran un vector de
números guardados como dos números binarios sin signo de dos bytes (código de tipo "H") en lugar de
los 16 bytes por elemento habituales en listas de objetos int de Python:
El módulo collections provee un objeto deque() que es como una lista más rápida para agregar y
quitar elementos por el lado izquierdo pero búsquedas más lentas por el medio. Estos objetos son
105
adecuados para implementar colas y árboles de búsqueda a lo ancho:
no_visitado = deque([nodo_inicial])
def busqueda_a_lo_ancho(no_visitado):
nodo = no_visitado.popleft()
for m in gen_moves(nodo):
if is_goal(m):
return m
no_visitado.append(m)
Además de las implementaciones alternativas de listas, la biblioteca ofrece otras herramientas como el
módulo bisect con funciones para manipular listas ordenadas:
El módulo heapq provee funciones para implementar heaps basados en listas comunes. El menor valor
ingresado se mantiene en la posición cero. Esto es útil para aplicaciones que acceden a menudo al
elemento más chico pero no quieren hacer un orden completo de la lista:
106
• seguimiento de cifras significativas,
• o para aplicaciones donde el usuario espera que los resultados coincidan con cálculos hechos a
mano.
Por ejemplo, calcular un impuesto del 5% de una tarifa telefónica de 70 centavos da resultados distintos
con punto flotante decimal y punto flotante binario. La diferencia se vuelve significativa si los resultados se
redondean al centavo más próximo:
El resultado con Decimal conserva un cero al final, calculando automáticamente cuatro cifras
significativas a partir de los multiplicandos con dos cifras significativas. Decimal reproduce la matemática
como se la hace a mano, y evita problemas que pueden surgir cuando el punto flotante binario no puede
representar exactamente cantidades decimales.
La representación exacta permite a la clase Decimal hacer cálculos de modulo y pruebas de igualdad
que son inadecuadas para punto flotante binario:
El módulo decimal provee aritmética con tanta precisión como haga falta:
>>> getcontext().prec = 36
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857')
107
¿Y ahora qué?
Leer este tutorial probablemente reforzó tu interés por usar Python, deberías estar ansioso por aplicar
Python a la resolución de tus problemas reales. ¿A dónde deberías ir para aprender más?
Este tutorial forma parte del juego de documentación de Python. Algunos otros documentos que
encontrarás en este juego son:
• library-index:
Deberías hojear este manual, que tiene material de referencia completo (si bien breve) sobre tipos,
funciones y módulos de la biblioteca estándar. La distribución de Python estándar incluye un
montón de código adicional. Hay módulos para leer archivos de correo de Unix, obtener
documentos vía HTTP, generar números aleatorios, interpretar opciones de línea de comandos,
escribir programas CGI, comprimir datos, y muchas otras tareas. Un vistazo por la Referencia de
Biblioteca te dará una idea de lo que hay disponible.
• install-index explica cómo instalar módulos externos escritos por otros usuarios de Python.
• reference-index: Una descripción en detalle de la sintaxis y semántica de Python. Es una lectura
pesada, pero útil como guía completa al lenguaje en si.
Para preguntas relacionadas con Python y reportes de problemas puedes escribir al grupo de noticias
comp.lang.python, o enviarlas a la lista de correo que hay en python-list@python.org. El grupo de noticias
y la lista de correo están interconectadas, por lo que los mensajes enviados a uno serán retransmitidos al
otro. Hay alrededor de 120 mensajes diarios (con picos de hasta varios cientos), haciendo (y
respondiendo) preguntas, sugiriendo nuevas características, y anunciando nuevos módulos. Antes de
escribir, asegúrate de haber revisado la lista de Preguntas Frecuentes (también llamado el FAQ), o
buscalo en el directorio Misc/ de la distribución de código fuente de Python. Hay archivos de la lista de
correo disponibles en http://mail.python.org/pipermail/. El FAQ responde a muchas de las preguntas que
aparecen una y otra vez, y puede que ya contenga la solución a tu problema.
108
Edición de entrada interactiva y
sustitución de historial
Algunas versiones del intérprete de Python permiten editar la línea de entrada actual, y sustituir en base al
historial, de forma similar a las capacidades del intérprete de comandos Korn y el GNU bash. Esto se
implementa con la biblioteca GNU Readline, que soporta edición al estilo de Emacs y al estilo de vi. Esta
biblioteca tiene su propia documentación que no duplicaré aquí; pero la funcionalidad básica es fácil de
explicar. La edición interactiva y el historial aquí descriptos están disponibles como opcionales en las
versiones para Unix y Cygwin del intérprete.
Este capítulo no documenta las capacidades de edición del paquete PythonWin de Mark Hammond, ni del
entorno IDLE basado en Tk que se distribuye con Python. El historial de línea de comandos que funciona
en pantallas de DOS en NT y algunas otras variantes de DOS y Windows es también una criatura
diferente.
Edición de línea
De estar soportada, la edición de línea de entrada se activa en cuanto el intérprete muestra un símbolo de
espera de ordenes (prompt) primario o secundario. La línea activa puede editarse usando los caracteres
de control convencionales de Emacs. De estos, los más importantes son: C-A (Ctrl-A) mueve el cursor al
comienzo de la línea, C-E al final, C-B lo mueve una posición a la izquierda, C-F a la derecha. La tecla
de retroceso (backspace) borra el caracter a la izquierda del cursor, C-D el caracter a su derecha. C-K
corta el resto de la línea a la derecha del cursor, C-Y pega de vuelta la última cadena cortada.
C-underscore deshace el último cambio hecho; puede repetirse para obtener un efecto acumulativo.
Sustitución de historial
La sustitución de historial funciona de la siguiente manera: todas las líneas ingresadas y no vacías se
almacenan en una memoria intermedia, y cuando se te pide una nueva línea, estás posicionado en una
linea nueva al final de esta memoria. C-P se mueve una línea hacia arriba (es decir, hacia atrás) en el
historial, C-N se mueve una línea hacia abajo. Cualquier línea en el historial puede editarse; aparecerá un
asterisco adelante del indicador de entrada para marcar una línea como editada. Presionando la tecla
Return (Intro) se pasa la línea activa al intérprete. C-R inicia una búsqueda incremental hacia atrás,
C-S inicia una búsqueda hacia adelante.
109
Atajos de teclado
Los atajos de teclado y algunos otros parámetros de la biblioteca Readline se pueden personalizar
poniendo comandos en un archivo de inicialización llamado ~/.inputrc. Los atajos de teclado tienen la
forma
nombre-de-tecla: nombre-de-función
...o
"cadena": nombre-de-función
Por ejemplo:
Observa que la asociación por omisión para la tecla Tab en Python es insertar un caracter Tab
(tabulación horizontal) en vez de la función por defecto de Readline de completar nombres de archivo. Si
insistes, puedes redefinir esto poniendo
Tab: complete
en tu ~/.inputrc. (Desde luego, esto hace más difícil escribir líneas de continuación indentadas si estás
acostumbrado a usar Tab para tal propósito.)
Hay disponible opcionalmente completado automático de variables y nombres de módulos. Para activarlo
en el modo interactivo del intérprete, agregá lo siguiente a tu archivo de arranque: 5
Esto asocia la tecla Tab a la función de completado, con lo cual presionar la tecla Tab dos veces
sugerirá valores para completar; se fija en nombres de instrucciones Python, las variables locales del
momento, y los nombres de módulos disponibles. Para expresiones con puntos como string.a, evaluará
la expresión hasta el último '.' y luego sugerirá opciones a completar de los atributos de el objeto
110
resultante. Tené en cuenta que esto puede ejecutar código definido por la aplicación si un objeto con un
método __getattr__() forma parte de la expresión.
Un archivo de inicialización con más capacidades podría ser como este ejemplo. Observá que éste borra
los nombres que crea una vez que no se necesitan más; esto se hace debido a que el archivo de
inicialización se ejecuta en el mismo espacio de nombres que los comandos interactivos, y borrar los
nombres evita que se produzcan efectos colaterales en el entorno interactivo. Tal vez te resulte cómodo
mantener algunos de los módulos importados, tales como os, que usualmente acaban siendo necesarios
en la mayoría de las sesiones con el intérprete.
import atexit
import os
import readline
import rlcompleter
historyPath = os.path.expanduser("~/.pyhistory")
def save_history(historyPath=historyPath):
import readline
readline.write_history_file(historyPath)
if os.path.exists(historyPath):
readline.read_history_file(historyPath)
atexit.register(save_history)
del os, atexit, readline, rlcompleter, save_history, historyPath
111
completado podría usar la tabla de símbolos del intérprete. Un comando para verificar (o incluso sugerir)
coincidencia de paréntesis, comillas, etc. también sería útil.
Un intérprete interactivo mejorado alternativo que está dando vueltas desde hace rato es IPython, que
ofrece completado por tab, exploración de objetos, y administración avanzada del historial. También
puede ser configurado en profundidad, e integrarse en otras aplicaciones. Otro entorno interactivo
mejorado similar es bpython.
112
Aritmética de Punto Flotante:
Problemas y Limitaciones
Los números de punto flotante se representan en el hardware de la computadora en fracciones en base 2
(binario). Por ejemplo, la fracción decimal
0.125
0.001
...tiene el valor 0/2 + 0/4 + 1/8. Estas dos fracciones tienen valores idénticos, la única diferencia real es
que la primera está escrita en notación fraccional en base 10 y la segunda en base 2.
El problema es más fácil de entender primero en base 10. Considerá la fracción 1/3. Podés aproximarla
como una fracción de base 10
0.3
...o, mejor,
0.33
...o, mejor,
0.333
...y así. No importa cuantos dígitos desees escribir, el resultado nunca será exactamente 1/3, pero será
una aproximación cada vez mejor de 1/3.
De la misma manera, no importa cuantos dígitos en base 2 quieras usar, el valor decimal 0.1 no puede
representarse exactamente como una fracción en base 2. En base 2, 1/10 es la siguiente fracción que se
repite infinitamente:
0.0001100110011001100110011001100110011001100110011...
Frená en cualquier número finito de bits, y tendrás una aproximación. Es por esto que ves cosas como:
>>> 0.1
0.10000000000000001
En la mayoría de las máquinas de hoy en día, eso es lo que verás si ingresás 0.1 en un prompt de Python.
Quizás no, sin embargo, porque la cantidad de bits usados por el hardware para almacenar valores de
113
punto flotante puede variar en las distintas máquinas, y Python sólo muestra una aproximación del valor
decimal verdadero de la aproximación binaria guardada por la máquina. En la mayoría de las máquinas, si
Python fuera a mostrar el verdadero valor decimal de la aproximación almacenada por 0.1, tendría que
mostrar sin embargo
>>> 0.1
0.1000000000000000055511151231257827021181583404541015625
El prompt de Python usa la función integrada repr() para obtener una versión en cadena de caracteres
de todo lo que muestra. Para flotantes, repr(float) redondea el valor decimal verdadero a 17 dígitos
significativos, dando
0.10000000000000001
repr(float) produce 17 dígitos significativos porque esto es suficiente (en la mayoría de las máquinas)
para que se cumpla eval(repr(x)) == x exactamente para todos los flotantes finitos X, pero
redondeando a 16 dígitos no es suficiente para que sea verdadero.
Notá que esta es la verdadera naturaleza del punto flotante binario: no es un error de Python, y tampoco
es un error en tu código. Verás lo mismo en todos los lenguajes que soportan la aritmética de punto
flotante de tu hardware (a pesar de que en algunos lenguajes por omisión no muestren la diferencia, o no
lo hagan en todos los modos de salida).
La función integrada :func: str de Python produce sólo 12 dígitos significativos, y quizás quieras usar esa.
Normalmente eval(str(x)) no reproducirá x, pero la salida quizás sea más placentera de ver:
Es importante darse cuenta de que esto es, realmente, una ilusión: el valor en la máquina no es
exactamente 1/10, simplemente estás redondeando el valor que se muestra del valor verdadero de la
máquina.
>>> 0.1
0.10000000000000001
...quizás estés tentado de usar la función round() para recortar el resultado al dígito que esperabas.
Pero es lo mismo:
>>> round(0.1, 1)
0.10000000000000001
El problema es que el valor de punto flotante binario almacenado para "0.1" ya era la mejor aproximación
binaria posible de 1/10, de manera que intentar redondearla nuevamente no puede mejorarla: ya era la
mejor posible.
Otra consecuencia es que como 0.1 no es exactamente 1/10, sumar diez valores de 0.1 quizás tampoco
dé exactamente 1.0:
114
>>> suma = 0.0
>>> for i in range(10):
... suma += 0.1
...
>>> suma
0.99999999999999989
La aritmética de punto flotante binaria tiene varias sorpresas como esta. El problema con "0.1" es
explicado con detalle abajo, en la sección "Error de Representación". Mirá los Peligros del Punto Flotante
(en inglés, The Perils of Floating Point) para una más completa recopilación de otras sorpresas normales.
Como dice cerca del final, "no hay respuestas fáciles". A pesar de eso, ¡no le tengas mucho miedo al
punto flotante! Los errores en las operaciones flotantes de Python se heredan del hardware de punto
flotante, y en la mayoría de las máquinas están en el orden de no más de una 1 parte en 2**53 por
operación. Eso es más que adecuado para la mayoría de las tareas, pero necesitás tener en cuenta que
no es aritmética decimal, y que cada operación de punto flotante sufre un nuevo error de redondeo.
A pesar de que existen casos patológicos, para la mayoría de usos casuales de la aritmética de punto
flotante al final verás el resultado que esperás si simplemente redondeás lo que mostrás de tus resultados
finales al número de dígitos decimales que esperás. str() es normalmente suficiente, y para un control
más fino mirá los parámetros del método de formateo str.format() en formatstrings.
Error de Representación
Esta sección explica el ejemplo "0.1" en detalle, y muestra como en la mayoría de los casos vos mismo
podés realizar un análisis exacto como este. Se asume un conocimiento básico de la representación de
punto flotante binario.
Error de representación se refiere al hecho de que algunas (la mayoría) de las fracciones decimales no
pueden representarse exactamente como fracciones binarias (en base 2). Esta es la razón principal de por
qué Python (o Perl, C, C++, Java, Fortran, y tantos otros) frecuentemente no mostrarán el número decimal
exacto que esperás:
>>> 0.1
0.10000000000000001
¿Por qué es eso? 1/10 no es representable exactamente como una fracción binaria. Casi todas las
máquinas de hoy en día (Noviembre del 2000) usan aritmética de punto flotante IEEE-754, y casi todas las
plataformas mapean los flotantes de Python al "doble precisión" de IEEE-754. Estos "dobles" tienen 53
bits de precisión, por lo tanto en la entrada la computadora intenta convertir 0.1 a la fracción más cercana
que puede de la forma J/2***N* donde J es un entero que contiene exactamente 53 bits. Reescribiendo
1 / 10 ~= J / (2**N)
...como
115
J ~= 2**N / 10
...y recordando que J tiene exactamente 53 bits (es >= 2**52 pero < 2**53), el mejor valor para N es
56:
>>> 2**52
4503599627370496L
>>> 2**53
9007199254740992L
>>> 2**56/10
7205759403792793L
O sea, 56 es el único valor para N que deja J con exactamente 53 bits. El mejor valor posible para J es
entonces el cociente redondeado:
Ya que el resto es más que la mitad de 10, la mejor aproximación se obtiene redondeándolo:
>>> q+1
7205759403792794L
Por lo tanto la mejor aproximación a 1/10 en doble precisión 754 es eso sobre 2**56, o
7205759403792794 / 72057594037927936
Notá que como lo redondeamos, esto es un poquito más grande que 1/10; si no lo hubiéramos
redondeado, el cociente hubiese sido un poquito menor que 1/10. ¡Pero no hay caso en que sea
exactamente 1/10!
Entonces la computadora nunca "ve" 1/10: lo que ve es la fracción exacta de arriba, la mejor aproximación
al flotante doble de 754 que puede obtener:
>>> .1 * 2**56
7205759403792794.0
Si multiplicamos esa fracción por 10**30, podemos ver el valor (truncado) de sus 30 dígitos más
significativos:
...lo que significa que el valor exacto almacenado en la computadora es aproximadamente igual al valor
decimal 0.100000000000000005551115123125. Redondeando eso a 17 dígitos significativos da el
0.10000000000000001 que Python muestra (bueno, mostraría en cualquier plataforma que cumpla con
754 cuya biblioteca en C haga la mejor conversión posible en entrada y salida... ¡la tuya quizás no!).
116