La semántica de Python: Variables y Objetos

Esta sección trata sobre la semántica del lenguaje Python (la clase pasada vimos la syntaxis). En vez de cómo escribir "órdenes" para ejecutar en Python, la semántica tiene que ver con el significado de esas oraciones.

Esta sección trata de la semántica de las variables y los objetos, que son las principales maneras en que se pueden almacenar, referenciar, y operar sobre datos (información) en un script de Python.

Las variables son punteros

Asignar valores a variables en Pyhon es simple: sólo hay que poner el nombre de la variable (hay algunas restricciones en cómo se pueden llamar las variables), un signo igual (=) y el valor que se le quiere asignar. Por ejemplo, para asignar el valor 4 a la variable x, entonces:


In [3]:
# asignamos 4 a x
x = 8

In [4]:
print(x)


8

In [8]:
1x = 3


  File "<ipython-input-8-65f96fb4f055>", line 1
    1x = 3
     ^
SyntaxError: invalid syntax

En la mayoría de los lenguajes de programación, las variables pueden ser consideradad como "cajas" en donde ponemos datos, en el lenguaje de programación C, por ejemplo, cuando decimos

int a = 4;

lo que queremos decir es que hay una caja en la memoria de la máquina (de un tamaño en particular, 4 bytes, probablemente en este caso) que está guardando el valor 5. Pero en Python, cuando escribimos

x = 4

estamos básicamente definiendo un puntero llamado x que apunta a otro espacio de memoria que contiene el número 4. Noten que como en Python las variables son punteros, no hay necesidad de declarar la variable, el interprete se da cuenta del tipo (magia sucede aqui), pero nosotros sólo vemos que podemos tipear las variables "dinámicamente" sólo asignando objetos de diferentes tipos:


In [9]:
x = 1         # x is an integer
x = 'hello'   # now x is a string
x = [1, 2, 3] # now x is a list
print(x)


[1, 2, 3]

Alguna gente puede extrañar typing (entendible, el compilador nos grita si nos equivocamos), sin embargo dynamic typing hace que Python sea más fácil de leer y escribir.

Sin embargo, hay una cosa importante que tenemos que tener en cuenta debido a la característica de Python que "variables son punteros". Si dos variables apuntan al mismo espacio de memoria, cambiar uno va a cambiar el otro! Supongan...


In [13]:
x = [1, 2, 3]
y = x

Hemos creado dos variables x e y, ambas apuntando al mismo objeto (la lista [1,2,3]. Entonces, si modificamos la lista por cualquiera de sus nombres, veremos que la otra lista se modifica también!


In [18]:
x.append(4) # agregar 4 a la lista a la que apunta y
print(y) # la lista x también se modificó!


[1, 2, 3, 4, 4, 4, 4, 4]

La conducta les puede parecer rara, pero siempre recuerden que no son "cajitas" las variables en Python, son como "flechas" que apuntan a cajas, y uno puede cambiar esas flechas como crea conveniente! Por ejemplo:


In [19]:
x = 'something else'
print(y)  # y is unchanged
print(x)


[1, 2, 3, 4, 4, 4, 4, 4]
something else

Qué pasó aquí? Bueh, esto tiene sentido porque lo que hicimos es cambiar el puntero de x hacia una dirección de memoria que tiene el string "something else" almacenado en ella. Sin embargo, el puntero de y no se vio afectado, seguía apuntando a la lista!

Números, strings (cadenas de caracteres) son inmutables en Python. No pueden cambiar el valor de ellos, sólo pueden cambiar los valores a los que apuntan las variables. Por ejemplo, tratemos de cambiar el string de x:


In [20]:
x[2] = "x"


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-20-17e731829bad> in <module>()
----> 1 x[2] = "x"

TypeError: 'str' object does not support item assignment

Es decir, no puedo escribir...


In [22]:
print(x[4])


t

... pero si puedo leer esa posición! Con números pasa lo mismo pero es menos obvio:


In [23]:
x = 10
y = x
x += 5  # add 5 to x's value, and assign it to x
print("x =", x)
print("y =", y)


x = 15
y = 10

Cuando llamamos x += 5, técnicamente no estamos modificando el valor 10 al que apuntaba x. Más bien, estamos cambiando la dirección de memoria, ahora x apunta al valor que resulta de la evaluación de 10 + 5.

Todo es un objeto

Python es un lenguaje "orientado a objeto", y cualquier cosa que encuentren en Python, es un objeto.


In [24]:
(5).__add__(5)


Out[24]:
10

In [30]:
s = int("5")

In [32]:
type(s)


Out[32]:
int

In [ ]:
print("hola,"+"mundo")

In [35]:
len(5)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-35-375e86089a3d> in <module>()
----> 1 len(5)

TypeError: object of type 'int' has no len()

Si, muy bonito, pero son edge cases, veamos un poquito más a fondo que cosa son estos objetos. Ya que las variables en Python no están "tipeadas" (typed), los n00bs asumen que Python es un lenguaje "type-free". No, ma'am! No, sir! Toda cosa tiene su tipo en Python:


In [36]:
x = 4
type(x)


Out[36]:
int

In [37]:
type("")


Out[37]:
str

In [38]:
type(2.1)


Out[38]:
float

and so on and so forth. Some objects are actually very complex. We'll see some of them in the future, but the list [] is one of them:


In [39]:
type([])


Out[39]:
list

Python tiene "tipos", entonces. Pero los tipos de datos de Python están asociados a los objetos mismos, no a las variables (por eso el ejemplo type([]) funcionó).

In lenguajes que implementan programación orientada a objetos como Python, un objeto es una entidad que contiene datos, metadatos, y funcionalidad que modifica o "muta" el objeto. Los objetos tienen metadatos, llamados "atributos" (como el len de los strings) y funciones que lo modifican, llamados "métodos". El append() de las listas son un método, por ejemplo. Los atributos y métodos se acceden a través del operador ".", "dot syntax".

Por ejemplo, el método append de las listas se accede a través del punto:


In [42]:
L = [1, 2, 3]
L.append(100)
print(L)


[1, 2, 3, 100]

Como vimos, no sólo objetos complejos en Python tienen atributos y métodos. Por ejemplo, los tipos numéricos tienen los atributos real e imag, por ejemplo que retornan el real e imaginario de ese númemro, si se lo ve como un número complejo.


In [43]:
x = 4.5
print(x.real, "+", x.imag, 'i')


4.5 + 0.0 i

Los métodos son como atributos, excepto que se les puede pasar parámteros a través de paréntesis (como ya vimos en la clase anterior). Por ejemplo:


In [44]:
x = 4.5
x.is_integer()


Out[44]:
False

In [45]:
x = 4.0
x.is_integer()


Out[45]:
True

Cuando decimos que en Python todo es un objeto, no es broma...


In [46]:
type(x.is_integer)


Out[46]:
builtin_function_or_method

In [ ]: