Introducción a la programación en Python
clase 6

Tipos en Python

Hay diversos tipos de objetos en Python, que a su vez se agrupan en diversas categorías:

categoría de tipo nombre

descripción

None type(None)

el objeto nulo None

Números int
float
complex
bool

entero (precisión arbitraria)
coma flotante
complejo
booleano (True o False)

Secuencias str
list
tuple
range

cadena de caracteres
lista
tuple
rango de enteros

Mapeo dict

diccionario

Sets set
frozenset
set mutable
set inmutable

Como se vio en la clase 2, la función type() devuelve el tipo al que pertenece un objeto:

In [1]:
type(None)
Out[1]:
NoneType
In [2]:
type(3)
Out[2]:
int
In [3]:
type(3.2)
Out[3]:
float
In [4]:
type(True)
Out[4]:
bool
In [5]:
type("hola")
Out[5]:
str
In [6]:
type([2, 3, 5])
Out[6]:
list
In [7]:
type(range(10))
Out[7]:
range

Tuples

Al igual que las listas y las cadenas de caracteres (strings), los tuples son objetos de tipo secuencia.

Son muy similares a las listas y comparten varias de sus funciones y métodos, aunque su principal diferencia es que son inmutables.

Al igual que las listas, los tuples son:

  • secuencias ordenadas de elementos, que se acceden mediante un índice.
  • heterogéneos: sus elementos pueden ser de diferentes tipos, incluyendo listas u otros tuples.
  • multidimensionales: los sets pueden anidarse hasta cualquier nivel arbitrario.

Pero por otra parte, los tuples son:

  • inmutables: al igual que las cadenas, los tuples no pueden modificarse.

Un tuple puede crearse simplemente mediante una secuencia de elementos separados por comas.
Opcionalmente pueden delimitarse los elementos mediante paréntesis (en vez de corchetes, como las listas).
En algunos casos, los paréntesis pueden ser necesarios para evitar ambigüedades. También podemos tomar como criterio siempre usar paréntesis para definir un tuple.

In [8]:
tuple1 = 1, 2, 3
tuple2 = (4, 5, 6)
print(tuple1, type(tuple1), sep='\t')
print(tuple2, type(tuple2), sep='\t')
(1, 2, 3)	<class 'tuple'>
(4, 5, 6)	<class 'tuple'>

Aternativamente, puede crearse un tuple mediante la función tuple(), con cualquier iterable como argumento:

In [9]:
tuple1 = tuple([1, 2, 3])
tuple2 = tuple(range(4, 7))
print(tuple1, type(tuple1), sep='\t')
print(tuple2, type(tuple2), sep='\t')
(1, 2, 3)	<class 'tuple'>
(4, 5, 6)	<class 'tuple'>

Un tuple vacío puede crearse con un par de paréntesis vacíos:

In [10]:
tuple1 = ()
print(tuple1, type(tuple1), sep='\t')
()	<class 'tuple'>

Para crear un tuple de un solo elemento es necesario poner una coma después del mismo, de lo contrario se evaluará la expresión.
Esto es independiente del uso de paréntesis.

In [11]:
no_tuple1 = 3*4
no_tuple2 = (3*4)
tuple1 = 3*4,
tuple2 = (3*4,)
print(no_tuple1, type(no_tuple1), sep='\t')
print(no_tuple2, type(no_tuple2), sep='\t')
print(tuple1, type(tuple1), sep='\t')
print(tuple2, type(tuple2), sep='\t')
12	<class 'int'>
12	<class 'int'>
(12,)	<class 'tuple'>
(12,)	<class 'tuple'>

Funciones y métodos de tuples

Los tuples disponen de todas las funciones y los métodos comunes a las secuencias inmutables, vistas anteriormente.

T1 + T2 concatenación
T * N, N * T repetición
X in T
X not in T
pruebas de pertenencia
T[i] acceso por indexado
T[i:j], T[i:j:k] acceso por particionado, con incremento opcional
len(T) longitud
min(T), max(T) ítem mínimo y máximo
for item in T:
[expr for item in T]
iteración

También admite dos métodos específicos:

.index()
.count()

Estos métodos funcionan de la misma manera que en las listas, tal como fue visto en la clase pasada.

Ejemplos

In [12]:
tuple1 = (1, 2)
tuple1 *= 3
tuple2 = (3, 4, 5, 6)
mi_tuple = tuple1 + tuple2
print(mi_tuple)
print(mi_tuple[2])
print(mi_tuple[3:6])
print(9 in mi_tuple)
print(6 in mi_tuple)
print(max(mi_tuple), min(mi_tuple))
print(len(mi_tuple))
print([x**3 for x in mi_tuple])
print(mi_tuple.index(4))
print(mi_tuple.count(2))
(1, 2, 1, 2, 1, 2, 3, 4, 5, 6)
1
(2, 1, 2)
False
True
6 1
10
[1, 8, 1, 8, 1, 8, 27, 64, 125, 216]
7
3

