Python es un lenguaje de programación:

  • De Alto nivel, es decir, lidiamos con abstracciones y no nos preocupamos de los detalles internos de las computadoras, tal como utilizar direcciones de memoria y registros.
  • Multipropósito, pues es utilizado para construir: websites, programas de escritorio, videojuegos, análisis de datos, etc.
  • Interpretado, ya que Python incluye algo llamado intérprete, lo que va traduciendo el texto escrito por el usuario en instrucciones que la computadora comprende a medida que lo va leyendo.
  • Dinámico
  • Multiparadigma

Plataforma de data science. Incluye:

  • Lenguaje de programación Python
  • Entorno de programación tradicional Spyder
  • Entorno de programación multipropósito Jupyter Notebook
  • Librerías de data science (sklearn, numpy, pandas, nltk, matplotlib, etc.)

Particularidades de Python

Python tiene ciertas características que lo definen, y lo diferencian de otros lenguajes.

Indentación para bloques de código

En python, las estructuras de control no están delimitadas por caracteres como los tradicionales corchetes:

if (2 + 3 == 5) {
    x = 5 + 3;
    y = 2 + 3;
} else {
    x = 5 - 3;
    y = 2 - 3;
}

En Python los caracteres de espacio importan. En lugar de los corchetes, Python usa una indentación o sangría, por defecto de 4 espacios, para denotar un bloque de código subordinado a una estructura de control:


In [1]:
if 2 + 3 == 5:
    x = 5 + 3
    mensaje = "Verdadero!"
else:
    x = 5 - 3
    mensaje = "Falso!"
    
print(x)
print(mensaje)


8
Verdadero!

De esta manera, Python estandariza el aspecto del código desde la definición del lenguaje.

Nota:

  • Como se puede observar en el código anterior, en Python, las estructuras de bloque no requieren paréntesis, y se delimitan por el caracter de dos puntos ":"
  • No es necesario el caracter punto y coma ";" al final de cada expresión si es la única en la linea. Pero puede ser usada para colocar múltiples en una sola línea.
    x = 1 + 1; print(x);
    

Palabras Reservadas

Adicionalmente Python cuenta con un conjunto de palabras, llamadas palabras clave o reservadas que tienen un significado especial dentro del lenguaje, las cuales no pueden ser utilizadas como nombre de variables:

False      class      finally    is         return
None       continue   for        lambda     try
True       def        from       nonlocal   while
and        del        global     not        with
as         elif       if         or         yield
assert     else       import     pass
break      except     in         raise

Tipos de dato

Python maneja varios tipos de dato predefinidos. Mencionaremos algunos tipos de dato comúnmente utilizados:

Booleanos

Tipos de datos que expresan valor de verdad.


In [2]:
type(True)


Out[2]:
bool

Los operadores para variables booleanas son sólo 3

Operation Result
x or y si x es falso, entonces y, en caso contrario x
x and y si x es falso, entonces x, en caso contrario y
not x si x es falso, entonces True, en caso contrario False

Fuente: Python docs.

Numéricos


In [3]:
type(5)


Out[3]:
int

In [4]:
type(3.1416)


Out[4]:
float

La siguiente tabla muestra las operaciones posibles entre variables numéricas:

