print 'Lista de números:'
lista_de_numeros = [1, 6, 9, 5, 2]
print lista_de_numeros
print type(lista_de_numeros)

# Pero en ningún lugar dice que la lista tiene que ser sólo
# de números, por lo que podría definir mi lista_de_cosas
# que tenga los mismos números que tenía antes y un string
# en el medio:
print '\nLista de cosas:'
string = 'cadena_de_caracteres'
lista_de_cosas = [1, 6, string, 9, 5, 2]
print lista_de_cosas

lista_de_cosas.append(string)
print lista_de_cosas
print type(lista_de_cosas)


"""
Explanation: <!--
20/10
Tipos de datos compuestos. Estructuras de control repetitivas. 
Índices y slices
Diccionarios como acumuladores/contadores
-->

Tipos de datos compuestos
Listas
En Python podemos guardar varios elementos usando listas
End of explanation
"""


print 'El {} esta en {}?: {}'.format(5, lista_de_numeros, 5 in lista_de_numeros)
print 'El {} esta en {}?: {}'.format(7, lista_de_numeros, 7 in lista_de_numeros)

"""
Explanation: Si se quiere saber si un número se encuentra en una lista o no, es muy simple, sólo hay que usar el operador in:
End of explanation
"""


print 'mundo in "Hola mundo": ', 'mundo' in "Hola mundo"
print 'MUNDO in "Hola mundo": ', 'MUNDO' in "Hola mundo"

"""
Explanation: El operador in también funciona para strings (es case sensitive):
End of explanation
"""


lista_de_numeros = [1, 6, 3, 9, 5, 2]
print lista_de_numeros
lista_de_numeros[3] = 152
print lista_de_numeros

"""
Explanation: A diferencia de los strings, en las listas si podemos cambiar un elemento cualquiera:
End of explanation
"""


lista_de_cosas = [2, 5.5, 'letras', [1, 2, 3], 
                  ('tupla', 'de', 'strings')]
print lista_de_cosas

"""
Explanation: En ningún momento dijimos que la lista era de enteros, por lo que tranquilamente podemos guardar elementos de distintos tipos de datos
End of explanation
"""


lista_de_cosas = [2, 5.5, 'letras', [1, 2, 3],
                  ('tupla', 'de', 'strings')]
print 'Lista de cosas:', lista_de_cosas

del lista_de_cosas[3]
print 'Después de eliminar la posición 3:', lista_de_cosas

lista_de_numeros = [1, 2]

if lista_de_numeros:
    print 'la lista tiene elementos'
else:
    print 'la lista no tiene elementos'


"""
Explanation: Para eliminar un elemento sólo tenemos que usar la función del e indicar la posición.
End of explanation
"""


print 'primer elemento:', lista_de_cosas[0]
ultimo = lista_de_cosas[-1]
print 'último:', ultimo
print 'del_segundo_al_ultimo_sin_incluirlo:', lista_de_cosas[1:4]
print 'del_segundo_al_ultimo_sin_incluirlo:', lista_de_cosas[1:-1]
print 'del_segundo_al_ultimo_incluyendolo:', lista_de_cosas[1:]

"""
Explanation: Y con las listas también se pueden hacer slices:
End of explanation
"""


print range.__doc__
print
print 'Ejemplos:'
print '  range(15):', range(15)
print '  range(15)[2:9]:', range(15)[2:9]
print '  range(15)[2:9:3]:', range(15)[2:9:3]
print '  range(2,9):', range(2,9)
print '  range(2,9,3):', range(2,9,3)

"""
Explanation: Existe una función llamada range que crea permite crear listas de números:
End of explanation
"""


tupla = (1, 2, 3, 4)  # Se usa paréntesis en lugar de corchetes
print tupla

tupla = tupla[2:4]
print tupla
print type(tupla)

"""
Explanation: Tuplas
Las tuplas son listas inmutables, es decir, que no se pueden modificar. 
Si no se pueden modificar, ¿para qué existen?. Porque crearlas es mucho más eficiente que crear listas y en muchas ocasiones, como con las constantes, queremos crear variables que no se modifiquen.
End of explanation
"""