Zip

La función zip permite recorrer más de un iterable en paralelo, y devuelve una secuencia de tuples donde cada elemento pertence a cada uno de los iterables.

In [13]:
lista1 = [1, 2, 3, 4]
lista2 = ['a', 'b', 'c', 'd']

# crea una lista por comprensión
lista_zip1 = [(x, y) for x, y in zip(lista1, lista2)]
print(lista_zip1)

# crea una lista mediante la función list()
lista_zip2 = list(zip(lista1, lista2))
print(lista_zip2)
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]

La función zip puede utilizarse en todo tipo de estructuras en las que se recorren iterables.
Los iterables pueden ser de cualquier tipo (listas, tuples, cadenas de caracteres, función range(), etc.).

In [14]:
lista = list(zip('hola', range(1,5)))
print(lista)
[('h', 1), ('o', 2), ('l', 3), ('a', 4)]

In [15]:
for (x, y) in zip('hola', range(1,5)):
    print(x*y)
h
oo
lll
aaaa

Si los iterables no tienen la misma longitud, la función zip() ajusta la iteración a la longitud del más corto.

In [16]:
lista = list(zip("cadena larga", "foo"))
print(lista)
[('c', 'f'), ('a', 'o'), ('d', 'o')]

Ejemplo 6.1

Dada una lista de alturas en nota MIDI, y una lista de duraciones en fracciones de tiempo, generar una lista de notas en forma de tuples (altura, duración). Imprimir en columnas.

In [17]:
# listas con las alturas y las duraciones
alturas = [72, 62, 59, 56, 67, 70, 66, 57, 63, 76, 73, 65]
duraciones = [1.5, 1.75, 0.25, 1, 1.5, 1, 0.5, 0.5, 0.75, 0.75, 0.75, 0.75]

# lista con las notas en forma de tuples
notas = list(zip(alturas, duraciones))

# imprimir la lista, y en columnas
print(notas)
for i in notas:
    print(i[0], i[1], sep='\t') 
[(72, 1.5), (62, 1.75), (59, 0.25), (56, 1), (67, 1.5), (70, 1), (66, 0.5), (57, 0.5), (63, 0.75), (76, 0.75), (73, 0.75), (65, 0.75)]
72	1.5
62	1.75
59	0.25
56	1
67	1.5
70	1
66	0.5
57	0.5
63	0.75
76	0.75
73	0.75
65	0.75

sets

Los sets son colecciones no ordenadas de elementos únicos. A diferencia de las secuencias, sus elementos no pueden accederse por indexado ni particionado.
Hay dos typos de sets: set, que es mutable, y frozenset, que es inmutable.
Ambos tipos de sets pueden crearse con las respectivas funciones set() y frozenset().
Estas funciones pueblan el set iterando sobre el argumento dado.

In [18]:
a = set([1, 2, 3, 2, 1, 4, 5, 3])
print(a, type(a))
{1, 2, 3, 4, 5} <class 'set'>

In [19]:
a = frozenset([1, 2, 3, 2, 1, 4, 5, 3])
print(a, type(a))
frozenset({1, 2, 3, 4, 5}) <class 'frozenset'>

Los elementos de un set son únicos (sin repeticiones dentro del set), y deben ser objetos inmutables: números, cadenas de caracteres, tuples y sets inmutables, pero no listas ni sets mutables.

In [20]:
a = set(["foo", (2, 1, 4), 5, 3])
print(a)
{'foo', (2, 1, 4), 3, 5}

In [21]:
a = set(["foo", frozenset([2, 1, 4]), 5, 3])
print(a)
{'foo', 3, 5, frozenset({1, 2, 4})}

In [22]:
a = set(["foo", [2, 1, 4], 5, 3])
print(a)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-22-e21b9686c485> in <module>()
----> 1 a = set(["foo", [2, 1, 4], 5, 3])
      2 print(a)

TypeError: unhashable type: 'list'
In [23]:
a = set(["foo", set([2, 1, 4]), 5, 3])
print(a)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-23-4b1d1e5f3df1> in <module>()
----> 1 a = set(["foo", set([2, 1, 4]), 5, 3])
      2 print(a)

TypeError: unhashable type: 'set'

Ejemplo 6.2

Crear una función que determine el pitch-class set de una secuencia de alturas dada como argumento. Utilizar para ello la función que devuelve el pitch-class de una altura.

In [24]:
def pitch_a_pc(pitch):
    '''convierte una altura en número de nota MIDI a pitch-class'''
    pc = pitch % 12
    return pc