Operación Resultado
x + y suma de x e y
x - y diferencia de x e y
x * y producto de x e y
x / y cociente de x e y
x // y cociente of x e y con redondeo hacia abajo
x % y resto de x / y
-x cambio de signo de x
+x x sin modificación
abs(x) valor absoluto o magnitud de x
int(x) x covnertido a entero (integer)
long(x) x convertido a entero largo (long integer)
float(x) x convertido a punto flotante (floating point)
complex(re,im) un número complejo con parte real re, parte imaginaria im. im es por defecto cero.
c.conjugate() la conjugación del número complejo c.
divmod(x, y) el par (x // y, x % y)
pow(x, y) x elevado a la y
x ** y x elevado a la y

Fuente: Python docs.

Secuencias

Python maneja distintos dipos de secuencias: listas, tuplas y rangos. Cada una tiene sus características, sin embargo la gran mayoría de las siguientes operaciones puede ser utilizada con ellas.

Operación Resultado
x in s True si un elemento de s es igual a x, en caso contrario False
x not in s False si un elemento de s es igual a x, en caso contrario True
s + t la concatenación de s y t
s * n ó n * s equivalente a añadir s a sí mismo n veces
s[i] i-ésimo elemento de s, con origen 0
s[i:j] subsecuencia (slice) de s desde posición i hasta posición j
s[i:j:k] subsecuencia (slice) de s desde posición i hasta posición j con pasos de longitud k
len(s) longitud de s
min(s) elemento de menor valor en s
max(s) elemento de mayor valor en s
s.index(x[, i[, j]]) índice de la primera ocurrencia de x en s (en o después del índice i y antes del índice j)
s.count(x) cantidad total de ocurrencias de x en s

Fuente: Python docs.

Adicionalmente las secuencias pueden ser mutables o inmutables. Las secuencias mutables permiten las siguientes operaciones:

Operación Resultado
s[i] = x elemento i de s es reemplazado por x
s[i:j] = t slice (subsección) de s desde i hasta j es reemplazada por los componentes del iterable t
del s[i:j] igual que s[i:j] = []
s[i:j:k] = t los elementos de s[i:j:k] son reemplazados por los de t
del s[i:j:k] remueve los elementos de s[i:j:k] de la lista
s.append(x) añade x al final de la secuencia (igual que s[len(s):len(s)] = [x])
s.clear() remueve todos los elementos de s (igual que del s[:])
s.copy() crea una copia superficial de s (igual que s[:])
s.extend(t) ó s += t extiende s con los contenidos de t (por lo general igual que s[len(s):len(s)] = t)
s *= n actualiza s con sus contenidos repetidos n veces
s.insert(i, x) inserta x en s en la posición i inserts (igual que s[i:i] = [x])
s.pop([i]) obtiene y remueve el elemento en la posición i de s
s.remove(x) remueve el primer elemento de s donde s[i] == x
s.reverse() invierte los lugares de los elementos de s

Fuente: Python docs.

a) Listas (Secuencias)

Las listas son la estructura de datos básica de Python. Se pueden definir de la siguiente manera:


In [5]:
lista_vacia = []
print(lista_vacia)

#O equivalentemente
lista_vacia = list()
print(lista_vacia)


[]
[]

También pueden crearse directamente con valores adentro:


In [6]:
semana = ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"]
print(semana)


['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo']

Las listas no están restringidas a tener elementos del mismo tipo:


In [7]:
cosas_aleatorias = [1+2, "Donald Trump", None, 3.5]
print(cosas_aleatorias)


[3, 'Donald Trump', None, 3.5]

Para acceder a algun valor, solo hace falta indizarlo haciendo uso de la siguiente sintaxis con corchetes:


In [8]:
#En Python, los índices inician en 0
print (cosas_aleatorias[0])
print (cosas_aleatorias[1])


3
Donald Trump

Adicionalmente se pueden seleccionar subsecciones de las listas haciendo slicing.

Para el slicing, el primer índice indica el inicio de la sublista (inclusivo), el segundo indica el fin (exclusivo), y el tercer número opcional indica cada cuantos elementos coger.

La selección de sublistas adicionalmente tienen reglas adicionales ilustradas a continuación:


In [9]:
print (semana[0:3]) #Desde el primero hasta el tercer elemento
print (semana[0:7:2]) #Toda la lista, pero cada dos elementos
print (semana[:3]) #Desde el inicio de la lista hasta el tercer elemento
print (semana[5:]) #Desde el quinto elemento hasta el final de la lista
print (semana[:-2]) #Desde el primer elemento hasta 2 espacios antes del final de la lista
print (semana[:]) #Toda la lista


['Lunes', 'Martes', 'Miércoles']
['Lunes', 'Miércoles', 'Viernes', 'Domingo']
['Lunes', 'Martes', 'Miércoles']
['Sábado', 'Domingo']
['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes']
['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo']

