<jupyter_start><jupyter_text>Introducción a Python: Listas, Iteraciones y Strings> Ya conocemos un poco más de la sintaxis de Python, como hacer funciones y como usar condicionales. Es hora que veamos otros tipos de variables (arreglos) y cómo hacer líneas de código que ejecuten operaciones repetitivas.Referencias:- https://www.kaggle.com/learn/python___  1. ListasLas listas son objetos en Python representan secuencias ordenadas de valores. Veamos un par de ejemplos de como crearlas:<jupyter_code># Primeros números primos
primos = [2, 5, 3, 7]
# Planetas del sistema solar
planetas = ['Mercurio', 'Venus', 'Tierra', 'Marte', 
            'Jupiter', 'Saturno', 'Urano', 'Neptuno']
#Compitas
compitas = ['Armando', 'Samy','Rulaz','El ALex']
primos
planetas
compitas<jupyter_output><empty_output><jupyter_text>Vemos que las listas no son exclusivamente de números. Ya vimos listas de números, pero también de strings.Incluso, se pueden hacer listas de listas:<jupyter_code>lista_primos_planetas = [primos, planetas]
lista_primos_planetas<jupyter_output><empty_output><jupyter_text>Aún más, se pueden hacer listas de diferentes tipos de objetos:<jupyter_code>lista_diferentes_tipos = [2, 0., 'Hola', help, primos]
lista_diferentes_tipos<jupyter_output><empty_output><jupyter_text>Sin duda, en muchas ocasiones nos será muy útil tener una sola lista guardando varios resultados, que muchos resultados guardados en objetos individuales.Pero, una vez en la lista, ¿cómo accedemos a los objetos individuales?  1.1 IndizadoPodemos acceder a los elementos individuales pertenecientes a la lista a través de brackets ([])Por ejemplo, ¿cuál planeta está más cercano al sol en nuestro sistema solar?- Acá una nota importante: Python usa índices comenzando en cero (0):<jupyter_code># Planeta más cercano al sol
planetas[0]
# Siguiente planeta
planetas[1]<jupyter_output><empty_output><jupyter_text>Todo bien...Ahora, ¿cuál es el planeta más alejado del sol?- Los elementos de una lista pueden tambien ser accedidos de atrás para adelante, utilizando números negativos:<jupyter_code># Planeta más alejado del sol
planetas[-1]
# Segundo planeta más alejado
planetas[-2]<jupyter_output><empty_output><jupyter_text>Muy bien...Y si quisiéramos averiguar, por ejemplo, ¿cuáles son los tres planetas más cercanos al sol?<jupyter_code># Tres primeros planetas
planetas[0:3]<jupyter_output><empty_output><jupyter_text>Entonces `lista[a:b]` es nuestra manera de preguntar por todos los elementos de la lista con índice comenzando en `a` y continuando hasta `b` sin incluir (es decir, hasta `b-1`).Los índices de comienzo y de término son opcionales:- Si no ponemos el índice de inicio, se asume que es cero (0): `lista[:b] == lista[0:b]`<jupyter_code># Reescribir la expresión anterior
planetas[:3]
planetas[-3:]<jupyter_output><empty_output><jupyter_text>- Equivalentemente, si no ponemos el índice de fin, se asume que este equivale a la longitud de la lista:<jupyter_code># Lista de todos los planetas comenzando desde el planeta tierra
planetas[2:]<jupyter_output><empty_output><jupyter_text>También podemos usar índices negativos cuando accedemos a varios objetos.Por ejemplo, ¿qué obtenemos con las siguientes expresión?<jupyter_code>planetas[-1]<jupyter_output><empty_output><jupyter_text>```pythonlista[n:n + N] = [lista[n], lista[n + 1], ..., lista[n + N - 1]]```<jupyter_code>planetas[1:-1]
planetas[-3:]
planetas[:4]
planetas[5:]
planetas[:4] + planetas[5:]<jupyter_output><empty_output><jupyter_text>Slice:```pythonlista[n:n+N:s] = [lista[n], lista[n + s], lista[n + 2 * s], ..., ]```<jupyter_code>primos
primos[::2]
# Elementos de la lista en reverso (al revés)
primos[::-1]<jupyter_output><empty_output><jupyter_text>1.2 Modificando listasLas listas son objetos "mutables", es decir, sus objetos pueden ser modificados directamente en la lista.Una manera de modificar una lista es asignar a un índice.Por ejemplo, supongamos que la comunidad científica, con argumentos basados en la composición del planeta, decidió modificar el nombre de "Planeta Tierra" a "Planeta Agua".<jupyter_code>planetas
planetas[2] = 'Agua'
planetas<jupyter_output><empty_output><jupyter_text>También podemos cambiar varios elementos de la lista a la vez:<jupyter_code>planetas[:3] = ['mer', 'ven', 'tie']
planetas
primos
#lista with steps
primos[::2]
#lista with step -1 (Elementos de la lista en reverso)
primos[::-1]<jupyter_output><empty_output><jupyter_text>1.3 Funciones sobre listasPython posee varias funciones supremamente útiles para trabajar con listas.`len()` nos proporciona la longitud (número de elementos) de una lista:<jupyter_code># función len()
len(planetas)
len(primos)<jupyter_output><empty_output><jupyter_text>`sorted()` nos regresa una versión ordenada de una lista:<jupyter_code># Ayuda en la función sorted
help(sorted)
primos
# Llamar la función sorted sobre primos
sorted(primos)
sorted(primos, reverse=True)
planetas = ['Mercurio', 'Venus', 'Tierra', 'Marte', 
            'Jupiter', 'Saturno', 'Urano', 'Neptuno']
