Los strings en Python son sin duda uno de los objetos más interesantes que posee el lenguaje. Un String como sabemos no es más que un arreglo de caracteres, los cuales ya poseen un orden y una posición establecida. Veamos un par de ejemplos.
mensaje = 'Hola mundo'
cantidad_caracteres = len(mensaje)
>>> print( cantidad_caracteres)
7
>>> print( mensaje[0] ) # Primer carácter
H
>>> print( mensaje[ cantidad_caracteres - 1 ] ) # Último carácter
o
En este caso mi variable mensajes posee una longitud de 7 caracteres (Hola mundo). El espacio, por supuesto, es tomado en cuenta. 😃
Al igual que una lista o una tupla, los strings en Python se rigen por la regla de los índices. En este caso a cada carácter le pertenece una posición dentro del string. Para nosotros acceder a un carácter en especial basta con apoyarnos de corchetes y el índice del cual queremos conocer su valor .
>>> print( mensaje[0] ) # Primer carácter
H
De igual forma, es importante mencionar, en Python los strings son objetos inmutables. Una vez nosotros definamos un string, este no podrá ser modificado en tiempo de ejecución. Si ejecutamos el siguiente ejemplo obtendremos como resultado un error.
mensaje = 'Hola mundo'
mensaje[1] = 'O'
TypeError Traceback (most recent call last)
<ipython-input-72-3d27ce0e62a1> in <module>
----> 1 mensaje[1] = 'O'
TypeError: 'str' object does not support item assignment
Si bien es cierto un string no puede ser modificado, es posible crear uno nuevo a partir de otros.
>>> mensaje = 'Hola'
>>> nombre = 'Cody'
>>> nuevo_mensaje = mensaje + ' ' + Cody
>>> print(nuevo_mensaje)
Hola Cody
En este caso, como podemos observar, estoy creando un nuevo string a partir de tres ya existentes (la variable mensaje, el espacio y la variable nombre).
Veamos otro ejemplo.
>>> nombre = 'cody'
>>> titulo = nombre[0].upper() + nombre[1:]
>>> print(titulo)
>>> Cody
Para este ejemplo creo un nuevo string utilizando la variable nombre como base. La única modificación ocurren en el primer carácter (Convertimos a mayúsculas).
Hasta aquí muy probablemente ya conocías todas estas cualidades de los strings, sin embargo ¿Habías escuchado hablar de string interning ? 😲 ¿No? pues no te preocupes, en esta ocasión me gustaría explicar como Python maneja la memoria al momento de crear objetos inmutables, tal y como son los objetos de tipo string. Es algo bastante interesante, y sin duda puede llegar hacernos de mucha utilidad.
Bien, Sin más preámbulos, comencemos.
String interning
Comencemos con un par de ejemplos. ¿Cúal crees que sería el resultado de ejecutar los siguiente script?
>> a = 'Cody'
>> b = 'Cody'
>>> a is b
>>> c = 'Cody!'
>>> d = 'Cody!'
>>> c is d
Recordemos que la palabra reservada is nos permite conocer si dos objetos son el mismo.
Tomate tu tiempo. Quizás el resultado sea ¿True True? ¿False False? ¿True False? o ¿False True? no ejecutes nada, simplemente piensalo.
Bien, aquí va la respuesta. Si ejecutamos ambos scripts obtendremos como resultado True y False. ¿Sorprendido? ¿A qué se debe esto? Deja te explico. 😋
En Python al crear objetos inmutables, como lo son los strings, habrá ocasiones en las cuales múltiples variables hagan referencia al mismo objeto, esto claramente se puede ver en el primer ejemplo.
>> a is b
True
Si examinamos el id de cada objeto estos serán los mismos.
>>> id(a)
4365205152
>>> id(b)
4365205152
A esto se le conoce como String interning y el lenguaje lo implemente con la finalidad de ahorrar memoria y evitar crear nuevos objetos de forma innecesaria. Con esto en mente el primer resultado tiene todo el sentido del mundo, a is b
True, sin embargo ¿por qué en el segundo ejemplo el resultado es False? Bien, esto se debe a que Python posee "ciertas reglas" para que un objeto pueda, o no ser interning (O internado, por su traducción al español, aun que esto no suena muy bien).
En el caso de los strings, estos debe de cumplir con por lo menos una de las siguientes reglas.
- Todos los strings longitud 0 o longitud 1 serán interning.
- Los strings los cuales no están compuestos por letras de código ASCII, dígitos o guiones bajos no será interning.
Con esto en mente el segundo ejemplo ya toma sentido. El resultado es False por el signo de admiración (!) _'Cody!' _.
Veamos un par de ejemplos para que nos más en claro.
En este caso todas nuestras variables posee una longitud de 1.
>>> a = 'a'
>>> b = 'b'
>>> c = '!'
>>> d = '!'
>>> a is b
True
>>> c is d
True
Para este segundo ejemplo los strings cuentan con un guión bajo.
>>> usuario1 = 'codigo_facilito'
>>> usuario2 = 'codigo_facilito'
>>> usuario1 is usuario2
True
Si colocamos algún carácter que no se encuentre dentro de las letras del código ASCII, el string no podrá ser interning.
>>> a = '@Cody'
>>> b = '@Cody'
>>> a is b
False
### Integer Interning
El interning no solo funciona para los string, no nada de eso. Como mencionamos anteriormente esto se utiliza sobre todos los objetos inmutables, y los enteros no son la excepción.
En Python al nosotros declarar cualquier número entero que comprenda del -5 al 256 este será cacheado en memoria y serán utilizado como referencia.
>>> numer1 = 120
>>> numer2 = 120
>>> number1 is numer2
True
¿Por qué utilizar interning?
Ahora pasemos a la pregunta clave ¿Por qué es importante conocer los interning? La respuesta es sencilla, por temas de optimización.
Al utilizar interning no solo estaremos ahorrando espacio en memoria, si no que las comparaciones entre objetos serán mucho más eficientes. Por ejemplo, al comparar dos strings Python no recorrerá carácter por carácter para conocer si ambos objetos son el mismo, en lugar de ello comparará su espacio en memoria. Si ambos objetos comparten el mismo espacio en memoria entonces se concluye que son el mismo objeto.
Si bien es cierto todo este conocimiento no influye en el cómo vamos a codificar, si es importante conocerlo para realizar programas cada vez eficientes, y por supuesto, profesionales. 🍻