Nota: Hay que tener cuidado con un detalle. Las sublistas seleccionadas de esta manera son un reflejo de la lista original. En otras palabras, si se modifica esta sublista, se modifica la lista original.


In [10]:
cosas_aleatorias[:3] = [1, 2, 3]
print(cosas_aleatorias)


[1, 2, 3, 3.5]

Una forma rápida de definir una lista conteniendo una secuencia de enteros es haciendo uso de los rangos.


In [11]:
dias_en_diciembre = list(range(1, 32))
print (dias_en_diciembre)


[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]

Pero si las reglas son un poco más complejas, es necesario hacer uso de los llamados list comprehensions. Las list comprehensions son expresiones que son usadas para describir un conjunto de valores, de forma similar a como se describen los conjuntos por comprensión en matemática:

$S = \{x^2 : x > 0 \wedge x \le 5\}$

Lo cual, expresado por extensión, resulta:

$S = \{1, 2, 4, 8, 16\}$

De la misma manera podemos definir una lista haciendo uso de list comprehensions:


In [12]:
[2**x for x in range(5)]


Out[12]:
[1, 2, 4, 8, 16]

In [13]:
[x for x in dias_en_diciembre if x % 2 == 0] #Días pares en diciembre


Out[13]:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]

Los list comprehensions tienen tres partes:

  1. La función
  2. El dominio
  3. La condición

Expresado de la siguiente manera:

[{Funcion(x)} for x in {dominio} if {condicion}]

Y se puede entender como "aplica la función a todo elemento del dominio que cumpla con la condición y devuelve los resultados en una nueva lista".

b) Tuplas (Secuencias)

Las tuplas son muy similares a las listas, con la crucial distinción de que son inmutables. En otras palabras, no se pueden modificar los valores contenidos en las tuplas:


In [53]:
#Las tuplas se crean haciendo uso de paréntesis en lugar de corchetes
semana = ("Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo")
semana[0] = "Enero" #Intentar modificar un valor de una tupla genera un error


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-53-77a183cb740b> in <module>()
      1 #Las tuplas se crean haciendo uso de paréntesis en lugar de corchetes
      2 semana = ("Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo")
----> 3 semana[0] = "Enero" #Intentar modificar un valor de una tupla genera un error

TypeError: 'tuple' object does not support item assignment

Cadenas de caracteres (Secuencias)

Las cadenas de caracteres en Python son secuencias inmutables, y se pueden aplicar las mismas operaciones comunes a todos los tipos de dato secuencia inmutables, a la vez que no es permitido modificarlos.


In [15]:
pangrama = "El veloz murciélago hindú comía feliz cardillo y kiwi. La cigüeña tocaba el saxofón detrás del palenque de paja"
print(pangrama[3:8])

pangrama[3:8] = "lento"


veloz
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-0bf33a9cb414> in <module>()
      2 print(pangrama[3:8])
      3 
----> 4 pangrama[3:8] = "lento"

TypeError: 'str' object does not support item assignment

Adicionalmente tienen muchos métodos adicionales (que pueden ver en la referencia oficial), como por ejemplo upper()


In [16]:
print(pangrama.upper())


EL VELOZ MURCIÉLAGO HINDÚ COMÍA FELIZ CARDILLO Y KIWI. LA CIGÜEÑA TOCABA EL SAXOFÓN DETRÁS DEL PALENQUE DE PAJA

Sets o Conjuntos

Los sets o conjuntos son colecciones de elementos sin orden particular. Los usos comunes de los sets es realizar operaciones de verificación de pertenencia, intersecciones, uniones y diferencias.


In [17]:
numeros_pares = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18} #La creación de conjuntos es con llaves o con la funcion set()
print ("numeros_pares:", numeros_pares)
multiplos_de_tres = set(range(0, 20, 3)) #La función set puede recibir otros iterables o secuencias
print ("multiplos_de_tres:", multiplos_de_tres, "\n")

print("Intersección:", numeros_pares & multiplos_de_tres)
print("Union:", numeros_pares | multiplos_de_tres)
print("Diferencia:", numeros_pares - multiplos_de_tres)