print 'Podría usar los registros para guardar los datos de un alumno'

registro = {
    'padron': 98128,
    'nombre': 'Rodriguez, Carlos'
}

print registro
print type(registro)
print

print 'Y una vez que el alumno rinda el parcial, le puedo agregar',
print ' la nota a ese registro'

registro['nota'] = 6
print registro
print 

print 'Incluso le puedo agregar un diccionario que tenga las notas',
print 'de los trabajos prácticos. '
registro['TPs'] = {1: 4, 2: 8}
print registro
print 'Prestar atención que las claves del diccionario de trabajo',
print 'práctico son números enteros.'
print 'La nota del TP 2 es:', registro['TPs'][2]


"""
Explanation: Diccionarios
El equivalente a los registros de Pascal serían los diccionarios, pero éstos también ofrecen mayor flexibilidad:
End of explanation
"""


alumno = {
    'nombre': 'Juan',
    'apellido': 'Perez',
    'nota': 4
}

print 'El alumno {nombre} {apellido} se sacó un {nota}'.format(**alumno)

"""
Explanation: Además, se pueden usar los campos de un registro para armar una forma más simple los strings:
End of explanation
"""


alumno = {
    'nombre': 'Juan',
    'apellido': 'Perez',
    'nota': 2
}
print alumno

alumno['nota'] = 5
print alumno


"""
Explanation: Y si le queremos modificar la nota a un alumno, sólo tenemos que acceder a ese campo y asignarle un nuevo valor:
End of explanation
"""


alumno = {
    'nombre': 'Juan',
    'apellido': 'Perez',
    'parcial': 2
}
print 'Alumno:', alumno


alumno['parcial'] = [2, 6]  # Cambio el tipo de dato de int a list
print 'Agrego la nota del recuperatorio:', alumno

alumno['coloquio'] = 8  # Agrego un nuevo campo
print 'Agrego la nota del coloquio:', alumno

del alumno['parcial']  # Elimino el campo nota
print 'Elimino las notas del parcial:', alumno

"""
Explanation: O incluso se le puede cambiar el tipo de dato a un campo y agregar uno nuevo:
End of explanation
"""


print 'La posición del número 1 es:', id(1)
x = 1
print 'La posición de X que apunta al número 1 es:', id(x)
print '¿Y si al número 1 lo incremento?'
x += 1
print 'La posición de X ahora apunta a:', id(x)
lista_de_unos = [1, 1, 1, 1, 1]
print 'La lista tiene los elementos:', lista_de_unos
posiciones_de_memoria = [id(1), id(1), id(1), id(1), id(1)]

print 'Las posiciones de esos elementos son:', posiciones_de_memoria


"""
Explanation: Algo que hay que tener en cuenta es que el orden en que se asignan los campos a un registro no es el orden interno de esos campos.
Cajas vs Etiquetas
A diferencia de otros lenguajes, Python no trabaja con el concepto de variables como una caja donde podemos guardar valores. En su lugar, usa las variables como etiquetas que hacen referencia a valores, que si bien son parecidos, no son exactamente lo mismo. <br>
Cuando decimos que guardamos un valor en una caja, lo que en realidad hace la computadora es acceder a una posición de memoria y escribir en esa posición. <br>
Ejemplo | Cajas | Etiquetas
-------| ---------- |  ------------
Si a la variable a le asignamos el número 1 | Guardamos el valor 1 en la caja a  | Hacemos que la referencia a apunte al valor 1 
Y cuando a esa variable queremos asignarle el número dos | Pisamos el valor de a y guardamos el valor 2  | Nuestra referencia deja de apuntar a donde lo hacía antes y ahora apunta a una nueva posición de memoria quedando el número 1 sin ser apuntado por nadie  
Y al decir que la variable a es igual a la variable b lo que sucede es que se copia el contenido de a en b | En este caso se crea una nueva caja con el valor 2   | Al copiar el contenido, lo que se copia es la referencia 
Esto en python se puede ver usando la función id:
End of explanation
"""


