3. La computación con cadenas

3.1. Como crear una cadena

Una cadena es una secuencia de caracteres delimitados entre comillas sencillas o dobles. He aquí algunos ejemplos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
>>> autor = 'Miguel Cervantes'
>>> autor
'Miguel Cervantes'
>>> autordoble = "Miguel Cervantes"
>>> autordoble
'Miguel Cervantes'
>>> novela = 'Don Quijote' de la Mancha'
File "<stdin>", line 1
   circus = 'Don Quijote' de la Mancha'
                        ^
    SyntaxError: invalid syntax
>>> novela = "Don Quijote' de la Mancha"
>>> novela
"Don Quijote' de la Mancha"
>>> novela = 'Don Quijote\' de la Mancha'
>>> novela
"Don Quijote' de la Mancha"

¿Has notado la causa del error en 8? ¿Has notado cómo se puede evitarlo con las comillas dobles de 13? Por fin, te has dado cuenta del empleo de \ como un caracter de escape en 15, lo cual le avisa a Python para que no procese ' como delimitador de cadena sino como apóstrofe o comilla sencilla?

Note

Puesto que el delimitar una cadena con comillas dobles resuelve el problema de la comilla sencilla en una cadena, muchos prefieren poner comillas dobles con todas las cadenas. Yo prefiero las comillas senciallas, sin embargo, porque me ahorran el esfuerzo de apretar la tecla de mayúsculas dos veces. Además, como línea 4 muestra, si no hay necesidad de comillas dobles, Python las cambia a comillas sencillas. Me desconcierta que Python me corrija el código.

3.2. Métodos básicos de las cadenas

3.2.1. Como combinar y repetir cadenas

Una cadena nueva se puede formar combinando o concatenando dos cadenas con + o repetiendo una cadena un número de veces con * número. Por desgracia, no se puede borrar un caracter con . Abajo se da el código de entrada, sin la salida que le corresponde. Te toca a ti entrar los comandos para ver lo que hacen:

1
2
3
4
5
6
7
8
>>> C = 'cenicienta'
>>> C+'!'
>>> 'la '+C
>>> 'la '+C+'!'
>>> C*2
>>> C+'!'*2
>>> (C+'!')*2
>>> C-'a'

3.2.1.1. El alcance de un operador

Líneas 6 y 7 plantean una duda: ¿ como representar el orden de procesamiento entre varios operadores?

3.2.2. Como calcular lo larga que es una cadena con len()

Entra lo siguiente:

1
2
3
>>> len(C)
>>> len(C+'!')
>>> len(C*2)

Debieras haber sacado la conclusión de que len(C) produce el largo de una cadena, es decir, el número de caracteres que contiene. Si tiene sentido, la salida de un método puede ser la entrada de otro.

3.2.3. Como es la sintaxis o notación de un método

La materia agregada a un método entre paréntesis se llama el argumento del mismo. En los ejemplos de arriba, el argumento C se puede concebir lingüísticamente como el complemento de un sustantivo: el largo de C, la ordenación de C, o el conjunto de C.

Pero si se necesitan dos datos para que funcione un método, por ejemplo, para contar las i de cinicienta, se antepone el segundo ‘argumento’ al método:

>>> C.count('i')

La expresión se puede leer como “en C, cuenta las i”. El argumento es la sub-cadena que se cuenta, 'i', y el atributo es la cadena por la cual se hace el recuento. De forma más general:

Note

atributo.método(argumento)

Lo que puede ser atributo y lo que puede ser argumento varia de método en método y se tiene que aprender de memoria. Se suele llamar la sintaxis del método.

3.2.4. Como limpiar una cadena

Hay un grupo de métodos que modifican las propiedades de una cadena, ejemplificados abajo. Puedes adivinar lo que hacen por sus nombres:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> C = 'yO tE qUiEro'
>>> C.lower()
>>> C.upper()
>>> C.swapcase()
>>> C.capitalize()
>>> C.title()
>>> C.replace('O','o')
>>> C.strip('y')
>>> C2 = ' '+C+' '
>>> C2
>>> C2.strip()

¿Has entendido la diferencia entre title() and capitalize()? Quizás el único que necesita de una explicación es strip(), el cual quita una sub-cadena por los bordes de una palabra. Si no se especifica una sub-cadena que quitar en el argumento, por defecto se quita cualquier espacio en blanco.