numeros_pares: {0, 2, 4, 6, 8, 10, 12, 14, 16, 18}
multiplos_de_tres: {0, 3, 6, 9, 12, 15, 18} 

Intersección: {0, 18, 12, 6}
Union: {0, 2, 3, 4, 6, 8, 9, 10, 12, 14, 15, 16, 18}
Diferencia: {2, 4, 8, 10, 14, 16}

Adicionalmente, dentro de un set no pueden existir elementos duplicados. Transformar una lista o tupla en set remueve los duplicados, lo cual a menudo resulta muy útil.


In [18]:
semana = ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"]
semana *= 3
print("Lista semana: ", semana, "\n")

semana = set(semana)
print("Set semana:", semana)


Lista semana:  ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'] 

Set semana: {'Viernes', 'Jueves', 'Martes', 'Domingo', 'Miércoles', 'Lunes', 'Sábado'}

Diccionarios

Los diccionarios son estructuras de datos que guardan colecciones de pares de elementos. Cada par es conocido como llave/valor.

Los diccionarios pueden crearse de múltiples maneras equivalentes, cada una útil en su propio contexto:


In [19]:
#Todas estas maneras de crear diccionarios son equivalentes, definiendo la capital de tres países
capitales_1 = dict(Peru = 'Lima', Ecuador = 'Quito', Argentina = 'Buenos Aires')
capitales_2 = {'Peru': 'Lima', 'Ecuador': 'Quito', 'Argentina': 'Buenos Aires'}
capitales_3 = dict(zip(['Peru', 'Ecuador', 'Argentina'], ['Lima', 'Quito', 'Buenos Aires']))
capitales_4 = dict([('Ecuador', 'Quito'), ('Peru', 'Lima'), ('Argentina', 'Buenos Aires')])
capitales_5 = dict({'Argentina': 'Buenos Aires', 'Peru': 'Lima', 'Ecuador': 'Quito'})
capitales_1 == capitales_2 == capitales_3 == capitales_4 == capitales_5


Out[19]:
True

Los valores de los diccionarios se acceden a través de su llave usando la sintaxis

diccionario[llave]

In [20]:
print("'Peru' ->", capitales_1['Peru'])
print("'Ecuador' ->", capitales_1['Ecuador'])
print("'Argentina' ->", capitales_1['Argentina'])


'Peru' -> Lima
'Ecuador' -> Quito
'Argentina' -> Buenos Aires

Es posible recorrer un diccionario, valor por valor, pero cabe mencionar que no se garantiza ninguna clase de orden, a diferencia de las secuencias:


In [21]:
for capital in capitales_1: #En breve veremos que significa 'for'
    print(capital)


Ecuador
Peru
Argentina

Estructuras de control

El verdadero poder de los lenguajes de programación imperativo se encuentra en la posibilidad de utilizar las llamadas estructuras de control, lo que nos permite escribir programas más complejos y más útiles de los que podríamos crear sin ellas.

Hasta ahora sólo hemos podido escribir programas secuenciales, donde cada estructura se ejecuta una tras de otra hasta el final del programa (salvo que se encuentre un error).

Flujo Condicional: If-Else

Mediante las palabras clave if y else podemos escribir instrucciones condicionales. Es decir, partes del programa que se ejecutarán únicamente si se cumple con cierta condición. De esta manera podemos responder a situaciones no conocidas antes de ejecutar un programa (por ejemplo: el input recibido, los valores que se lean de un archivo o base de datos, etc.)

La estructura de un condicional puede ser de tres maneras:

1. if

if ({expresion condicional}):
    {codigo a ejecutar si la condicion evalua a True}
    {mas codigo...}

2. if-Else

if ({expresion condicional}):
    {codigo a ejecutar si la expresion condicional evalua a True}
    {mas codigo...}
else:
    {codigo a ejecutar en caso contrario (si la expresion condicional evalua a False)}
    {mas codigo...}

3. Encadenar if con if-elif

La palabra reservada elif es una abreviación de else if, lo cual traducido significa en caso contrario, si, y sirve para encadenar condiciones adicionales en caso las anteriores no se hayan cumplido.

