55. Apéndice: Python básico

Por diversos problemas administrativos y logísticos parece que hay alumnos con un evidente desconocimiento de partes de Python que consideramos esenciales. Por tanto voy a describir en este documento toda la sintaxis de Python ya descrita hasta el momento. Lee el documento rápidamente, sin pararte en cada detalle. Vuelve a él cuando tengas alguna duda.

55.1. Expresiones

Las expresiones son la forma de expresar cómputos en el ordenador. Hay expresiones simples y compuestas. Las expresiones simples más importantes son los literales, es decir, los valores inmediatos, como un valor numérico o un texto que se imprime por pantalla.

55.1.1. Literales

Las expresiones tienen un tipo asociado que le permite a Python determinar cuándo tienen sentido (semántica estática) o qué operaciones pueden aplicarse.

type(1+34 + 2**5)
int
type('hola ' + 'caracola')
str
type(251/2)
float
type(max)
builtin_function_or_method

55.1.1.1. Cadenas de texto

El texto se representa como una secuencia de caracteres entre comillas. Se les llama habitualmente cadenas de texto.

print('¿Qué tal?')
¿Qué tal?

Las cadenas de texto funcionan como cualquier otra secuencia (listas, tuplas, etc.) en muchos sentidos. Por ejemplo, podemos obtener cualquier letra de la cadena con el operador de indexación. Los índices negativos sirven para contar desde el final.

s = 'abcdefghijklmnñopqrstuvwxyz'
print(s[0], s[4], s[-1], s[-4])
a e z w

Podemos obtener la longitud con la función len.

len(s)
27

También se pueden obtener subcadenas (rodajas o slices) usando la notación especial separada por dos puntos. La notación corresponde a primero:último:salto pero el segundo signo : junto al tercer número se pueden omitir si el salto es de uno en uno. Por otro lado si se omite el primer número se asume el primer elemento. Y si se omite el último se asume len(s). El último elemento no se incluye en la rodaja.

print(s[4:7], s[:3], s[3:], s[-3:])
print(s[::4], s[::-1])
efg abc defghijklmnñopqrstuvwxyz xyz
aeimptx zyxwvutsrqpoñnmlkjihgfedcba

55.1.1.2. Números enteros

Python 3 tiene enteros de precisión arbitraria. Eso significa que no hay límite en el tamaño que pueden tener. Por ejemplo:

2**3450
3576720803565275057839928219353083921763535259468698662753739152198552441867883647143304428637726141133835596930347257022227256683860990623981160430533007689883916308559118990520146216899699248238931399641413845615405251814462209444850454652714334205292077092223677308216738965107105682968359166327863506711948610474227555031881236534132855712180707371506836324487806365067661619066349957068368599876048426776492574045512344928622851612631408186799965481951599514299320985419831699339082151647887667702459162952921870631514562032839424059238157521104510001197050206012304136306326417222969089279622337718269953046640849445119972864193163906852973990993916046565484375085517592524959655061567575060419482887691343703741609788280527973997927868816235126269880880695310768033978427832595814683231289717256596146826674836556793769044528480443382424075224851267780806416197680367795308315040020246019511627745987158655671829042268906633428954797799329045711381633521769648556208828100473192244572164644396423321472805318672454370005905911578624

Se puede operar con enteros con los operadores habituales, pero algunas operaciones, como la división, transforma automáticamente la expresión a un real (float). Utiliza // para forzar división entera y no perder precisión.

1 + 234/2 - 12**46
-4.388714385610605e+49
1 + 234//2 - 12**46
-43887143856106046360568987631860370008329246736266

55.1.1.3. Números reales

Se expresan en base 10 con un punto para separar la parte decimal y en notación científica (con una e para representar \(\times 10^n\)).

1.25e-5 + 12.34 - 15e+3
-14987.6599875

Los números reales no tienen precisión infinita. Se representan internamente como signo, mantisa normalizada de tamaño fijo y exponente de tamaño fijo utilizando base 2. Los detalles no son relevantes en este punto del curso. Pero sí es importante saber que esto implica que es fácil perder resolución.

n = 1.0
for i in range(10):
    n = n - 0.1
n
1.3877787807814457e-16

Por tanto no se debe comparar reales con igualdad

55.1.1.4. Llamada a función

La expresión más importante de todas es la llamada a función. Es tan importante porque por sí sola permite implementar todas las demás. Consideramos completamente esencial saber definir funciones y usar funciones (llamar a dichas funciones). La llamada a función es similar al uso de funciones en matemáticas.

max(1,2,5,21,4,43,11,3,9)
43

55.2. Variables

Python permite poner nombres a valores almacenados en memoria usando sentencias de asignación.

pi = 3.141592653589793
radio = 20
area = pi * radio**2

A la derecha del signo = debe haber un valor. Por ejemplo, el resultado de evaluar una expresión aritmética, o un literal. Ese valor se almacena en memoria y se le asocia el nombre de la izquierda. A partir de ahora se puede usar el nombre para referirse al valor almacenado.

Python permite asignar varias variables de golpe separando los elementos por comas:

a, b = 10, 25
print(a)
print(b)
10
25

En cualquier caso todas las expresiones de la derecha se evalúan antes de poner los nombres de la izquierda. Por tanto puede usarse para intercambio de valores:

a, b = b, a
print(a)
print(b)
25
10

55.3. Funciones

Es muy importante saber definir y usar funciones. La estructura general es simple.

def funcion(arg1, arg2):
    resultado = (arg1 * arg2)**0.5
    return resultado

Los argumentos o parámetros formales de la función (arg1 y arg2 en nuestro ejemplo) son simplemente nombres para referirnos a lo que se pase como primer argumento, segundo argumento, etc. en cualquier llamada a la función. Permiten escribir lo que debe hacer la función con esos argumentos sin conocer los valores que se van a usar. En este sentido proporcionan un mecanismo de abstracción, que se conoce como abstracción lambda.

La forma en la que se implementa esta abstracción lambda en Python es también interesante. Cuando se llama a una función se crea un nuevo ámbito de declaración de variables y en ese nuevo ámbito se crean nuevas variables con el nombre de los parámetros formales que se refieren a los valores de los argumentos pasados en la llamada. El ámbito de declaración de la función desaparece cuando la función se destruye.

Por ejemplo:

funcion(12, 15)
13.416407864998739

En esta llamada se crea un ámbito de declaración en el que se incluyen las siguientes asignaciones.

arg1 = 12
arg2 = 15
resultado = (arg1*arg2)**0.5

Las dos primeras corresponden a los parámetros formales y l última es del cuerpo de la función. Durante la llamada a la función sí que existe una variable para cada parámetro formal, es la forma en la que Python implementa la abstracción lambda. Además cualquier asignación a variables que ocurra dentro de esa llamada a función se realizará en el ámbito de declaración de variables de la función, que desaparecerá tan pronto como la función termine con un return o llegando a la última sentencia. Es decir, arg1, arg2 y resultado ya no existirán después de ejecutar la función. Lo único que permanece es el valor devuelto con return, como valor de la expresión de llamada. Tendrás que almacenarlo en una variable o pasarlo como argumento a otra función para que pueda ser usado.

Si no se entiende la llamada a función recomendamos la experimentación con Python Tutor de Philip Guo. No es posible avanzar de ninguna manera sin haber entendido perfectamente cómo funcionan la definición y las llamadas a funciones. Si necesitas ayuda acude a tutorías o al laboratorio.

55.4. Bifurcación

La ejecución condicional de código se realiza con la sentencia if:

if a > 5:
    print('a es mayor de 5')
elif a < 0:
    print('a es negativo')
else:
    print('a está en el intervalo [0,5]')
a es mayor de 5

La claúsula elif es una abreviatura de else if y puede repetirse tantas veces como sea necesario (nada recomendable). Tanto la claúsula elif como la claúsula else son opcionales. Las condiciones después de if o después elif deben ser expresiones booleanas (de valor True o False).

55.5. Repetición

Python incluye dos sentencias para repetir: while y for. Se podría hacer todo con while pero for es más cómodo cuando se conoce a priori el número de repeticiones, o se conoce una expresión para calcular el número de repeticiones.

def leer_entero():
    while True:
        s = input('Introduce un número ')
        if s.isdecimal():
            return int(s)
        print('Entrada inválida')

def paracetamol_dosis(kg):
    mg_kg_hora = 50/24
    mg_hora = mg_kg_hora*kg
    ml_hora = mg_hora/40
    print('Apiretal 40 para niño de {}kg\n'.format(kg))
    print('Cada\tmg\tml')
    print('----\t--\t--')
    for horas in (4, 6, 8):
        print('{}h\t{}\t{}'
              .format(horas,
                      round(mg_hora*horas),
                      round(ml_hora*horas)))
leer_entero()
Introduce un número 1a
Entrada inválida
Introduce un número 12
12
paracetamol_dosis(13)
Apiretal 40 para niño de 13kg

Cada        mg      ml
----        --      --
4h  108     3
6h  162     4
8h  217     5

55.6. Listas

Las listas son la secuencia más flexible de Python. Mantiene el orden de sus elementos y puede modificarse después de su creación (se dice que es mutable).

Operación Significado
l = [] Crea una lista vacía
l = [1,2,3] Crea una lista con valores iniciales
len(l) Número de elementos
l.append(4) Añade un elemento
l.remove(2) Elimina un elemento
l.count(2) Número de veces que aparece el elemento 2 en l
del l[2] Elimina el elemento de la posición 2
l.extend([5,6]) Extiende la lista con los elementos de otra lista
m = l + [5,6] Crea otra lista concatenando l y [5,6]
4 in l True si 4 está en la lista l
a = l.pop() Quita el último elemento de l y lo devuelve
l[1] Segundo elemento de la lista
m = l[:] Copia todos los elementos de la lista l en una nueva lista m
m = l[1:4] Nueva lista con los elementos de l que ocupan las posiciones 1, 2 y 3
m = l[:3] Nueva lista con los 3 primeros elementos de l
m = l[-3:] Nueva lista con los 3 últimos elementos de l
m = l[::2] Nueva lista con los elementos de l en posición par
m = l[1::2] Nueva lista con los elementos de l en posición impar

55.7. Tuplas

Las tuplas son la secuencia más sencilla (y eficiente) de Python. Una vez creada no puede modificarse (se dice que es inmutable).

Operación Significado
l = (1,2,3) Crea una tupla con 3 valores
len(l) Número de elementos
m = l + (5,6) Crea otra tupla concatenando l y (5,6)
4 in l True si 4 está en la tupla l
l[1] Segundo elemento de la tupla

55.8. Diccionarios

Los diccionarios son contenedores de parejas clave/valor. Pueden modificarse después de su creación (se dice que es mutable).

Operación Significado
d = {} Crea un diccionario vacío
d = {'a':1,'b':2, 'c':3] Crea un diccionario con valores iniciales
len(d) Número de elementos
d['d'] = 4 Añade un elemento
del d['b'] Elimina un elemento
d.update({'d':4,' e':5}) Extiende el diccionario con los elementos de otro diccionario
'c' in d True si 'c' está en el diccionario d
a = d.pop('e') Quita el elemento con clave 'e' y lo devuelve
m = d['a'] Elemento con clave ‘a’

55.9. Conjuntos

Los conjuntos son contenedores sin orden donde los elementos no se repiten. Pueden modificarse después de su creación (se dice que son mutables) y están optimizados para determinar pertenencia. Implementan todas las operaciones típicas del álgebra de Boole. Los conjuntos no soportan indexación (operador []).

Operación Significado
a = set() Crea un conjunto vacío
``a = set([1,2,3])` ` Crea un conjunto con valores iniciales
len(a) Número de elementos
a.add(4) Añade un elemento
a.remove(2) Elimina un elemento
a.update([5,6]) Añade un conjunto de elementos
a.union(b) Nuevo conjunto con unión de conjuntos
``a.intersection(b) `` Nuevo conjunto con intersección de conjuntos
a.symetric_differ ence(b) Nuevo conjunto con diferencia simétrica de conjuntos
a - b Nuevo conjunto con diferencia de conjuntos
4 in a True si 4 está en el conjunto a
x = a.pop() Quita un elemento de a y lo devuelve

55.10. Rodajas (slices)

Uno de los aspectos más importantes de los contenedores es la capacidad de indexar para seleccionar trozos del contenedor. Cuanta más práctica tengas en el uso de rodajas más sencillos serán tus tipos de datos.

La forma general de una rodaja es contenedor[primero:ultimo:salto]. Produce un subconjunto de elementos (rodaja) que corresponde a todos los elementos con posiciones entre primero y ultimo (sin contar ultimo) tomando un elemento de cada salto elementos. Si se omite primero se asume 0, si se omite ultimo se asume len(contenedor) y si se omite salto se asume 1.

Esto funciona con cualquier objeto iterable que tenga operador de indexación. Funciona con cadenas de texto, listas, tuplas, rangos. Veamos algunos ejemplos.

import random
l = tuple(random.randint(0,100) for i in range(15))
l
(77, 86, 43, 7, 72, 63, 13, 58, 86, 94, 98, 96, 40, 25, 68)

Los elementos de posición par y los de posición impar:

l[::2], l[1::2]
((77, 43, 72, 13, 86, 98, 40, 68), (86, 7, 63, 58, 94, 96, 25))

Los tres primeros y los tres últimos:

l[:3], l[-3:]
((77, 86, 43), (40, 25, 68))

Acostúmbrate a usar rodajas para modelar datos complejos. Por ejemplo, una matriz 3x3 se puede representar directamente como un tupla de 9 elementos:

a = tuple(random.randint(0,10) for i in range(9))
a
(10, 5, 0, 4, 5, 9, 2, 4, 8)

Las tres filas se pueden imprimir como rodajas:

a[:3], a[3:6], a[6:]
((10, 5, 0), (4, 5, 9), (2, 4, 8))

Las tres columnas también:

a[::3], a[1::3], a[2::3]
((10, 4, 2), (5, 5, 4), (0, 9, 8))

Incluso las diagonales pueden escribirse como rodajas:

a[::4], a[2:-1:2]
((10, 5, 8), (0, 5, 2))

Si en algún momento quieres representarla como una lista de listas también es fácil, pero no te lo recomendamos, es más lento.

[list(a[i:i+3]) for i in (0,3,6)]
[[10, 5, 0], [4, 5, 9], [2, 4, 8]]

O como una tupla de tuplas (un poco más eficiente pero tampoco mucho):

tuple(a[i:i+3] for i in (0,3,6))
((10, 5, 0), (4, 5, 9), (2, 4, 8))

56. Apéndice. Biblioteca estándar

Python tiene una amplísima biblioteca de funciones de todo tipo. Para cualquier trabajo serio de programación sería muy conveniente echar un vistazo a los documentos de docs.python.org. En este curso utilizaremos pocas funciones de la biblioteca estándar, pero de todas formas en los ejemplos verás con frecuencia formas alternativas de resolver problemas que utilizan funciones de la bibioteca estándar. En este apéndice recogemos las más importantes.

56.1. Formateo de cadenas

Uno de los aspectos más recurrentes en la programación es la necesidad de presentar la información según unas indicaciones determinadas. En Python hay varias formas de construir cadenas de acuerdo a un formato dado, pero la más moderna es el método format de las cadenas.

help(''.format)
Help on built-in function format:

format(...) method of builtins.str instance
    S.format(*args, **kwargs) -> str

    Return a formatted version of S, using substitutions from args and kwargs.
    The substitutions are identified by braces ('{' and '}').

Se utilizan los caracteres {} para representar puntos de inserción de los argumentos. Por ejemplo:

'{} x {} = {}'.format(5, 9, 5*9)
'5 x 9 = 45'

Se pueden usar números que indican la posición del argumento a sustituir para evitar ambigüedad o hacer repeticiones:

'{0} es a {1} como {1} es a {0}'.format('tortilla', 'zapatilla')
'tortilla es a zapatilla como zapatilla es a tortilla'

Y probablemente lo más útil es la posibilidad de indicar el formato con dos puntos. Por ejemplo:

import math
print('{0:.4f}\t{0:.7E}'.format(math.pi))
3.1416      3.1415927E+00

O incluso indicar el ancho que debe ocupar y si se alinea a derecha, centrado, o a izquierda.

'[{:>10s}] [{:^10s}] [{:<10s}]'.format('Hola', 'Pasa', 'Adios')
'[      Hola] [   Pasa   ] [Adios     ]'

56.1.1. Funciones de cadenas

Es importante conocer qué funciones proporcionan las cadenas. Para conocerlas basta utilizar los mecanismos de introspección normales.

s=''
dir(s)
['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__init__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmod__',
 '__rmul__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'capitalize',
 'casefold',
 'center',
 'count',
 'encode',
 'endswith',
 'expandtabs',
 'find',
 'format',
 'format_map',
 'index',
 'isalnum',
 'isalpha',
 'isdecimal',
 'isdigit',
 'isidentifier',
 'islower',
 'isnumeric',
 'isprintable',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'ljust',
 'lower',
 'lstrip',
 'maketrans',
 'partition',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'split',
 'splitlines',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'zfill']

Algunas de las que más se usan son startswith, endswith, lower, upper, split, join, find y count. Obtener ayuda sobre cada una de ellas es trivial.

help(s.split)
Help on built-in function split:

split(...) method of builtins.str instance
    S.split(sep=None, maxsplit=-1) -> list of strings

    Return a list of the words in S, using sep as the
    delimiter string.  If maxsplit is given, at most maxsplit
    splits are done. If sep is not specified or is None, any
    whitespace string is a separator and empty strings are
    removed from the result.

No es esencial conocer en detalle estas funciones, pero simplifican extraordinariamente el trabajo con cadenas de texto.

** Este apéndice necesita completarse **

57. Apéndice. Aspectos avanzados

En este apéndice describiremos dos características de Python que tienen una fuerte relación. No es necesario dominar ninguna de estas características para completar satisfactoriamente los objetivos del curso, pero sí es necesario entender mínimamente su funcionamiento, porque se emplean continuamente.

57.1. Generadores

A lo largo del curso hemos visto y seguiremos viendo multitud de casos donde se necesita generar una considerable cantidad de datos pero no se necesita consumirlos de golpe, sino uno a uno. En esos casos es frecuentemente más sencillo emplear generadores. Los generadores son funciones que producen datos usando la sentencia yield. Esta sentencia

57.2. Comprehensions

Las comprensiones de lista, diccionario o tupla

58. Apéndice. Operadores

Python tiene una rica colección de operadores. Cuantos más conozcas más sencillas serán tus expresiones. Ponemos en este apéndice los que creemos que pueden serte útiles.

58.1. Operadores aritméticos

Expresión Tipo de operando Significado
a+b Numéricos Suma
a+b Cadenas Concatenación
a+b Listas Concatenación
a+b Conjuntos Unión
a-b Numéricos Resta
a-b Conjuntos Diferencia de conjuntos
a*b Numéricos Multiplicación
a*b Cadena, Entero Repetición
a*b Lista, Entero Repetición
a/b Numéricos División real
a//b Numéricos División entera
a%b Numéricos Resto de división entera
a**b Numéricos Potencia (a elevado a b)

58.2. Operadores de comparación

Expresión Tipo de operando Significado
a == b Cualquiera Igual
a != b Cualquiera Distinto
a < b Comparables Menor
a > b Comparables Mayor
a <= b Comparables Menor o igual
a >= b Comparables Mayor o igual

58.3. Operadores lógicos

Expresión Tipo de operando Significado
a and b Booleano Y lógico
a or b Booleano Ó lógico
not a Booleano No lógico
a in b Cualquiera/secuencia Pertenence

Nota: a in b funciona para b lista, tupla, diccionario, conjunto o cadena.

[1,2,3]*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
Next Section - 59. Sobre este libro