3.3. Como andar por una cadena

Otro cosa útil que se puede hacer con una cadena es extraerle una sub-cadena, lo cual presupone un medio para llevar la cuenta de una secuencia de caracteres, o sea, su ordinalidad. Python representa la ordinalidad asignando a cada caracter un número entero que fija su lugar, llamado un índice.

3.3.1. Como encontar un lugar dado un caracter

Puedes pedirle a Python el índice de un caracter con los métodos index() or rindex(), con la cadena como atributo y el caracter como argumento:

1
2
3
4
5
6
7
>>> C = 'electroencefalograma'
>>> C.index('c')
>>> C.rindex('c')
>>> C.index('m')
>>> C.rindex('m')
>>> C.index('e')
>>> C.rindex('e')

El método find() parece hacer lo mimso:

1
2
3
4
5
6
>>> C.find('c')
>>> C.rfind('c')
>>> C.find('m')
>>> C.rfind('m')
>>> C.find('e')
>>> C.rfind('e')

Se distinguen en como responden a una entrada que no existe en la cadena:

1
2
3
4
5
6
7
8
9
>>> C.find('z')
-1
>>> C.index('z')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: substring not found
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: substring not found

Al no encontrar el caracter, find() responde con -1, lo cual podría servir para procesamiento poterior, mientras que index() para el procesamiento con un mensaje de error.

Pero he evitado lo mejor – ¿qué número entero señala el primer lugar?

3.3.1.1. Indización a base de cero