if ({expresion condicional 1}):
    {codigo a ejecutar si la expresion condicional 1 evalua a True}
    {mas codigo...}
elif ({expresion condicional 2}):
    {codigo a ejecutar si la expresion condicional 1 evalua a False
                        y la expresion condicional 2 evalua a True}
    {mas codigo...}
elif ({expresion condicional 3}):
    {codigo a ejecutar si la expresion condicional 1 evalua a False
                        y la expresion condicional 2 evalua a False
                        y la expresion condicional 3 evalua a True}
else:
    {codigo a ejecutar si ninguna de las expresiones condicionales anteriores evalua a True}
    {mas codigo...}

Notar el uso de indentación para delimitar el código que se ejecutará en cada caso.


In [22]:
x = int(input("Por favor ingrese un entero: "))
if x < 0:
    print("El numero ingresado es negativo")
elif x == 0:
    print("El numero ingresado es cero")
else:
    print("El numero ingresado es positivo")


Por favor ingrese un entero: 1
El numero ingresado es positivo

Flujo Iterativo: While

Adicionalmente, podemos contar con código que repita un grupo de instrucciones una cantidad de veces, dependiendo de una condición, utilizando la palabra clave while. De esta manera, al igual que con if y else, podemos crear programas que funcionen de forma distinta dependiendo de las condiciones en las que se ejecuta el programa.

La sintaxis es similar a la de if.

while({expresion condicional}):
    {(a) codigo a ejecutar mientras la expresion condicional evalua a True}
    {(b) mas codigo}
{(c) codigo exterior}

El flujo del programa seguiría de la siguiente manera:

  1. Se evalúa la expresión condicional.
    1. Si la expresión condicional evalua a False, el código interno no se ejecuta. En otras palabras, la próxima instrucción a ejecutar en el caso anterior será {(c) código exterior} y se habrá salido del bucle.
    2. Si la expresión condicional evalúa a True, se ejecuta el código interno (a) y (b).
  2. Si se ejecutó el código interno (en el paso B.), se regresa al paso 1: evaluar la expresión condicional.

In [23]:
#Fibonacci
a, b = 0, 1
while b < 1000:
    print(b, end=',')
    a, b = b, a + b


1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,

También podemos salir de un bucle en cualquier momento utilzando la palabra reservada break. Ejecutar esta instrucción implica terminar la ejecución del bucle más cercano.


In [24]:
password = ""
while True: # <- Con True como condición, el bucle se ejecutaría permanentemente
    password = input("Por favor ingrese la contraseña: ")
    if password == "secret":
        print("Gracias. Ha ingresado la contraseña correcta.")
        break # <- Pero con break podemos salir del bucle
    else:
        print("Lo sentimos, la contraseña es incorrecta - inténtelo de nuevo.")


Por favor ingrese la contraseña: prueba
Lo sentimos, la contraseña es incorrecta - inténtelo de nuevo.
Por favor ingrese la contraseña: secret
Gracias. Ha ingresado la contraseña correcta.

Asimismo podemos utilizar la palabra reservada continue para terminar temparanamente una iteración y continuar con la siguiente


In [25]:
tareas = []
while True:
    #Formateo de strings https://docs.python.org/3/library/string.html#format-string-syntax
    tarea = input("%d tareas ingresadas. Ingrese una tarea o 'exit' para terminar: " % len(tareas))
    
    if len(tarea) == 0:
        print ("Por favor ingrese una tarea")
        continue # <- Esta palabra reservada termina inmediatamente la actual iteración y continúa con la siguiente
    
    if tarea == "exit":     #
        break               # Todo este código es ignorado durante la actual iteración si
                            # anteriormente se ejecuta una instrucción continue
    tareas.append(tarea)    #

print("Su lista de tareas:")
print("\r\n".join(tareas))


