Manipulación de Codificación de Texto
En el lenguaje Python, la conversión entre cadenas de caracteres y secuencias de bytes se gestiona mediante los métodos encode() y decode(). Es fundamental comprender cómo diferentes esquemas de codificación afectan el consumo de memoria.
Al emplear el estándar UTF-8, los caracteres ASCII ocupan un solo byte. Sin embargo, para caracteres no latinos, como los ideogramas chinos, el espaccio requerido aumenta.
texto_ascii = 'python'
datos_binarios = texto_ascii.encode('utf-8')
print(f'Secuencia UTF-8: {datos_binarios}')
La salida mostrará una secuencia de bytes donde cada letra corresponde a un byte. En contraste, los caracteres chinos varían según el esquema:
- UTF-8: Generalmente 3 bytes por carácter chino.
- GBK: Generalmente 2 bytes por carácter chino.
simbolos = '编码'
bytes_utf = simbolos.encode('utf-8')
bytes_gbk = simbolos.encode('gbk')
print(f'UTF-8: {bytes_utf}')
print(f'GBK: {bytes_gbk}')
El proceso inverso, transformar bytes nuevamente a legibilidad humana, se realiza con decode(), especificando el mismo esquema utilizado para la codificación para evitar errores de interpretación.
raw_data = '测试'
codificado = raw_data.encode('gbk')
decodificado = codificado.decode('gbk')
print(f'Original: {raw_data}')
print(f'Recuperado: {decodificado}')
Mecanismos de Copia de Objetos
En Python, la aisgnación de variables no crea una duplicación del objeto en memoria, sino que establece una referencia al mismo. Para duplicar datos, es necesario utilizar métodos específicos de copiado, los cuales se dividen en superficiales y profundos.
Copia Superficial (Shallow Copy)
Una copia superficial crea un nuevo objeto contenedor, pero los elementos internos siguen siendo referencias a los objetos originales. Esto implica que las modificaciones en objetos anidados se reflejarán en ambas variables.
Primero, observemos el comportamiento de la asignación directa:
lista_base = [1, 2, 3]
referencia = lista_base
lista_base.append(99)
print(f'Lista: {lista_base}')
print(f'Referencia: {referencia}')
print(f'¿Misma identidad? {lista_base is referencia}')
Ambas variables apuntan a la misma dirección de memoria. Ahora, utilizando el método .copy():
estructura = [10, 20, [30, 40]]
copia_superficial = estructura.copy()
# Modificamos un elemento interno
estructura[2].append(50)
print(f'Original: {estructura}')
print(f'Copia: {copia_superficial}')
print(f'ID sublista original: {id(estructura[2])}')
print(f'ID sublista copia: {id(copia_superficial[2])}')
Como se observa, aunque la lista exterior es diferente, la sublista interna comparte la misma identidad, propagando los cambios.
Copia Profunda (Deep Copy)
Para lograr una independencia total, encluyendo los objetos anidados, se utiliza el módulo copy con la función deepcopy. Esto recursivamente copia todos los niveles del objeto.
import copy as cp
datos_originales = [1, 2, [3, 4]]
copia_profunda = cp.deepcopy(datos_originales)
datos_originales.append(5)
datos_originales[2].append(6)
print(f'Original: {datos_originales}')
print(f'Copia Profunda: {copia_profunda}')
print(f'¿IDs diferentes? {id(datos_originales) != id(copia_profunda)}')
En este escenario, cualquier alteración en la estructura original, ya sea en el nivel superior o en niveles anidados, no afecta a la copia profunda.
Comparativa Técnica
- Asignación: No hay copia, solo referencia. Los IDs de memoria son idénticos.
- Copia Superficial: El contenedor externo es nuevo, pero los elementos internos son referencias. Afecta a tipos mutables anidados.
- Copia Profunda: Recursividad total. Objetos completamente independientes en memoria.
- Tipos Inmutables: Para enteros o cadenas simples, las diferencias entre copias son menos relevantes debido a la optimización interna de Python.