# Llamar la función sorted sobre planetas
sorted(planetas)
len('Jupiter')
def long_str(s):
    return len(s)
long_str2 = lambda s: len(s)
long_str("Jupiter"), long_str2("Jupiter")<jupyter_output><empty_output><jupyter_text>**Paréntesis: Funciones anónimas**Las funciones anónimas comienzan con la palabra clave `lambda` seguidas por el (los) argumento(s) de la función. Después de `:` se escribe lo que retorna la función.<jupyter_code>sorted(planetas, key=long_str)
sorted(planetas, key=lambda s: len(s))
# Llamar la función sorted sobre planetas
sorted(planetas)
primos
# Ayuda en la función sum
help(sum)
# sum
sum(primos)<jupyter_output><empty_output><jupyter_text>En la clase pasada utilizamos las funciones `min()` y `max()` sobre varios argumentos.También le podemos pasar un solo argumento tipo lista.<jupyter_code># min
min(primos)
# max
max(primos)<jupyter_output><empty_output><jupyter_text>___ Pausa: ObjetosHasta ahora he venido utilizando la palabra **objeto** sin darle mucha importancia. ¿Qué significa en realidad?- si han visto algo de Python, pueden haber escuchado que todo en Python es un objeto.En la siguiente semana estudiaremos a nivel muy básico qué es la programación orientada a objetos.Por ahora, nos basta con saber que los objetos cargan varias "cosas" con ellos, y podemos acceder a estas "cosas" utilizando la "sintaxis punto (.)" de Python.Por ejemplo, los números en Python tienen una variable asociada llamada `imag`, la cual representa su parte imaginaria:<jupyter_code># Atributos real e imag
a = 7
a.imag, a.real
dir(a)
a.denominator, a.numerator
b = (6 + 5j) / 3
b.real, b.imag
dir(b.imag)
c = 5 / 3
c.as_integer_ratio()
7505999378950827 / 4503599627370496
from fractions import Fraction
Fraction(c).limit_denominator(10)
help(Fraction().limit_denominator)<jupyter_output>Help on method limit_denominator in module fractions:

limit_denominator(max_denominator=1000000) method of fractions.Fraction instance
    Closest Fraction to self with denominator at most max_denominator.
    
    >>> Fraction('3.141592653589793').limit_denominator(10)
    Fraction(22, 7)
    >>> Fraction('3.141592653589793').limit_denominator(100)
    Fraction(311, 99)
    >>> Fraction(4321, 8765).limit_denominator(10000)
    Fraction(4321, 8765)<jupyter_text>Entre las "cosas" que los objetos cargan, también pueden haber funciones. Una función asociada a un objeto se llama **método**.Las "cosas" asociadas a los objetos, que no son funciones, son llamados **atributos** (ejemplo: imag).<jupyter_code># Método conjugate()
b.conjugate()<jupyter_output><empty_output><jupyter_text>Y si no sabemos qué hace un método determinado en un objeto, también podemos pasar métodos a la función `help()`, de la misma manera en que le pasamos funciones:<jupyter_code># help(objeto.metodo)
help(b.conjugate)<jupyter_output>Help on built-in function conjugate:

conjugate(...) method of builtins.complex instance
    complex.conjugate() -> complex
    
    Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.<jupyter_text>Bueno, ¿y esto de que nos sirve?Pues las listas tienen una infinidad de métodos útiles que estaremos usando...___  1.4 Métodos de las listas`list.append()` modifica una lista añadiéndole un elemento en el final:<jupyter_code>planetas = ['Mercurio',
            'Venus',
            'Tierra',
            'Marte',
            'Jupiter',
            'Saturno',
            'Urano',
            'Neptuno']
# Plutón también es un planeta
variable = planetas.append("Pluton")
print(variable)
planetas<jupyter_output><empty_output><jupyter_text>¿Porqué no obtuvumos una salida en la celda de arriba?Verifiquemos la documentación del método append:<jupyter_code>help(planetas.append)
help(list.append)
help(append)<jupyter_output><empty_output><jupyter_text>**Comentario:** append es un método de todos los objetos tipo `list`, de manera que habríamos podido llamar `help(list.append)`. Sin embargo, si intentamos llamar `help(append)`, Python nos dirá que no existe nada con el nombre "append", pues `append` solo existe en el contexto de listas. `list.pop()` remueve y devuelve el último elemento de una lista:<jupyter_code># Que Plutón siempre no es un planeta
planetas.pop()
planetas
help(planetas.pop)
planetas.pop(1)
planetas<jupyter_output><empty_output><jupyter_text>1.4.1 Buscando en listas¿En qué lugar de los planetas se encuentra la Tierra? Podemos obtener su índice usando el método `list.index()`:<jupyter_code>planetas = ['Mercurio',
            'Venus',
            'Tierra',
            'Marte',
            'Jupiter',
            'Saturno',
            'Urano',
            'Neptuno']
planetas
# índice del planeta tierra
planetas.index("Tierra")
planetas[2]
planetas[planetas.index('Tierra'):]<jupyter_output><empty_output><jupyter_text>Está en el tercer lugar (recordar que el indizado en Python comienza en cero)¿En qué lugar está Plutón?<jupyter_code># índice del planeta plutón
planetas.index('Pluton')<jupyter_output><empty_output><jupyter_text>Error ...  ¡como debe ser!Para evitar este tipo de errores, existe el operador `in` para determinar si un elemento particular pertenece a una a una lista:<jupyter_code>planetas
# ¿Es la Tierra un planeta?
'Tierra' in planetas
# ¿Es Plutón un planeta?
'Pluton' in planetas
# Usar esto para evitar el error de arriba
if 'Pluton' in planetas:
    planetas.index("Pluton")<jupyter_output><empty_output><jupyter_text>Hay otros métodos interesantes de las listas que no veremos. Si quieren aprende más acerca de todos los métodos y atributos de un objeto particular, podemos llamar la función `help()` sobre el objeto.Por ejemplo:<jupyter_code>dir(list)