x = 1
print 'El valor de X es:', x
print 'La posición de X que apunta al número 1 es:', id(x)
print '¿Y si al número 1 lo incremento?'
x += 1
print 'El valor de X es:', x
print 'La posición de X ahora apunta a:', id(x)

"""
Explanation: Tipos de datos mutables e inmutables
En python se pueden diferenciar entre los tipos de datos en mutables e inmutables. <br>
Que una variable sea inmutable significa que cada vez que se la modifica, en realidad, lo que sucede es que se crea un nuevo valor y esa variable pasa a apuntarlo:
End of explanation
"""


x = []
print 'El valor de X es:', x
print 'La posición de X que apunta a la lista vacía es:', id(x)
print '¿Y si le agrego el número 1 a la lista?'
x.append(1)
print 'Ahora, la lista X tiene los elementos:', x
print 'Y sin embargo, la posición a la que apunta X es:', id(x)
print 
x = [1, 2, 3]
print 'Sin embargo, si a X le asignamos una nueva lista:', id(x)

"""
Explanation: En cambio, cuando lo que hacemos es modificar una variable mutable, como pueden ser las listas, lo que sucede es que esa variable sigue apuntando al mismo lugar, pero ahora lo que se modificó es el valor que contiene:
End of explanation
"""


print range(15)
for i in range(15):
    print i

"""
Explanation: Los tipos de datos inmutables son:
* int
* str
* long
* float
* bool
* None
* tuple
Los tipos de datos mutables son:
* list
* dict
* set
Para más información sobre la modificación de variables mutables e inmutables pueden ver el siguiente link
Estructuras de control repetitivas
Así como en Pascal se delimitan los bloques de código con las palabras reservadas begin y end, en Python se usan la indentación (espacios) para determinar qué se encuentra dentro de una estructura de control y qué no.
for
Si queremos imprimir los números del 0 al 14 podemos crear una lista con range y usar el for para imprimir cada valor:
End of explanation
"""


for i in [1, 6, 3, 9, 5, 2]:
    print i


"""
Explanation: Incluso, si queremos imprimir los valores de una lista que nosotros armamos, también podemos hacerlo:
End of explanation
"""


lista = range(15, 30, 3)
print lista
for idx, value in enumerate(lista):
    print '{0}: {1}'.format(idx, value)


"""
Explanation: Y si queremos imprimir cada elemento de la lista junto con su posición podemos usar la función enumerate:
End of explanation
"""


for par in zip([1, 2, 3], [4, 5, 6]):
    print par

"""
Explanation: También se puede usar la función zip para ir tomando los primeros elementos de una lista, después los segundos, y así sucesivamente
End of explanation
"""


for caracter in "Hola mundo":
    print caracter


"""
Explanation: Y en realidad, se puede iterar sobre cualquier elemento iterable, como por ejemplo los strings:
End of explanation
"""


lista = [1, 2, "12", "34", [5, 6]]
print 'La lista tiene los elementos:', lista
for elemento in lista:
    print '{0}*2: {1}:'.format(elemento, elemento*2)


"""
Explanation: También se pueden iterar listas que tengan distintos tipos de elementos, pero hay que tener en cuenta qué se quiere hacer con ellos:
End of explanation
"""


numero = 5
while numero < 10:
    print numero
    numero += 1

"""
Explanation: while
El ciclo while también ejecuta un bloque de código mientras la condición sea verdadera:
End of explanation
"""


lista = range(5)
print 'La lista antes de entrar al while tiene:', lista
while lista:  # Si la lista no esta vacía, sigo sacando elementos
    print lista.pop()

print 'La lista después de salir del while tiene:', lista

"""
Explanation: Las listas tienen una función llamada pop que lo que hace es tomar el último elemento de ella y lo elimina:
End of explanation
"""


lista = range(5)
print 'La lista antes de entrar al while tiene:', lista
while lista:  # Si la lista no esta vacía, sigo sacando elementos
    print lista.pop(0)

print 'La lista después de salir del while tiene:', lista

"""
Explanation: Aunque también podría obtener el primero:
End of explanation
"""