0 tareas ingresadas. Ingrese una tarea o 'exit' para terminar: Comprar pan
1 tareas ingresadas. Ingrese una tarea o 'exit' para terminar: 
Por favor ingrese una tarea
1 tareas ingresadas. Ingrese una tarea o 'exit' para terminar: Pagar recibo de internet
2 tareas ingresadas. Ingrese una tarea o 'exit' para terminar: Llevar ropa a la lavandería
3 tareas ingresadas. Ingrese una tarea o 'exit' para terminar: exit
Su lista de tareas:
Comprar pan
Pagar recibo de internet
Llevar ropa a la lavandería

Flujo Iterativo: For

La palabra reservada for es similar a while en cuanto nos permite ejecutar código de forma iterativa. La diferencia está en que for nos permite realizar fácilmente operaciones por cada elemento de una colección iterable (listas, tuplas, sets, etc).

La sintaxis es como sigue:

for ({elemento} in {coleccion}):
    {(a) codigo a ejecutar por cada elemento en la coleccion}
    #Aquí se puede acceder al elemento actual bajo el nombre dado en {elemento}

Nota: Todo lo que se puede hacer con for se puede hacer con while, sin embargo, for es más apropiado para las tareas para las que se ha creado, por ser más comprensible y elegante.


In [30]:
#Añdadir número de tarea a la lista definida anteriormente
i = 1
for tarea in tareas:
    print ("%d. %s" % (i, tarea))
    i += 1


1. Comprar pan
2. Pagar recibo de internet
3. Llevar ropa a la lavandería

El bucle anterior puede ser resumido con el uso de la función predefinida enumerate


In [32]:
for (i, tarea) in enumerate(tareas):
    print ("%d. %s" % (i+1, tarea))


1. Comprar pan
2. Pagar recibo de internet
3. Llevar ropa a la lavandería

Asimismo podemos utilizar for para realizar una operación una cantidad determinada de veces haciendo uso de la función predefinida range()


In [38]:
#Diez primeros dígitos de la secuencia Fibonacci

a, b = 0, 1
for _ in range(10):
    print(b, end=',')
    a, b = b, a + b


1,1,2,3,5,8,13,21,34,55,

También es factible usar break y continue dentro de los bucles for.


In [42]:
#Diez primeros dígitos de la secuencia Fibonacci, o hasta encontrar un múltiplo de 7

a, b = 0, 1
for _ in range(10):
    print(b, end=',')
    if b % 7 == 0:
        break
    a, b = b, a + b


1,1,2,3,5,8,13,21,

Funciones

Python permite definir funciones, las cuales son bloques de código reutilizable (tales como las ya utilizadas print(), input(), list(), dict(), set(), range() y enumerate()).

En algunos lenguajes de programación las funciones están obligadas a devolver valores, pero en python pueden no hacerlo. Adicionalmente las funciones en Python no están obligadas a devolver un solo valor, sino que pueden devolver más de uno.

La sintaxis básica para definir una función es como sigue:

def nombre_de_funcion( {parametros} ):
    """Documentación del código"""
    {Codigo ejecutable}
    return {expresion}

Por ejemplo, definimos una función que devuelva el número n de la secuencia de Fibonacci.


In [19]:
#Función que devuelve el elemento n de la serie Fibonacci. Tiene un solo parámetro, el número de elemento deseado.
def fib(n):
    """Escribe la serie Fibonacci hasta el número n."""
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a+b
    return a

Una vez definida la función podemos llamarla como sigue:


In [21]:
x = fib(20)
print("Nnúmero Fibonacci #20:", x)


Nnúmero Fibonacci #20: 6765

Es importante notar que todas las variables declaradas dentro de una función son accesibles únicamente dentro de la misma función. Por ejemplo si intentamos obtener el valor de b, obtendremos un error que indica que la variable no está definida:


In [33]:
print(b)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-33-4851c8fca996> in <module>()
----> 1 print(b)

NameError: name 'b' is not defined

Esto nos permite mantener los contextos de declaración y llamada de la función separados, de tal forma de que no haya que preocuparse por el conflicto de nombres de variables en dichas situaciones. Esto se ilustra en el siguiente ejemplo, donde además se declara una función con más de una variable:


In [39]:
import math