help(list)
primos
primos.extend([11, 13])
primos<jupyter_output><empty_output><jupyter_text>1.5 TuplasTambién son arreglos de objetos similares a las listas. Se diferencian en dos maneras:- La sintaxis para crear tuplas usa paréntesis (o nada) en vez de brackets:<jupyter_code>t = (1, 2, 3)
t
# O equivalentemente
t = 1, 2, 3
t
t[1:]<jupyter_output><empty_output><jupyter_text>- Las tuplas, a diferencia de las listas, no pueden ser modificadas (son objetos inmutables):<jupyter_code># Intentar modificar una tupla
t[1] = 5<jupyter_output><empty_output><jupyter_text>Las tuplas son usadas comúnmente para funciones que devuelven más de un valor.Por ejemplo, el método `as_integer_ratio()` de los objetos `float`, devuelve el numerador y el denominador en la forma de una tupla:<jupyter_code># as_integer_ratio
0.25.as_integer_ratio()
num, den = 0.25.as_integer_ratio()
num
den
# Ayuda en el método float.as_integer_ratio
help(float.as_integer_ratio)<jupyter_output>Help on method_descriptor:

as_integer_ratio(self, /)
    Return integer ratio.
    
    Return a pair of integers, whose ratio is exactly equal to the original float
    and with a positive denominator.
    
    Raise OverflowError on infinities and a ValueError on NaNs.
    
    >>> (10.0).as_integer_ratio()
    (10, 1)
    >>> (0.0).as_integer_ratio()
    (0, 1)
    >>> (-.25).as_integer_ratio()
    (-1, 4)<jupyter_text>También pueden ser usadas como un atajo:<jupyter_code>a = (1, 2)
b = (0, 'A')
a, b = b, a
print(a, b)<jupyter_output>(0, 'A') (1, 2)<jupyter_text>2. Ciclos o iteraciones 2.1 Ciclos `for`Las iteraciones son una manera de ejecutar cierto bloque de código repetidamente:<jupyter_code># Planetas, de nuevo
planetas = ['Mercurio', 'Venus', 'Tierra', 'Marte', 'Jupiter', 'Saturno', 'Urano', 'Neptuno']
# Imprimir todos los planetas en la misma línea
for planeta in planetas:
    print(planeta, end=', ')<jupyter_output>Mercurio, Venus, Tierra, Marte, Jupiter, Saturno, Urano, Neptuno,<jupyter_text>Para construir un ciclo `for`, se debe especificar:- el nombre de la variable que va a iterar (planeta),- el conjunto de valores sobre los que va a iterar la variable (planetas).Se usa la palabra `in`, en este caso, para hacerle entender a Python que *planeta* va a iterar sobre *planetas*.El objeto a la derecha de la palabra `in` puede ser cualquier objeto **iterable**. Básicamente, un iterable es cualquier arreglo (listas, tuplas, conjuntos, arreglos de numpy, series de pandas...).Por ejemplo, queremos hallar la multiplicación de todos los elementos de la siguiente tupla.<jupyter_code>multiplicandos = (2, 2, 2, 3, 3, 5)
# Multiplicación como ciclo
producto = 1
for number in multiplicandos:
    producto *= number
    
producto<jupyter_output><empty_output><jupyter_text>Incluso, podemos iterar sobre los caracteres de un string:<jupyter_code>s = 'steganograpHy is the practicE of conceaLing a file, message, image, or video within another fiLe, message, image, Or video.'
# Imprimir solo los caracteres en mayúscula, sin espacios, uno seguido de otro
for char in s:
    print(char if char.isupper() else '', end='')<jupyter_output>HELLO<jupyter_text>2.1.1 Función `range()`La función `range()` es una función que devuelve una secuencia de números. Es extremadamente útil para escribir ciclos for.Por ejemplo, si queremos repetir una acción 5 veces:<jupyter_code># For de 5 iteraciones