A primera vista, es lógico pensar que el primer caracter de una cadena lleve el índice 1, pero en la realidad Python lo indica con 0 y el segundo caracter lleva 1. Este formato tiene algunas ventajas que no nos importan aquí, pero para ayudarte a comprenderlo, cito un ejemplo del mundo real. En Europa, los pisos de un edificio se numeran de tal forma que la planta baja es la ceroésima y la siguiente es la primera. Asimismo, en el recuento de siglos, los años de 0 a 99 deberían componer el ceroésimo siglo. No es así y se llaman el primer siglo.[#]_

Sea como fuera, para que esta noción te quede grabada en la memoria, te invito a examinar con detenimiento la imagen que hay abajo:

_images/StringIndexation.png

Lo que viene a decir es que la indización cuenta los intervalos entre caracteres y no los caracteres en sí. Tal como un niño no cumple el primer año hasta que no llegue al final del primer año, el primer caracter de una cadena ocupa el ceroésimo intervalo. ¿Puedes adivinar porqué este esquema no tiene lugar en el sentido inverso, contando desde el final? [1]

3.3.1.2. Como encontrar el índice de una sub-cadena

index() and find() también encuentran sub-cadenas:

1
2
3
4
5
6
>>> C.find('electro')
>>> C.index('electro')
>>> C.find('encefalo')
>>> C.index('encefalo')
>>> C.find('grama')
>>> C.index('grama')

Devuelven el índice del primer caracter de la sub-cadena.

3.3.1.3. Como limitar la búsqueda a una sub-cadena

Los dos métodos permiten argumentos opcionales para el principio y fin de una sub-cadena, para limitar la búsqueda a sus fines:

1
2
3
4
>>> C.index('ele', 0, 6)
>>> C.rindex('ele', 0, 6)
>>> C.find('ele', 0, 6)
>>> C.rfind('ele', 0, 6)

Note

index/find(cadena, principio, fin)

3.3.2. Como encontrar un caracter dada una posición

Ahora estás listo para enterder lo que estas expresiones hacen:

1
2
3
4
5
>>> C = 'abcdefgh'
>>> C[2]
>>> C[5]
>>> C[-6]
>>> C[-3]

Un solo número entero entre corchetes señala una posición y por lo tanto retorna el caracter que hay allí.

3.3.2.1. Como cortar una cadena

A los corchetes se les puede añadir un segundo número entero:

1
2
3
4
>>> C[2:5]
>>> C[-6:-3]
>>> C[-6:5]
>>> C[5:-6]

Dos números enteros entre corchetes y separados por un colon extraen los caracteres del primero hasta, pero sin incluir, el segundo. Un número entero positivo indiza la cadena del principio al final; uno negativo la indiza del final al principio. De todas formas, la secuencia se tiene que recorrer de lo bajo a lo alto. Se puede mezclar positivos y negativos, siempre respetando la secuenciación de bajo a alto. Si no, el resultado es la cadena nula o vacia ''. En Python, se dice que los corchetes llevan a cabo cortar, inglés slicing.

Si un corte no indica la posición inicial o final, por defecto Python recorre del principio o hasta el final:

1
2
3
4
5
>>> C[2:]
>>> C[-2:]
>>> C[:2]
>>> C[:-2]
>>> C[:]

3.3.2.2. Como cortar una cadena por pasos

La sintaxis del corte permite un tercer argumento, anexionando un colon y número entero adicionales. ¿Qué hacen estas expresiones?:

1
2
3
4
>>> C[::1]
>>> C[::2]
>>> C[::3]
>>> C[::4]

El tercer argumento se llama el argumento de paso. Línea 1 recorre la cadena un caracter por uno y retorna la cadena entera. Línea 2 recorre la cadena dos por dos – como en la historia del arco de Noé de Genesis, donde los animales abordaron el arco en parejas, dos por dos – y retorna los caracters pares. Línea 3 recorre la cadena tres por tres y el 4, cuatro por cuatro.

Por supuesto puedes poner los primeros dos argumentos para cortar una sub-cadena:

1
2
3
4
>>> C[1:7:1]
>>> C[1:7:2]
>>> C[1:7:3]
>>> C[1:7:6]

Para resumir, el formato de un corte es:

Note

cadena[principio:fin:paso]

3.3.2.3. Como invertir una cadena

¿Qué ocurre si el argumento de paso es negativo?:

1
2
3
4
>>> C[::-1]
>>> C[::-2]
>>> C[::-3]
>>> C[::-4]

Lo que ocurre es que la cadena se procesa en el sentido inverso, empezando por el final. El resultado es la inversión de la cadena.[#]_

3.3.2.4. Como combinar un corte con otras operaciones

El resultado de un corte es una cadena, la cual puede servir de entrada a otra operación sobre cadenas:

1
2
3
4
5
>>> C[:-1] + '!'
>>> C[:2] + C[2:]
>>> C[-2:] * 2
>>> C[5].upper()
>>> C.upper()[5]

Líneas 4 y 5 plantean una duda: el resultado es el mismo, 'F', en los dos casos, pero ¿es el procesamiento el mismo?

Para agudizarte la intuición sobre el problema, vamos a tratar de plantearnoslo de forma visual, como en el diagrama abajo:

_images/3-CorteAmbiguidad.png

Por la izquierda, véase línea 4, y leyendo de abajo hasta arriba, la cadena C es la entrada al corte, que da 'f' como resultado. Esta, a su vez, es entrada a upper(), que produce 'F'. upper() sólo procesa una letra, porque el corte anterior ha quitado las otras siete. En cambio, por la derecha, , véase línea 5, y leyendo de abajo hasta arriba, la cadena C es la entrada a upper(), lo cual produce ‘ABCDEFGH’. El corte las reduce a 'F'. En este caso, upper() procesa las ocho letras, y por lo tanto línea 5 debería ser más lenta que línea 4.

3.3.2.5. La noción del alcance de un operador

Hay un giro para hablar de la diferencia entre líneas 4 y 5, tomado prestado de la lógica. Se llama el alcance o ámbito de un operador. Centrandonos en el corte en el diagrama, por la izquierda es la primera operación y es la que está más baja en el árbol. Se dice que tiene alcance estrecho. Por lo contrario, por la derecha, es la última operación y es la que está más alta en el árbol. Se dice que tiene alcance amplio.

Como regla general, se puede decir que cuánto más estrecho es el elcance de un corte, más rápida es el procesamiento.

3.4. Como manejar cadenas de más de una línea

Hasta el momento, las cadenas que has manejado encajan cómodamente en una sola línea, pero en cuanto te ocupes de textos enteros, no va a ser así. Abajo hay cuatro maneras de crear cadenas multilineales:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> 'Para bailar la bamba'\
... 'se necesita un poco de gracia.'
'Para bailar la bambase necesita un poco de gracia.'
>>> ('Para bailar la bamba'
... 'se necesita un poco de gracia.')
'Para bailar la bambase necesita un poco de gracia.'
>>> '''Para bailar la bamba
... se necesita un poco de gracia.'''
'Para bailar la bamba\nse necesita un poco de gracia.'
>>> """Para bailar la bamba
... se necesita un poco de gracia."""
'Para bailar la bamba\nse necesita un poco de gracia.'

La primera agrega las dos cadenas con una raya oblicua inversa y la segunda metiéndolas entre parentesis. El resultado es igual. Como no hay ningún caracter entre 'bamba' y 'se', Python las une en la ‘palabra’ 'bambase'. Las otras dos agrega las cadenas dentro de comillas sencillas y dobles, lo cual conserva la división en líneas con '\n', el caracter de línea nueva. Fíjate que en los cuatro casos, Python sabe esperar a que termines de entrar la cadena. Este es el significado del los tres puntos o puntos suspensivos, que conforman una señal iniciadora de línea o caracter de petición para que sigas esribiendo.

La presencia del caracter de línea nueva abre una posibilidad de formateo adicional, que se revela con la expresión de print:

1
2
3
4
5
6
7
8
>>> c1 = 'Para bailar la bamba'\
... 'se necesita un poco de gracia.'
>>> c1
>>> print c1
>>> c2 = '''Para bailar la bamba
... se necesita un poco de gracia.'''
>>> c2
>>> print c2

Lo que hace print es mostrar el formateo de una cadena. Con un caracter de línea nueva, print reproduce la división original; sin él, la división original se pierde.

3.5. De cadenas a listas, pasando por casos y tipos

3.5.1. Como poner una cadena en orden alfabético con sorted()

¿Qué hace el métido sorted()?

1
2
3
4
5
>>> C = 'electroencefalograma'
>>> sorted(C)
['a', 'a', 'a', 'c', 'c', 'e', 'e', 'e', 'e', 'f', 'g', 'l', 'l', 'm', 'n', 'o', 'o', 'r', 'r', 't']
>>> sorted(C, reverse=True)
['t', 'r', 'r', 'o', 'o', 'n', 'm', 'l', 'l', 'g', 'f', 'e', 'e', 'e', 'e', 'c', 'c', 'a', 'a', 'a']
1
2
3
4
>>> len(sorted(C))
>>> set(C)
>>> sorted(set(C))
>>> len(set(C))

3.5.2. La diferencia entre un caso y un tipo

El proceso de suprimir repeticiones realizado por set() plantea un concepto fundamental de la computación lingüística, el de la distinción entre un caso y un tipo. Una representación en el cual se permiten repeticiones consiste en casos, mientras que una en el cual no se permiten repeticiones consiste en tipos. De esa forma, set() convierte los casos de una cadena en tipos. En 'cenicienta' hay un tipo de 'c', pero dos casos de 'c'. [2]

3.6. La asignación y la mutabilidad

3.6.1. La asignación de una expresión a un variable

Has efectuado ya varias asignaciones de un variable a una expresión y es de esperar que hayas interiorizado algo así como:

Note

variable = expresión

Se lee como “variable se asigna a expresión”. Por comódidad, a menudo se refiere al variable como el nombre de la expresión, aunque la documentación de Python prefiere identificador a nombre.

3.6.1.1. Que limitaciones tiene un nombre

Un nombre tiene varias limitaciones. La principal es que no puede ser una de las palabras que Python reserve para sí, como:

>>> print = 'zzz'

Hay una lista de tales palabras reservadas aquí.

Un caso más difícil es el de los métodos de Python, tales como los de cadenas revisados en este capítulo:

>>> len = 'zzz'

Asignar a uno de ellos no se prohibe pero está mal visto porque se puede confundir con el método len() y llevar a un malentendido. No quieres crear la posibilidad de expresiones como len(len) en tu código.

También hay limitaciones en los caracteres que pueden formar parte de un nombre. Un nombre debe empezar con una letra o un subrayado, pero no con un dígito:

1
2
3
>>> nombre = 'zzz'
>>> _nombre = 'zzz'
>>> 0nombre = 'zzz'

Al empezer un nombre de forma correcta, puede seguir con cualquier combinación de letras, dígitos o subrayados, pero nada más:

1
2
3
4
>>> mi_nombre = 'zzz'
>>> mi_nombre1 = 'zzz'
>>> mi nombre = 'zzz'
>>> mi-nombre = 'zzz'

Letras minúsculas y mayúsculas son distintas:

1
2
3
>>> nombre = 'zzz'
>>> Nombre = 'zzz2'
>>> nombre == Nombre

3.6.2. Que es la mutabilidad

Intenta esto:

>>> nombre[0] = 'b'

Te he pedido que asignes 'b' al primer caracter de nombre, pero Python no te lo permite. Asimismo, Python tiene una instrucción de borrado – con la sintaxis de print – pero no borra el primer caracter de nombre tampoco:

>>> del nombre[0]

Es porque una cadena es inmutable, lo cual significa que una vez que se ha creado, no se puede cambiar agregándole o suprimiéndole caracteres. La única forma de modificarla es através de los métodos revisados arriba.

3.7. Como controlar el tiempo de procesamiento

Has acabado con el contenido de este capítulo, pero hay un tema que

It would be helpful to demonstrate the contrast overtly and objectively. One possibility is to check whether the difference in processing scope is reflected in a difference in processing time. Python has a resource for measuring how long it takes to do something, but it takes a function as input, rather than a string. Thus we need to take a slight detour to talk about how to define a function.

3.7.1. How to define a function

For our purposes, we will consider a function to be a method that you define yourself. By way of illustration, the following lines of code turn the two slices above into functions:

1
2
3
4
5
6
7
8
9
>>> def f1():
...     s = 'abcdefgh'
...     s.upper()[5]
...

>>> def f2():
...     s = 'abcdefgh'
...     s[5].upper()
...

Python does a couple of new things here. After the colon, it knows to wait for you to type in the body of the function, which is indicated by changing the prompt to the ellipsis (three dots). But you must insert a tab, so that your code lines up beneath f. A function ends with a blank line, which tells Python to return to its regular prompt.

We can now time how long it takes to process each function.

3.7.2. The timeit module

To get the resources for measuring processing time, import the timeit module and then use either function as argument to the timeit method. When using a method of an imported module, the module name must be added as an attribute so that Python can find the method:

1
2
3
>>> import timeit
>>> timeit.timeit(f1)
>>> timeit.timeit(f2)

How speedy are they? On most computers, the second is about a third faster than the first.[#]_

A concluding thought: you may have wondered why the string s was assigned twice, once in each function. If assigned for one, shouldn’t it be available for the other? Check whether it is by typing it at the prompt:

>>> s

You should get a NameError saying that s is not defined. This is another instance of scope. A variable assigned within a function is only accessible to the function, and not to any code outside of it. That is to say, its scope is limited to the function. If you are starting to suspect that scope is a pervasive organizing principle of computer programs, you are right.

3.8. Summary

By now, you should be familiar with the following pythonic concepts: string, concatenation, token, type, method, zero-based indexation, slicing, function, scope, assignment, name or identifier, reserved word, mutability.

3.9. Further practice

Let us repeat that slices are string objects, so they can be used as attributes to these methods:

1
2
3
4
5
>>> S = 'ABCDEFGH'
>>> s = 'abcdefgh'
>>> S[:3].lower()
>>> s[:3].upper()
>>> S[:3].capitalize()

Moreover, processing of attributes proceeds from left to right, so they can be concatenated:

1
2
3
>>> S[:3].capitalize().upper()
>>> s[:3].capitalize().lower()
>>> (s[:4].upper()+S[4:].lower()).swapcase()

TO DO

3.10. Further reading

Check String Methods in Python 2.7’s documentation for more information on the methods reviewed in this chapter, plus some others.

Most of the properties of strings that are introduced here are due to the fact that they are sequence types, which we will cover more thoroughly when we get to lists.

One of the main properties of strings that has been omitted from this chapter is how to format them. We will take that up as the need arises.

3.11. Appendix

Footnotes

[1]Véase el artículo de Wikipedia de Caso y tipo para más información.
[2]See Python strings - why do character positions start with ‘0’, extended slicing with string in python and Zero-based numbering in Wikipedia.
[3]The negative direction starts at -1 because there is no -0 in any commonly-used integer system.
[4]I have always found this discussion on StackOverflow about how to reverse a string in Python to be helpful.
[5]For more information on the timeit module, see timeit in the Python documentation and the tutorial Time a Python Function

Last edited: January 26, 2015

Table Of Contents

Previous topic

2. An Introduction to Python

This Page