def magnitud(x, y):
    x = x**2
    y = y**2
    mag = x + y #Declarando variable interna mag
    mag = math.sqrt(mag)
    return mag

mag = 0 #Declarando variable externa mag para demostrar que es una variable completamente diferente a la interna
print ("Magnitud del vector [4, 3]:", magnitud(4, 3))

#Aquí podemos notar que las variables x e y son independientes de aquellas declaradas en la función
print ("Las variables declaradas y manipuladas fuera de la función siguen teniendo sus valores originales: mag =", mag)


Magnitud del vector [4, 3]: 5.0
Las variables declaradas y manipuladas fuera de la función siguen teniendo sus valores originales: mag = 0

Podemos también definir funciones con valores por defecto. De esta manera volvemos dichos parámetros opcionales, y cuando no se ingrese un valor específico durante una llamada, la función se ejecute con el valor indicado en lugar de echar un error.


In [26]:
def saludar(nombre, saludo="Hola"):
    print("{0} {1}!".format(saludo, nombre))

saludar("Kenyi") #Llamando a la función sin el parámetro saludo hace que la función se ejecute con el valor por defecto "Hola"
saludar("Kenyi", "Buenos días") #Pero si especificamos el valor, se utiliza ese en su lugar


Hola Kenyi!
Buenos días Kenyi!

Cuando la función tiene más de un parámetro, es posible llamar a la función nombrando específicamente a cual parámetro va cada valor, para facilitar la lectura en caso de que hayan muchos.


In [31]:
print( magnitud(x = 12, y = 9) )

#Otro beneficio de llamar funciones de esta forma es que no es necesario llamar a los argumentos en orden
saludar(saludo = "Buenos días", nombre = "Kenyi")


15.0
Buenos días Kenyi!

In [32]:
#Pero si se especifican parámetros con nombre, todas los parámetros subsecuentes deben ser llamados con nombre también
saludar(saludo = "Buenos días", "Kenyi")


  File "<ipython-input-32-20e8e68ed636>", line 2
    saludar(saludo = "Buenos días", "Kenyi")
                                    ^
SyntaxError: positional argument follows keyword argument

También podemos definir funciones con una cantidad variable de parámetros, de la misma forma que trabaja print(), anteponiendo un asterisco * al último nombre de variable (es importante que sea el último), el cual será tratado como tupla dentro de la función.


In [28]:
#En este caso el parámetro sumandos contendrá todos los parámetros ingresados
def sumatoria(*sumandos):
    total = 0
    #sumandos es una tupla
    for elemento in sumandos:
        total += elemento
    return total

print (sumatoria(3, 5, 6))
print (sumatoria(1, 2))


14
3

La siguiente función devuelve dos valores:


In [13]:
def cuadrado_y_cubo(n): # devuelve el cuadrado y el cubo del número ingresado
    cuadrado = n**2
    cubo = n**3
    return cuadrado, cubo

x, y = cuadrado_y_cubo(4)
print("Cuadrado:", x, "- Cubo:", y)


Nnúmero Fibonacci 20: 6765
Cuadrado: 16 - Cubo: 64

Podemos también definir funciones que no devuelvan un valor (Nota: Estrictamente hablando, las funciones sin valor de retorno especificado devuelven None).


In [14]:
def fib(n):    # escribir los primeros n elementos de la serie Fibonacci
    """Escribe la serie Fibonacci hasta el número n."""
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a+b
        print(a, end=' ')
    print()

# Ahora podemos llamar la función que acabamos de definir:
fib(20)
#0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181


1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 

Una última nota, si se modifica un parámetro de tipo mutable dentro de una función, se modificará tambien fuera de ella. No así con las variables de tipo inmutable:


In [48]:
def mutabilidad(entero, lista):
    entero += 10
    lista.append("Hola")
    lista.append("Mundo")

entero = 0
lista = []

mutabilidad(entero, lista)

print(entero) #Entero sigue conteniendo el valor 0, y no 10
print(lista) #Lista ahora tiene 2 nuevos elementos, añadidos dentro de la función


0
['Hola', 'Mundo']

Lambdas