for i in range(5):
    print('Hola, ¡Mundo!')
help(range)
range(4, 8)
list(range(4, 8)), list(range(4, 8, 2))<jupyter_output><empty_output><jupyter_text>**Ejercicio:**1. Escribir una función que devuelva los primeros $n$ elementos de la sucesión de Fibonacci, usando un ciclo `for`.<jupyter_code>def fibonacci_for(n):
    if n == 1:
        fibonacci = [0]
    elif n == 2:
        fibonacci = [0, 1]
    elif n >= 3:
        fibonacci = [0, 1]
        for i in range(n - 2):
            fibonacci.append(fibonacci[-2] + fibonacci[-1])
        
    return fibonacci
fibonacci_for(10)<jupyter_output><empty_output><jupyter_text>2.2 Ciclos `while`Son otro tipo de ciclos en Python, los cuales iteran hasta que cierta condición deje de cumplirse.Por ejemplo:<jupyter_code>i = 0
while i >= 0:
    print(i, end=' ')
    # i = i + 1 es equivalente a i += 1
    i += 1<jupyter_output>0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 27[...]<jupyter_text>El argumento de un ciclo `while` se evalúa como una condición lógica, y el ciclo se ejecuta hasta que dicha condición sea **False**. **Ejercicio:**1. Escribir una función que devuelva los primeros $n$ elementos de la sucesión de Fibonacci, usando un ciclo `while`.2. Escribir una función que devuelva los elementos menores a cierto número $x$ de la sucesión de Fibonacci, usando un ciclo `while`.<jupyter_code>def fibonacci_while(n):
    if n == 1:
        fibonacci = [0]
    elif n == 2:
        fibonacci = [0, 1]
    elif n >= 3:
        i = 2
        fibonacci = [0, 1]
        while i < n:
            fibonacci.append(fibonacci[-2] + fibonacci[-1])
            i += 1
        
    return fibonacci
fibonacci_while(10)<jupyter_output><empty_output><jupyter_text>Pausa: RecursiónUna manera adicional de ejecutar iteraciones se conoce como *recursión*, y sucede cuando definimos una función en términos de sí misma.Por ejemplo, el $n$-ésimo número de la secuencia de Fibonacci, recursivamente sería:<jupyter_code>def fibonacci_recursive(n):
    if n == 1:
        return 0
    elif n == 2:
        return 1
    else:
        return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)
fibonacci_recursive(10)<jupyter_output><empty_output><jupyter_text>2.3 List comprehensions (no encuentro una traducción suficientemente buena de esto)List comprehension son una de las características más chidas de Python. La manera más fácil de entenderla, como muchas cosas, es revisando ejemplos:<jupyter_code># Primero, con ciclo for: listar los cuadrados de los 10 dígitos
sq = []
for i in range(10):
    sq.append(i**2)
sq
# Ahora con una list comprehension
sq = [i**2 for i in range(10)]
sq<jupyter_output><empty_output><jupyter_text>Podemos agregar, incluso, condicionales:<jupyter_code>planetas = ['Mercurio',
            'Venus',
            'Tierra',
            'Marte',
            'Jupiter',
            'Saturno',
            'Urano',
            'Neptuno']
# Ejemplo con los planetas
[planeta for planeta in planetas if len(planeta) < 7]
short_planets = []
for planet in planetas:
    if len(planet) < 7:
        short_planets.append(planet)
        
short_planets<jupyter_output><empty_output><jupyter_text>Se puede usar para dar formato:<jupyter_code># str.upper()
upper_planets = [planet.upper() for planet in planetas]
upper_planets<jupyter_output><empty_output><jupyter_text>Es supremamente importante aprender esto, ya que es ampliamente utilizado y ayuda a reducir muchísimas líneas de código.Ejemplo: escribir la siguiente función usando un ciclo for.<jupyter_code>def cuantos_negativos(iterable):
    """
    Devuelve el número de números negativos en el iterable dado.
    
    >>> cuantos_negativos([5, -1, -2, 0, 3])
    2
    """
    negativos = 0
    for num in iterable:
        if num < 0:
            negativos += 1
    return negativos
