List comprehension¶
This notebook covers roughly the same ground as this video
Remember that i ** 2 is Python notation for $i^2$. The code range(1,20) generates the list [1,2,3,...,17,18,19] (starting at 1 and stopping just before 20). The code [i ** 2 for i in range(1,20)] generates the list of squares of the numbers from 1 to 19. This kind of thing is called a list comprehension.
squares = [i ** 2 for i in range(1,20)] # This is 1^2, 2^2, 3^2, ... , 19^2
print(f'squares={squares}') # Print the list
print(f'{squares=}') # The same thing, using a formatting shortcut introduced in Python 3.8
When using this kind of syntax, the variable i does not have to be taken from a list of consecutive integers like range(1,13), it can be taken from any list that we already have available. For example, we can use the list squares that we defined above. Recall that i % 5 means the remainder of i modulo 5.
squares_mod_five = [i % 5 for i in squares]
print(f'squares_mod_five = {squares_mod_five}')
We now want to make a list of the cubes $i^3$ for all the odd numbers $i$ with $1\leq i\leq 19$. Remember that $i$ is odd if and only if i % 2 is equal to one. Thus, we can make the required list by using a list comprehension as above, but with the filter if i % 2 == 1 at the end. Alternatively, the code range(1,20,2) gives a list of integers starting with 1 and going up in steps of 2, so it gives the odd integers from 1 to 19. We can therefore use a list comprehension based on this with no filter clause.
cubes = [i ** 3 for i in range(1,20)]
odd_cubes0 = [i ** 3 for i in range(1,20) if i % 2 == 1]
odd_cubes1 = [i ** 3 for i in range(1,20,2)]
print(f'cubes = {cubes}')
print(f'odd_cubes0 = {odd_cubes0}')
print(f'odd_cubes1 = {odd_cubes1}')
We now find the squares that are also cubes, in two different ways. The first way is another list comprehension with a filter clause: we walk through the list of squares and keep only the ones that also appear in the list of cubes. For the second method, we convert our lists to sets and take the intersection (which is & in Python notation, not $\cap$). With this method the result is again a set; if we want a sorted list instead then we can apply sorted(list(...)).
square_cubes0 = [n for n in squares if n in cubes] # (Inefficient)
square_cubes1 = set(squares) & set(cubes) # Convert to sets and use intersection
square_cubes2 = sorted(list(square_cubes1)) # Convert back to list and sort
print(f'square_cubes0 = {square_cubes0}')
print(f'square_cubes1 = {square_cubes1}')
print(f'square_cubes2 = {square_cubes2}')
We now have some examples with strings, based on the following spectacularly long name.
long_name = 'Alfonso María Isabel Francisco Eugenio Gabriel Pedro Sebastián Pelayo Fernando Francisco de Paula Pío Miguel Rafael Juan José Joaquín Ana Zacarías Elisabeth Simeón Tereso Pedro Pablo Tadeo Santiago Simón Lucas Juan Mateo Andrés Bartolomé Ambrosio Gerónimo Agustín Bernardo Cándido Gerardo Luis-Gonzaga Filomeno Camilo Cayetano Andrés-Avelino Bruno Joaquín-Picolimini Felipe Luis-Rey-de-Francia Ricardo Esteban-Protomártir Genaro Nicolás Estanislao-de-Koska Lorenzo Vicente Crisóstomo Cristano Darío Ignacio Francisco-Javier Francisco-de-Borja Higona Clemente Esteban-de-Hungría Ladislado Enrique Ildefonso Hermenegildo Carlos-Borromeo Eduardo Francisco-Régis Vicente-Ferrer Pascual Miguel-de-los-Santos Adriano Venancio Valentín Benito José-Oriol Domingo Florencio Alfacio Benére Domingo-de-Silos Ramón Isidro Manuel Antonio Todos-los-Santos de Borbón y Borbón'
We will extract the initials in two different ways. The code [s for s in long_name] produces the list of letters in the name, with each letter represented as a string of length one. By adding the filter if s.isupper() we get a shorter list that contains only the capital letters. To combine this back into a single string, we use str.join('',...).
initials0 = str.join('', [s for s in long_name if s.isupper()])
print(initials0)
print(f'Length: {len(initials0)}')
As an alternative, we can split the name into individual words using the split() method, then take the first letter of each word, and again use str.join() to combine the list into a single string. This gives a slightly different answer. Some initials appear in the first approach and not the second, and some appear in the second and not the first. It is a good exercise to understand why this is the case.
initials1 = str.join('', [s[0] for s in long_name.split(' ')])
print(initials1)
print(len(initials1))