Adicionalmente se pueden definir funciones anónimas cortas haciendo uso de la sintaxis lambda:

lambda {variables} : {expresion de retorno}

Cuyo equivalente declarativo sería:

def funcion_anonima ( {variables} ): 
    return {expresion de retorno}

Por ejemplo, consideremos esta función que duplica el número ingresado:


In [61]:
#En este caso, la variable al_cuadrado contendrá una función, y podrá ser llamada posteriormente como tal
al_cuadrado = lambda x: x**2

print(al_cuadrado(4))
print(al_cuadrado(10))


16
100

Map, Filter, Reduce

Python nos ofrece tres funciones muy útiles para manejar secuencias de datos de forma concisa y legible: map(), filter() y reduce(). Estas funciones forman parte del paradigma de programación funcional que python contiene. Usar estas funciones puede ser complicado al principio, pero funciones de este tipo son utilizadas ampliamente en entornos de alta paralelización, especialmente en manejos de grandes cantidades de datos.

Map

La función map() aplica una función a todos los elementos de una colección de datos iterables (por ejemplo listas, tuplas, sets, diccionarios), y devuelve una lista con todos los valores modificados.

map({funcion_a_aplicar}, {coleccion_iterable})

Por ejemplo si deseamos volver mayúsculas todas las cadenas de caracteres en una lista, podríamos utilizar un bucle for:


In [56]:
semana_mayusculas = []
for i in semana:
    semana_mayusculas.append(i.upper())
    
print(semana_mayusculas)


['LUNES', 'MARTES', 'MIÉRCOLES', 'JUEVES', 'VIERNES', 'SÁBADO', 'DOMINGO']

O podríamos utilizar map.


In [63]:
semana_mayusculas = []
semana_mayusculas = map(str.upper, semana)
print (list(semana_mayusculas))


['LUNES', 'MARTES', 'MIÉRCOLES', 'JUEVES', 'VIERNES', 'SÁBADO', 'DOMINGO']

Comúnmente también veremos map utilizado con lambdas.


In [65]:
enteros = [1, 2, 3, 4, 5]
cuadrados = map(lambda x: x**2, enteros)
print(list(cuadrados))


[1, 4, 9, 16, 25]

Filter

La función filter() permite, como indica su nombre, filtrar elementos de una colección iterable que no cumplan con determinada condición.

filter({funcion_condicional}, {coleccion_iterable})

La funcion_condicional debe ser una función que devuelva True, en caso el elemento cumpla con la condición, o False, en caso contrario.

Por ejemplo, si deseamos filtrar los números positivos de una lista.


In [70]:
lista = [3, -5, -6, 1, 2, -9, 7, -2]

lista_filtrada = filter(lambda x: x <= 0, lista) 
#La función lambda devuelve True si es que el número en cuestión es menor o igual a 0

print(list(lista_filtrada))


[-5, -6, -9, -2]

Reduce

La función reduce() es un poco más complicada, pero extremadamente útil. Reduce aplica una función a dos elementos de una colección iterable, y luego aplica la misma función al resultado del cálculo anterior con el siguiente elemento, y así sucesivamente hasta terminar.

La sintaxis es muy similar a map() y filter():

reduce({funcion_a_aplicar(x, y)}, {coleccion_iterable})

Por ejemplo, la función reduce() con una función de suma y una lista de enteros:


In [73]:
from functools import reduce
lista = [47, 11, 42, 13]
sumatoria = reduce(lambda x, y: x + y, lista)

print(sumatoria)


113

Internamente el proceso que está ocurriendo es como lo muestra el gráfico:

Mayores Recursos

Aún falta mucho por ver en Python, por ejemplo programación orientada a objetos, librerías estándar de python, creación de librerías, manipulación de texto y archivos, reflectividad, etc.

Para referencia futura, no hay lugar con información más exhaustiva que la misma documentación de Python:

https://docs.python.org/3/index.html


In [ ]:
#Styling del notebook
from IPython.core.display import HTML
def css_styling():
    styles = open("./styles/custom.css", "r").read()
    return HTML(styles)
css_styling()