cuantos_negativos((5, -1, -2, 0, 3))<jupyter_output><empty_output><jupyter_text>Ahora, con list comprehensions:<jupyter_code>def cuantos_negativos(iterable):
    """
    Devuelve el número de números negativos en el iterable dado.
    
    >>> cuantos_negativos([5, -1, -2, 0, 3])
    2
    """
    return len([num for num in iterable if num < 0])
# Probar la función
cuantos_negativos([5, -1, -2, 0, 3])<jupyter_output><empty_output><jupyter_text>3. Strings y diccionarios 3.1 StringsSi hay algo en lo que Python es la ley es manipulando Strings. En esta sección veremos algunos de los métodos de los objetos tipo string, y operaciones de formateo (muy útiles en la limpieza de bases de datos, por cierto).  3.1.1 Sintaxis stringYa hemos visto varios ejemplos involucrando strings anteriormente. Solo para recordar:<jupyter_code>x = 'Pluton es un planeta'
y = "Pluton es un planeta"
x == y<jupyter_output><empty_output><jupyter_text>Hay casos particulares para preferir una u otra:- Las comillas dobles son convenientes si tu string contiene un apóstrofe.- De manera similar, se puede crear fácilmente un string que contiene comillas dobles englobándolo en comillas simples.Ejemplos:<jupyter_code>print("Pluto's a planet!")
print('My dog is named "Pluto"')
'Pluto's a planet!'
print('Pluto\'s a planet!')
"My dog is named "Pluto""
print("My dog is named \"Pluto\"")<jupyter_output>My dog is named "Pluto"<jupyter_text>3.1.2 Los strings son iterablesLos objetos tipo strings son cadenas de caracteres. Casi todo lo que vimos que le podíamos aplicar a una lista, se lo podemos aplicar a un string.<jupyter_code># string de ejemplo
string_ex = "This is an exAmple"
# Indexado
string_ex[2]
# Indexado multiple
string_ex[:4]
# ¿Cuántos caracteres tiene?
len(string_ex)
# También podemos iterar sobre ellos
[char for char in string_ex if char.lower() in ['a', 'e', 'i', 'o', 'u']]<jupyter_output><empty_output><jupyter_text>Sin embargo, una diferencia principal con las listas, es que son inmutables (no los podemos modificar).<jupyter_code>string_ex[0] = 't'<jupyter_output><empty_output><jupyter_text>3.1.3 Métodos de los stringsComo las listas, los objetos tipo `str` tienen una gran cantidad de métodos útiles.Veamos algunos:<jupyter_code># string de ejemplo
string_ex
# EN MAYÚSCULAS
string_ex.upper()
# en minúsculas
string_ex.lower()
string_ex
# pregunta: comienza con?
string_ex.upper().startswith('TH')
# pregunta: termina con?
string_ex.endswith('a')
string_ex.count('e')
string_ex.find('ex')
help(string_ex.find)
dir(str)<jupyter_output><empty_output><jupyter_text>Entre listas y strings: métodos `split()` y `join()`El método `str.split()` convierte un string en una lista de strings más pequeños.Esto es supremamente útil para tomar de un string cada una de sus palabras:<jupyter_code># Palabras de una frase
string_ex
string_ex.split()
string_ex.split(sep=" ", maxsplit=1)
help(str.split)<jupyter_output>Help on method_descriptor:

split(self, /, sep=None, maxsplit=-1)
    Return a list of the words in the string, using sep as the delimiter string.
    
    sep
      The delimiter according which to split the string.
      None (the default value) means split according to any whitespace,
      and discard empty strings from the result.
    maxsplit
      Maximum number of splits to do.
      -1 (the default value) means no limit.<jupyter_text>O para obtener cierta información:<jupyter_code># Año, mes y día de una fecha especificada como string
