Los elementos de un objeto contenedor son simplemente referencias a los correspondientes objetos, como se muestra en la Figura 1.10.
Figura 1.10 - Objeto inmutable que contiene objetos mutables.
La inmutabilidad consiste en no poder modificar las referencias de la tupla, es decir, en no poder efectuar una asignación del siguiente tipo:
Esto significa que no es posible que t[0] haga referencia a un objeto distinto al conjunto con identidad 3072619996. Pero esto no quiere decir que el objeto al cual t[0] se refiere no pueda ser modificado:
El resultado de t[0].add('c') y t[1].append(4) se muestra en la Figura 1.11, en la cual se puede ver cómo los objetos a los cuales t[0] y t[1] se refiere han sido modificados.
Figura 1.11 - Los objetos mutables contenidos en un objeto inmutable pueden ser modificados.
Los atributos son etiquetas
Cuando utilizamos la notación foo.attributo, tanto foo como attributo son etiquetas, cada una de las cuales se refiere al correspondiente objeto:
Así, si hablamos del atributo path del módulo sys, con el término “atributo” entendemos “etiqueta”. El atributo, siendo un sinónimo de etiqueta, hace referencia a un objeto, que en el caso de sys.path es una lista:
Etiquetas y nombres
En la sección Los elementos del código Python hemos dicho que, para crear y representar fácilmente las instancias de los tipos de datos básicos, han sido definidas ciertas formas textuales simbólicas, denominadas literales. Estas formas permiten determinar visualmente tanto el tipo como el valor de las instancias:
Para el resto de tipos de objeto el lenguaje no define una representación literal, también porque a menudo no es posible hacerlo, dado que el concepto de valor no es inmediato como para las instancias de los tipos de datos básicos. Para estos sí es necesario definir atributos que permitan distinguir el objeto respecto a otros objetos del mismo tipo. El elemento de distinción más insignificante en que podemos pensar es el que utilizamos en el día a día para distinguir a las personas entre ellas: el nombre. Para los objetos como las clases, las funciones y los métodos, el nombre se asigna al atributo _ _name_ _ en el momento de su creación, sobre la base del nombre de la etiqueta utilizada para hacer referencia al objeto (para las funciones y las clases) o del nombre del archivo (para los módulos):
Si un objeto tiene un atributo designado para representar el nombre, decimos que el nombre del objeto es la cadena a la cual hace referencia este atributo. Este, cuando existe, no es siempre _ _name_ _. Con los archivos, por ejemplo, se utiliza un atributo más explícito (sin guión bajo), el atributo name, el cual hace referencia a la cadena pasada a open() como primer argumento:
El nombre de una etiqueta es la cadena construida sobre la base de la etiqueta: por ejemplo, el nombre de la etiqueta foo es la cadena 'foo' y el del atributo path del módulo os es la cadena 'path'. La función integrada dir(), cuando se llama sin argumentos, devuelve una lista que contiene los nombres de las etiquetas en el ámbito actual; si no es así, al pasarle un objeto, como hemos visto en secciones anteriores, esta devuelve los nombres de los atributos más significativos de dicho objeto:
Así, el módulo os tiene el atributo path y el nombre de este atributo es la cadena 'path'. El nombre del objeto al cual hace referencia el atributo path de os, en los sistemas Unix-like, es la cadena 'posixpath':
NOTA
En la última sección de este capítulo hablaremos del módulo os, y veremos que en los sistemas Windows os.path hace referencia al módulo ntpath:
Un objeto no siempre tiene un nombre (las instancias de los tipos de datos básicos, por ejemplo, no lo tienen), mientras que una etiqueta sí, porque este lo da la etiqueta en forma de cadena. El nombre de un objeto, por tanto, no tiene nada que ver con el de las etiquetas que a él se refieren, porque, si lo pensamos bien, más de una etiqueta puede hacer referencia al mismo objeto. El único vínculo entre los dos es que, quizás (como para las clases, las funciones y los módulos), el nombre de la etiqueta se utiliza en el momento de la creación del objeto para asignarle el nombre:
Los nombres de las etiquetas tienen distintos contextos de uso. Por ejemplo, permiten recuperar los objetos a los cuales las etiquetas hacen referencia, utilizando los nombres como clave de diccionarios concretos que representan los espacios de nombres (namespace). Aunque hablaremos de ello con mayor profundidad en el Capítulo 4, vamos a mostrar un breve ejemplo, para exponer un caso práctico de uso. Para ello, consideremos la función integrada globals(), la cual devuelve los espacios de nombres global en forma de diccionario. Este último tiene como claves los nombres de las etiquetas globales, y como valores, los correspondientes objetos:
Dicho esto, cuando no exista ninguna posibilidad de error, hablaremos de la etiqueta pero refiriéndonos al objeto. Por ejemplo, en el caso siguiente:
en lugar de decir que "hemos sumado los objetos a los cuales se refieren a y b", diremos simplemente que hemos sumado a y b. Lo mismo es válido para los atributos, para los cuales diremos en el siguiente ejemplo que "hemos añadido un elemento al atributo a de Foo", en lugar de "hemos añadido un elemento al objeto al cual se refiere el atributo a de Foo":
En todos los casos que puedan dar lugar a malentendidos seremos más formales.
Tipos de errores
Los errores en un programa Python se manifiestan en forma de errores de sintaxis o de excepciones.
Errores