def pcset(alturas):
    '''devuelve el pitch-class set de una secuencia de alturas'''
    pc_set = set([pitch_a_pc(i) for i in alturas])
    return pc_set

# listas con las alturas en nota MIDI
alturas1 = [72, 62, 59, 56, 67, 70, 66, 57, 63, 76, 73, 65]

# imprimir el set
print(pcset(alturas1))
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}

Métodos y operaciones de sets

len()

Devuelve la longitud de un set.

.copy()

Hace una copia del set.

.difference()

Devuelve la diferencia entre dos sets: todos los elementos que están en el primero, pero no en el argumento.

In [25]:
set1 = set([4, 3, 11, 7, 5, 2, 1, 4])
set2 = set([11, 5, 9, 2, 4, 8])
print(set1)
print(set2)
print(set1.difference(set2))
print(set2.difference(set1))
{1, 2, 3, 4, 5, 7, 11}
{2, 4, 5, 8, 9, 11}
{1, 3, 7}
{8, 9}

.intersection()

Devuelve la intersección entre los sets: todos los elementos que están en ambos.

In [26]:
set1 = set([4, 3, 11, 7, 5, 2, 1, 4])
set2 = set([11, 5, 9, 2, 4, 8])
print(set1)
print(set2)
print(set1.intersection(set2))
print(set2.intersection(set1))
{1, 2, 3, 4, 5, 7, 11}
{2, 4, 5, 8, 9, 11}
{2, 11, 4, 5}
{2, 11, 4, 5}

.isdisjoint()

Devuelve True si no hay elementos comunes entre los sets.

In [27]:
set1 = set([4, 3, 11, 7, 5, 2, 1, 4])
set2 = set([11, 5, 9, 2, 4, 8])
print(set1)
print(set2)
print(set1.isdisjoint(set2))
{1, 2, 3, 4, 5, 7, 11}
{2, 4, 5, 8, 9, 11}
False

.issubset()

Devuelve True si el set es un subconjunto del set argumento.

In [28]:
set1 = set([4, 3, 11, 7, 5, 2, 1, 4])
set2 = set([11, 5, 9, 2, 4, 8])
set3 = set([11, 5, 2, 4])
print(set1)
print(set2)
print(set3)
print(set2.issubset(set1))
print(set3.issubset(set1))
{1, 2, 3, 4, 5, 7, 11}
{2, 4, 5, 8, 9, 11}
{2, 11, 4, 5}
False
True

.issuperset()

Devuelve True si el set es un superset del set argumento.

In [29]:
set1 = set([4, 3, 11, 7, 5, 2, 1, 4])
set2 = set([11, 5, 9, 2, 4, 8])
set3 = set([11, 5, 2, 4])
print(set1)
print(set2)
print(set3)
print(set1.issuperset(set2))
print(set1.issuperset(set3))
{1, 2, 3, 4, 5, 7, 11}
{2, 4, 5, 8, 9, 11}
{2, 11, 4, 5}
False
True

.symmetric_difference()

Devuelve todos los elementos que están en un set u otro, pero no en ambos.

In [30]:
set1 = set([4, 3, 11, 7, 5, 2, 1, 4])
set2 = set([11, 5, 9, 2, 4, 8])
print(set1)
print(set2)
print(set1.symmetric_difference(set2))
{1, 2, 3, 4, 5, 7, 11}
{2, 4, 5, 8, 9, 11}
{1, 3, 7, 8, 9}

.union()

Devuelve un set con todos los elementos que están en alguno de los sets.

In [31]:
set1 = set([4, 3, 11, 7, 5, 2, 1, 4])
set2 = set([11, 5, 9, 2, 4, 8])
print(set1)
print(set2)
print(set1.union(set2))
{1, 2, 3, 4, 5, 7, 11}
{2, 4, 5, 8, 9, 11}
{1, 2, 3, 4, 5, 7, 8, 9, 11}


Ejercicio 6.1

Generar una melodía de características isorrítmicas.

  1. Crear una lista de alturas (color) expresadas en nota MIDI, y una lista de duraciones (talea) expresadas en fracciones de unidad de tiempo. Las listas deben tener un número de elementos primos entre sí (por ejemplo, 9 y 7, o 12 y 5, etc.). Aplicando una técnica similar al Ejemplo 6.1, generar una lista de notas expresadas como tuples altura-duración. La longitud de la lista de notas debe ser tal que coincida con el final de una talea y un color. Imprimir la lista de notas en forma de columnas altura-duración.
  2. Dar al código forma de función que acepte como argumentos la lista de alturas y la de duraciones

Ejercicio 6.1b

  1. Extender la función anterior de modo de que acepte un tercer argumento (opcional, por defecto = 0) que represente el tiempo de inicio de la primera nota. Las notas se representarán ahora como tuples altura-tiempo de inicio-duración. Cada nota comienza donde termina la anterior.