"1987/04/10".split("/")<jupyter_output><empty_output><jupyter_text>`str.join()` nos sirve para devolver los pasos. Teniendo una lista de pequeños strings, la podemos convertir en un solo string usando el string sobre el que se llama como separador:<jupyter_code># Con la fecha...
"-".join(['1987', '04', '10'])
"-".join("1987/04/10".split("/"))
"1987/04/10".replace("/", "-")<jupyter_output><empty_output><jupyter_text>3.1.4 Concatenación de stringsPython nos permite concatenar strings con el operador `+`:<jupyter_code># Ejemplo
hola = "Hola"
nombre = "Esteban"
cordial = "¿Cómo estás?"
hola + " " + nombre + cordial<jupyter_output><empty_output><jupyter_text>Sin embargo, hay que tener cuidado:<jupyter_code>"Había " + 21 + " huevos en la canasta"
# Concatenar un string con un número
"Había " + str(21) + " huevos en la canasta"<jupyter_output><empty_output><jupyter_text>3.2 DiccionariosLos diccionarios son otros objetos de Python que mapean llaves a elementos:<jupyter_code>numeros = {'uno': 1, 'dos': 2, 'tres': 3}<jupyter_output><empty_output><jupyter_text>En este caso, los strings "uno", "dos", y "tres" son las llaves, y los números 1, 2 y 3 son sus valores correspondientes.Los valores son accesados con brackets, similarmente a las listas:<jupyter_code>numeros['uno']<jupyter_output><empty_output><jupyter_text>Usamos una sintaxis similar para añadir otro par llave, valor<jupyter_code>numeros['cuatro'] = 4
numeros<jupyter_output><empty_output><jupyter_text>O cambiar el valor asociado a una llave existente<jupyter_code>numeros['uno'] = '1'
numeros<jupyter_output><empty_output><jupyter_text>Navegando entre listas, tuplas, diccionarios: `zip` Supongamos que tenemos dos listas que se corresponden:<jupyter_code>key_list = ['name', 'age', 'height', 'weight', 'hair', 'eyes', 'has dog']
value_list = ['Esteban', 30, 1.81, 75, 'black', 'brown', True]<jupyter_output><empty_output><jupyter_text>¿Cómo puedo asociar estos valores en un diccionario? Con `zip`:<jupyter_code># Primero, obtener la lista de pares
list(zip(key_list, value_list))
# Después obtener diccionario de relaciones
dict(zip(key_list, value_list))
from collections.abc import Iterable
isinstance([1, 2], Iterable)
isinstance((1, 2), Iterable)
isinstance('hola', Iterable)
numeros
isinstance(numeros, Iterable)<jupyter_output><empty_output><jupyter_text>Al ser los diccionarios iterables, puedo iterar sobre ellos (valga la redundancia)<jupyter_code># Iterar sobre diccionario
for key in numeros:
    print(key, ': ', numeros[key])
# Iterar sobre valores
for val in numeros.values():
    print(val)
# Iterar sobre pares llave-valor
for key, val in numeros.items():
    print(key, ':', val)<jupyter_output>uno : 1
dos : 2
tres : 3
cuatro : 4
{'fraction_non_alphanumeric': 0.09908792703416273, 'fraction_numerical': 0.04488359068725498, 'mean_word_length': 5.0064872657376265, 'pattern_counts': {'":': 0, '<': 240, '<?xml version=': 0, '>': 264, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '61', 'dir': 'jupyter-structured-clean-dedup', 'id': '116789', 'license': 'MIT', 'n_tokens_mistral': 9950, 'n_tokens_neox': 8908, 'n_words': 2909, 'path': 'Semana1/Clase2_ListasIteracionesStrings.ipynb', 'repo_name': 'AlexNoelHdz/prope_programacion_2021', 'size': '24998'}