Anda di halaman 1dari 99

H

EL LENGUAJE DE PROGRAMACIN

PYTHON
Programacin orientada a objetos
Programacin funcional

Juan Ignacio Rodrguez de Len


jileon en twitter
euribates @ gmail.com

Objetos y Clases

Las clases permiten que podamos definir


nuestros propios tipos de datos
Las Clases definen las propiedases
(atributos) y las capacidades y
comportamiento (mtodos) general de
los nuevos tipos
Un objeto se crea o instancia a partir de
una clase

Objetos

Un objeto es una variable que


representa un caso particular dentro del
conjunto de posibles instancias de una
clase
De la misma forma que podemos
considerar al nmero 7 como una
instancia particular de la clase Numeros
Enteros

Creacin de clases

Palabra reservada class


La clase ms sencilla que podemos
pensar es:
>>>
>>> class
class X:
X:
...
pass
...
pass
>>>
>>>

Instanciamos un objeto usando el


nombre de la clase como si fuera una
funcin:
>>>
>>>
>>>
>>>

xx == X()
X()
print(x)
print(x)

La clase Point

Inicializador: Mtodo con el nombre


especial __init__
No es el constructor, pero casi
class
class Point:
Point:
def
def __init__(self,
__init__(self, lat,
lat, lng):
lng):
self.latitud
self.latitud == lat
lat
self.longitud
self.longitud == lng
lng
xx == Point(28.4779,
Point(28.4779, -16.3118)
-16.3118)
print(x.latitud,
print(x.latitud, x.longitud)
x.longitud)

self?

Se crea el objeto.
Inmediatamente a continuacin, como
hemos visto, se llama al inicializador

los dos parmetros que usamos al crear al


objeto son los mismos valores que se pasan
al mtodo inicializador con los nombres lat
y lng

Pero De donde sale el primer


parmetro, self? Y qu representa?

Para programadores de C++ o Java

Para programadores de C++ o Java es


la variable "magica" this.
En Python se prefiri esta forma por
considerarla ms explicita.
De igual manera, los atributos dentro de
la funcin tienen que venir precedidos
por el self., no hay alias mgicos para
los atributos de la instancia, para evitar
la ambigedad.

Seguimos con self

Empezemos por la segunda pregunta:


self representa al propio objeto recien
creado
Este primer parmetro es lo que
diferencia a las funciones que ya
conociamos de los mtodos:

un mtodo siempre tienen como primer


parmetro la instancia sobre la que est
siendo ejecutado.

Quien pone self ah

Al definir mtodos para una clase, hay


que reservar el primer parmetro para el
propio objeto
Al llamar al mtodo desde la instancia,
Python ya se ocupa de poner el valor
correcto como primer parmetro
La tradicin y la costumbre marcan que
este primer parmetro se llame self,
pero en realidad no existe obligacin de
hacerlo (pero es conveniente hacerlo,
por legibilidad)

Herencia

Para poder hablar de clases y objetos


con propidad es necesario que haya
algn tipo de herencia
La herencia nos permite definir una clase
a base de refinar o modificar otra
(herencia simple) u otras (herencia
mltiple)

Herencia simple

Si una clase A deriva o hereda de una


clase B (o tambin se dice que la clase B
es una superclase de A):

Entonces la clase A dispondr, de entrada,


de todos los atributos y mtodos de B

Pero puede aadir ms atributos y mtodos


e incluso modificar o borrar los que ha
heredado.

Como declarar herencia en Python


La forma de expresar esta herencia en
python es

>>>
>>> class
class A(B):
A(B):
...
pass
...
pass

Si la clase modifique un mtodo que ha


heredado, se dice que ha reescrito o
sobreescrito (override) el mtodo.

Caractersticas de la herencia

Como los objetos instanciados de A tienen


los mismos atributos y mtodos que B,
deben poder ser ser usados en cualquier
sitio donde se use una instacia de B
Entre A y B hay una relacin es un tipo de

B es un caso general de A

O,si se prefiere, A es una especializacion de B

Polimorfismo

A puede sobreescribir un mtodo f() de B


Si tenemos una lista con objetos de tipo A y
de tipo B mezclados, podemos invocar sin
miedo el mtodo f() en todos ellos, con la
seguridad de que en cada caso se invocar
al mtodo adecuado.
Esta capacidad se llama polimorfismo (del
griego Mltiples Formas)

Mtodos o atributos privados

No existen en Python
Existe una convencin de uso, por la cual
si un atributo o mtodo empieza con el
carcter subrayado, ha de entenderse
que:

Es de uso interno

No deberas jugar con l a no ser que sepas


muy bien lo que ests haciendo

Si en un futuro tu cdigo deja de funcionar


porque has usado ese atributo o mtodo, no
puedes culpar a nadie ms que a ti mismo

Beneficios de usar clases/objetos

Reducir el tamao del cdigo evitando


repeticiones

Si organizamos las herencias correctamente


en jerarquias, de mas genricas a ms
especficas, podemos compatibilizar el
cdigo comn de las primeras con el ms
especfico de las ltimas

Encapsulamiento
Polimorfismo
Delegacin de responsabilidades

super

Que pasa si A sobreescribe un mtodo de B, pero


aun as ha de invocarlo?
En realidad es un caso muy comn, A quiere hacer
lo mismo que B, y un poquito ms.
Desde Python 2.2 hay una funcin super() que nos
ayuda a invocar el cdigo de la clase (o clases) de
la que derivamos.
Python 2.x

class
class A(B):
A(B):
def
def f(self,
f(self, arg):
arg):
super(A,
super(A, self).f(arg)
self).f(arg)
...
...

Python 3.x

class
class A(B):
A(B):
def
def f(self,
f(self, arg):
arg):
super().f(arg)
super().f(arg)

Funciones auxiliares

isinstance(objeto, clase)

nos devolver verdadero si el objeto es una


instancia de una clase en particular, o de
alguna de sus subclases

issubclass(objeto, clase)

nos devolver verdadero si el objeto es una


instancia de una subclase de la clase
indicada.

Sobrecarga de operadores

Se puede, como en C++, sobreescribir


los operadores (operadores aritmticos,
acceso por ndices, etc...) mediante una
sintaxis especial
Los mtodos y atributos que empiezan y
acaban con un doble signo de subrayado
tiene por lo general un significado
especial.

Sobrecarga de operadores: len

Si en nuestra clase definimos un mtodo


__len__(), podemos hacer que las
instancias de esa clase puedan ser
usadas con la funcin len()
Vase ejemplos/clases_02.py

Sobrecarga de operadores: ndices

Si a una clase le aadimos los mtodos


__setitem__ y __getitem__ podemos
hacer que se comporte como si fuera
una contenedor accesible mediante las
operaciones de ndices

Ejemplos/clases_03.py

class
class A:
A:
_Tabla
_Tabla
0:
0:
3:
3:
6:
6:
}}

== {{
'ninguno',
'ninguno', 1:
1:
'tres',
4:
'tres',
4:
'umm...
'umm... seis',
seis',

'uno',
2:
'uno',
2: 'dos',
'dos',
'cuatro',
'cuatro', 5:
5: 'cinco',
'cinco',

def
def __len__(self):
__len__(self):
return
return 77 ## por
por la
la cara
cara
def
def __getitem__(self,
__getitem__(self, index):
index):
if
if 00 <=
<= index
index << 7:
7:
return
return self._Tabla[index]
self._Tabla[index]
else:
else:
return
return 'Muchos'
'Muchos'
def
def __setitem__(self,
__setitem__(self, index,
index, value):
value):
pass
pass

Sobrecarga de operadores: +/Supongamos que queremos escribir un mdulo de


lgebra lineal y que definimos la clase Vector
Podramos crear una funcin independiente para
sumar vectores:

v1
v1
v2
v2
v3
v3

==
==
==

Vector(2,
Vector(2, 3)
3)
Vector(-4,
Vector(-4, 2)
2)
suma_vector(v1,
suma_vector(v1, v2)
v2)

Pero es claramente mejor, ms legible y bonito, poder


hacer
v3
v3 == v1
v1 ++ v2
v2

ejemplos/clases_04.p
y

class
class Vector:
Vector:
def
def __init__(self,
__init__(self, x,
x, y):
y):
self.x
self.x == xx
self.y
self.y == yy
def
def __str__(self):
__str__(self):
return
return 'Vector({},
'Vector({}, {})'.format(self.x,
{})'.format(self.x, self.y)
self.y)
def
def __add__(self,
__add__(self, other):
other):
return
return Vector(
Vector(
self.x
self.x ++ other.x,
other.x,
self.y
self.y ++ other.y
other.y
))
def
def __sub__(self,
__sub__(self, other):
other):
return
return Vector(
Vector(
self.x
self.x -- other.x,
other.x,
self.y
self.y -- other.y
other.y
))

Sobrecarga de operadores: +/

Para eso definimos los mtodos


especiales __add__ y __sub__ para
definir el comportamiento cuando se
sumen o resten dos instancias de nuesta
clase.
Vase ejemplos/clases_04.py
Existen muchos mtodos especiales
A Guide to Python's Magic Methods:

http://www.rafekettler.com/magicmethods.html

Excepciones

Errores sintctis y excepciones


Informacin del error
Las excepciones se producen durante la
ejecucin

Pueden ser tratadas: capturadas

Si no se capturan, el programa acaba

Tipos de excepciones

Segn el error
ZeroDivisionError, ValueError, etc...
Como capturar excepciones: try/except
try:
try:
a,
a, bb == 7,
7, 00
cc == aa // bb
except
except ZeroDivisionError:
ZeroDivisionError:
print("No
print("No puedo
puedo dividir
dividir por
por cero")
cero")

Tratamiento de excepciones

Puede haber varias clusulas except,


para cada tipo de error
Una clusula puede gestionar varios
errores
Puede haber una clausula except
general (No recomendado)
Podemos volver a elevar la excepcion
que hemos capturado

else en clausulas try/except

Similar al else del for/while


El cdigo del else se ejecuta si y solo si:

Se han ejecutado todas las lneas del try

No se ha producido ninguna excepcin

Argumento de la excepcin

La excepcin se representa con un valor que tiene los


detalles del error
Usando la palabra reservada as podemos almacenar
este valor
Captura errores en las llamadas a funciones
>>>
>>> def
def esto_falla():
esto_falla():
...
xx == 1/0
...
1/0
...
...
>>>
>>> try:
try:
...
esto_falla()
...
esto_falla()
...
... except
except ZeroDivisionError
ZeroDivisionError as
as detail:
detail:
...
print('Detectado
...
print('Detectado error',
error', detail)
detail)
...
...
Detectado
Detectado error:
error: division
division by
by zero
zero
>>>
>>>

Legibilidad del cdigo


con excepciones

Los programas en C suelen consistir en


una serie de llamadas a funciones
intercaladas con comprobaciones de
resultados
Con excepciones:

La gestin de los errores no se entromete


con la funcin del algoritmo

Est centralizada y aparte

Elevar excepciones

Podemos elevar nosotros mismos


excepciones, usando la palabra
reservada raise
Podemos definir nuestras propias
excepciones

Derivadas de Exception

Jerarqua de excepciones

Finally

Clusula final, que se ejecutar siempre


Se hayan producido o no excepciones
Uso habitual:

Liberacin de recursos

Operaciones de limpieza

Cualquier cdigo que tenga que ejecutarse


"si si"

Gestores de contexto: with

with nos permite "envolver" un bloque de


cdigo con operaciones a ejecutar antes y
despus del mismo
Simetra en las operaciones
Garanta de ejecucin
Se pueden anidar
Ejemplos

ficheros: open/close

memoria: malloc/free

Ms claro
En vez de hacer esto:

try:
try:
ff == open('fichero.datos',
open('fichero.datos', 'r')
'r')
## proceso
proceso el
el fichero
fichero
nn == len(f.readlines())
len(f.readlines())
finally:
finally:
f.close()
f.close()
Hacemos esto:

with
with open('fichero.datos',
open('fichero.datos', 'r')
'r') as
as f:
f:
...
## proceso
...
proceso el
el fichero
fichero
...
nn == len(f.readlines())
...
len(f.readlines())

Como funciona with (1)

Usando Gestores de contexto


Son objetos que saben lo que hay que
hacer antes y despus de usar otro
objeto
El generador de contexto de file sabe
que hay que cerrar el archivo
Imposible olvidarse de cerrarlo

Como funciona with (2)


1) Evaluacin y obtencin del gestor de
contexto
2) Se carga __exit__()
3) Se ejecuta __enter__() (si devuelve un
valor y se ha usado as, se asigna a la
variable)
4) Se ejecuta el bloque
5) Se llama a __exit__() (con informacin
de errores, si hubiera)

Iteradores

Nuestras clases y objetos pueden ser


iterables
Como funciona for internamente:

Se llama a iter() pasndole lo que quiera


que sea que vamos a iterar

Obtenemos un iterador, es decir, un objeto


con un metodo next()

Llamamos a next() repetidas veces...

Hasta que termina: se eleva StopIteration

Ejemplo
>>>
>>> ss == 'abc'
'abc'
>>>
>>> it
it == iter(s)
iter(s)
>>>
>>> it
it
<iterator
<iterator object
object at
at 0x00A1DB50>
0x00A1DB50>
>>>
>>> it.next()
it.next()
'a'
'a'
>>>
>>> it.next()
it.next()
'b'
'b'
>>>
>>> it.next()
it.next()
'c'
'c'
>>>
>>> it.next()
it.next()
Traceback
Traceback (most
(most recent
recent call
call last):
last):
File
File "<stdin>",
"<stdin>", line
line 1,
1, in
in ??
it.next()
it.next()
StopIteration
StopIteration

Es fcil aadir iterabilidad

Definir un mtodo con el nombre


__iter__() que devuelva un objeto
Este objeto debe implementar un mtodo
next()

Cada vez que se llame a next(), este debe


devolver el siguiente elemento

A no ser que no queden, entonces, eleva


StopIteration

Nuestra clase puede ser su propio iterador:


basta con definir next() y en __iter__
devolver self

Ejercicio

Crear una clase CuentaAtras, que sea


iterable y que, ejem, cuente hacia atrs
hasta llegar al cero.

Solucin
class
class CuentaAtras:
CuentaAtras:
def
def __init__(self,
__init__(self, tope):
tope):
self.tope
self.tope == tope
tope
def
def __iter__(self):
__iter__(self):
self.counter
self.counter == self.tope
self.tope
return
return self
self
def
def next(self):
next(self): return
return self.__next__()
self.__next__() ## python
python 2.7
2.7
def
def __next__(self):
__next__(self):
result
result == self.counter
self.counter
self.counter
self.counter -=
-= 11
if
if result
result << 0:
0:
raise
raise StopIteration
StopIteration
return
return result
result

Generadores

Forma sencilla y potente de crear iteradores


Como una funcin, pero devuelven
resultados con yield, en vez de return
Cada vez que se llama a next(), el
generador continua a partir de donde se
qued
Recuerda su estado: valores de las
variables, ltima lnea que se ejecut, etc...

Cuenta Atras (como generador)


>>>
>>>
...
...
...
...
...
...
...
...
>>>
>>>
...
...
55
44
33
22
11
00
>>>
>>>

def
def cuenta_atras(n):
cuenta_atras(n):
while
while nn >=
>= 0:
0:
yield
yield nn
nn -=
-= 11
for
for ii in
in cuenta_atras(5):
cuenta_atras(5): print(i)
print(i)

Ventajas

Igual potencia que un iterador


Solo por usar yield ya se sabe que es
un generador
Normalmente ms fciles de escribir
Generacin automtica de next() y de
__iter__()
Eleva automticamente StopIteration

Ejemplos de generadores/iteradores

El mdulo os.path, como veremos ms


adelante, tiene una funcin walk, que es
un generador que nos permite recorrer
un rbol de directorios
El mdulo itertools define una funcion
combinations que nos da las
combinaciones de m elementos
tomandos de n en n

Ejemplo de combinaciones

Combinaciones tomando los cuatro


elementos ABCD

De uno en uno:

De dos en dos:

AB, AC, AD, CB, CD, BD

De tres en tres:

A, B, C, D

ACB, ACD, ABD, CBD

De cuatro en cuatro:

ABCD

La Fiscalia Anticorrupcin
nos pide ayuda

Ajustar cuentas

Estan investigando un caso


de un ex-tesorero y una
doble facturacin
Hay una serie de ingresos
por un lado
Y una serie de pagos por
otro
Demostrar que la serie de
pagos se corresponden,
sumadas, con los ingresos.

Los ingresos

1910.00
4090.20
1945.45

Los pagos
1404.93
207.68
297.39
1816.42
153.56
1286.85

322.90
175.04
335.43
259.74
301.28
1384.43

Como podemos hacerlo?

Empezar por uno de los ingresos

Tomar las combinaciones de pagos y


tomarlas de una en una. Ver si sumadas
coinciden con el ingreso

Tomar las combinaciones de pagos y


tomarlas de dos en dos. Ver si sumadas
coinciden con el ingreso

Seguir asi buscando combinaciones hasta


que una coincida. Cuando se encuentra,
retirar esos pagos de la lista de pagos y
seguir con el siguiente ingreso

Herramientas

Conseguir las combinaciones usando


itertoos.combinations.
La funcin sum() nos suma los
elementos de una secuencia
Empezar con una versin minima del
problema: 2 ingresos, 4 pagos, por
ejemplo.
Solucin en:

ejemplos/facturacion_b.py

facturacion_b.py (1)
from
from decimal
decimal import
import Decimal
Decimal
import
import itertools
itertools
ingresos
ingresos == [[
Decimal('4090.20'),
Decimal('4090.20'),
Decimal('1910.00'),
Decimal('1910.00'),
Decimal('1945.45'),
Decimal('1945.45'),
]]
pagos
pagos == [[
Decimal('1404.93'),
Decimal('1404.93'), Decimal('207.68'),
Decimal('207.68'), Decimal('297.39'),
Decimal('297.39'),
Decimal('1816.42'),
Decimal('1816.42'), Decimal('153.56'),
Decimal('153.56'), Decimal('1286.85'),
Decimal('1286.85'),
Decimal('322.9'),
Decimal('322.9'), Decimal('175.04'),
Decimal('175.04'), Decimal('335.43'),
Decimal('335.43'),
Decimal('259.74'),
Decimal('301.28'),
Decimal('259.74'), Decimal('301.28'), Decimal('1384.43'),
Decimal('1384.43'),
]]

facturacion_b.py (2)
for
for ingreso
ingreso in
in ingresos:
ingresos:
solucion
solucion == None
None
for
for size
size in
in range(1,
range(1, len(pagos)+1):
len(pagos)+1):
## Probando
Probando combinaciones
combinaciones de
de size
size elementos
elementos
for
a_probar
in
itertools.combinations(pagos,
for a_probar in itertools.combinations(pagos, size):
size):
if
if sum(a_probar)
sum(a_probar) ==
== ingreso:
ingreso:
print('Encontrada
print('Encontrada una
una solucin:')
solucin:')
solucion
solucion == tuple(a_probar)
tuple(a_probar)
print(*['{0:f}'.format(d)
print(*['{0:f}'.format(d) for
for dd in
in
solucion],
solucion],
sep='
sep=' ++ ',
',
end='
end=' == ')
')
print(ingreso)
print(ingreso)
break
break
if
if solucion:
solucion:
for
for pago
pago in
in solucion:
solucion:
pagos.remove(pago)
pagos.remove(pago)

Solucin
1816.42 + 153.56
+ 1286.85 + 322.9
+ 175.04 + 335.43 = 4090.20
1404.93 + 207.68 + 297.39 = 1910.00
259.74 + 301.28 + 1384.43 = 1945.45

Programacin funcional

Las funciones solo son otro tipo de


variable
Todo lo que se puede hacer con una
variable, se puede hacer con una funcin:

funciones como parmetros

funciones dentro de estructuras de datos

funciones como resultados

Las funciones son objetos de primera


clase

Funciones Lambda

Crear pequeas funciones annimas


lambda <argumentos>: <expresion>
Funcin que suma los dos parmetros
que se le pasan:

lambda(x,y): x+y

No hace falta especificar return


Azucar sintctico para una definicin de
funcin normal

filter

Primer parmetro: una funcin


Segundo parmetro: una secuencia
Devuelve: otra secuencia en la que se
estan slo aquellos valores de la
secuencia original para los que el
resultado de aplicarles la funcin es
True

Ejemplo

Calcular los primeros 200


nmeros que son divisibles
por 5 y por 7

los primeros 200 nmeros


que son divisibles por 5 y por 7

>>>
>>>
...
...
...
...
>>>
>>>
...
...
...
...
35
35
70
70
105
105
140
140
175
175
>>>
>>>

def
def div57(x):
div57(x):
return
return xx %% 55 ==
== 00 and
and xx %% 77 ==
== 00
for
for ii in
in filter(div57,
filter(div57, range(1,
range(1, 201)):
201)):
print(i)
print(i)

map

Primer parmetro: una funcin


Segundo parmetro: una secuencia
Devuelve: otra secuencia, compuesta
por los resultados de llamar a la funcin
en cada uno de los elementos de la
secuencia original

cubos de los 10 primeros nmeros


>>>
>>>
...
...
>>>
>>>
[1,
[1,
>>>
>>>

def
def cube(x):
cube(x): return
return x*x*x
x*x*x
map(cube,
map(cube, range(1,
range(1, 11))
11))
8,
8, 27,
27, 64,
64, 125,
125, 216,
216, 343,
343, 512,
512, 729,
729, 1000]
1000]

map (2)

Podemos pasar ms de una secuencia


La funcin pasada como parmetro
debe aceptar tantos parmetros como
secuencias haya
Ejercicio: media de los datos de otras
dos listas

media de los datos de dos listas


>>>
>>> l1
l1 == [123,
[123, 45,
45, 923,
923, 2,
2, -23,
-23, 55]
55]
>>>
>>> l2
l2 == [9,
[9, 35,
35, 87,
87, 75,
75, 39,
39, 7]
7]
>>>
>>> def
def media(a,
media(a, b):
b): return
return (a
(a ++ b)
b) // 22
...
...
>>>
>>> map(media,
map(media, l1,
l1, l2)
l2)
[66.0,
[66.0, 40.0,
40.0, 505.0,
505.0, 38.5,
38.5, 8.0,
8.0, 31.0]
31.0]
>>>
>>>

reduce

Primer parmetro: una funcin


Segundo parmetro: una secuencia
Devuelve: un nico valor

la funcin que se pasa como parmetro tiene


que aceptar dos valores, y retornar uno

Se calcula el resultado de aplicar la funcin a los


dos primeros valores de la secuencia

A continuacin, se aplica de nuevo la funcin,


usando como parmetros el dato anterior y al
tercer elemento de la secuencia

As hasta acabar la secuencia original

Ejercicio: sumar los valores


de una lista

Ejercicio: sumar los valores


de una lista
>>>
>>>
>>>
>>>
...
...
>>>
>>>
55
55
>>>
>>>

def
def suma(x,y):
suma(x,y): return
return x+y
x+y
reduce(suma,
reduce(suma, range(1,
range(1, 11))
11))

Ejercicio: sumar los valores


de una lista
>>>
>>>
>>>
>>>
...
...
>>>
>>>
55
55
>>>
>>>

def
def suma(x,y):
suma(x,y): return
return x+y
x+y
reduce(suma,
reduce(suma, range(1,
range(1, 11))
11))

Suma(1,2) = 3

Suma(21,7) = 28

Suma(3,3) = 6

Suma(28,8) = 36

Suma(6,4) = 10

Suma(36,9) = 45

Suma(10,5) = 15

Suma(45,10) = 55

Suma(15,6) = 21

Ejercicio: sumar los valores


de una lista
>>>
>>>
>>>
>>>
...
...
>>>
>>>
55
55
>>>
>>>

def
def suma(x,y):
suma(x,y): return
return x+y
x+y
reduce(suma,
reduce(suma, range(1,
range(1, 11))
11))

No se debe usar este modo de realizar sumas,


porque esta es una necesidad tan comn que ya
existe una funcin incorporada para ello:
sum(seq), que funciona exactamente igual, pero
ms rpido al estr implementada en C.

Compresin de listas

Forma muy expresiva de crear listas


Usos comunes

Crear una lista cuyos elementos son resultado


de aplicar una serie de operaciones a otra
secuencia

Crear una sebsecuencia de aquellos elementos


que cumplan una determinada condicin

En resumen, nos permiten hacer lo mismo


que map o filter, pero de forma ms
legible.

10 Nmeros cuadrados

Podemos crear una lista con los


cuadrados de los 10 primeros nmeros
as:
>>>
>>> squares
squares == []
[]
>>>
>>> for
for xx in
in range(11):
range(11):
...
squares.append(x**2)
...
squares.append(x**2)
...
...
>>>
>>> squares
squares
[0,
[0, 1,
1, 4,
4, 9,
9, 16,
16, 25,
25, 36,
36, 49,
49, 64,
64, 81,
81,
100]
100]
>>>
>>>

10 Nmeros cuadrados

O as, con map


squares
squares == map(lambda
map(lambda x:
x: x**2,
x**2,
range(10))
range(10))

10 Nmeros cuadrados

Con comprensin de listas es aun ms


fcil
squares
squares == [x**2
[x**2 for
for xx in
in range(11)]
range(11)]

Anatoma de una C.L.

Corchete [
Expresin
Clusula for
Cero, una o ms clusulas if
Corchete ]

Ejercicio

Cules de los primeros 1500 nmeros


enteros cumplen la condicin de que su
cubo acaba en 272?

str() convierte un nmero en texto

endswith() es un metodo de los textos que


devuelve True si la cadena de texto sobre la
que se aplica acaba en el texto indicado
como parmetro:

'hola'.endswith('a') True

Respuesta

[x
[x for
for xx in
in range(501)
range(501) if
if str(x**3).endswith('272')]
str(x**3).endswith('272')]

[238,
[238, 488]
488]

Expresiones generadoras

Muy similar a una conprensin de lista


Pero devuelve un generador, no una lista
La sintaxis es idntica, sustituyendo los
corchetes por parntesis

con la lista obtenemos todos los elementos


ya generados (y, por tanto, consumiendo
memoria)

El generador nos ir dando los valores de


uno en uno (lazy evaluation)

Ejemplo
>>>
>>> ss == [x**2
[x**2 for
for xx in
in range(11)]
range(11)]
>>>
>>> ss ## es
es una
una lista
lista
[0,
[0, 1,
1, 4,
4, 9,
9, 16,
16, 25,
25, 36,
36, 49,
49, 64,
64, 81,
81, 100]
100]
>>>
>>> ss == (x**2
(x**2 for
for xx in
in range(11))
range(11))
>>>
>>> ss ## es
es un
un generador
generador
<generator
<generator object
object <genexpr>
<genexpr> at
at 0xb74588ec>
0xb74588ec>
>>>
>>> s.next()
s.next()
00
>>>
>>> for
for ii in
in s:
s: print(i)
print(i)
...
...
11
44
[...]
[...]
81
81
100
100
>>>
>>>

Comprensin de diccionarios

Crear diccionarios a partir de otras


fuentes de datos
Sintaxis similar, pero cambiando
corchetes/parntesis por llaves: {}
La expresin tienen que tener la
forma <clave>:<valor>

Ejemplos
>>>
>>> dd == {x:x**2
{x:x**2 for
for xx in
in range(5)}
range(5)}
>>>
>>> dd
{1:
{1: 1,
1, 0:
0: 0,
0, 3:
3: 9,
9, 2:
2: 4,
4, 4:
4: 16}
16}
>>>
>>> dd == {x:x**2
{x:x**2 for
for xx in
in range(5)
range(5) if
if xx %% 22 ==
== 0}
0}
>>>
>>> dd
{0:
{0: 0,
0, 2:
2: 4,
4, 4:
4: 16}
16}
>>>
>>> print({i
print({i :: chr(65+i)
chr(65+i) for
for ii in
in range(4)})
range(4)})
{0
{0 :: 'A',
'A', 11 :: 'B',
'B', 22 :: 'C',
'C', 33 :: 'D'}
'D'}
>>>
>>>

Conprensin de conjuntos

Definir un conjunto a partir de otros


valores
Igual que con los diccionarios, pero la
expresin no va en la forma
<clave>:<valor>, sino como una
expresin simple
NO se puede crear as un diccionario
vacio (Creara un diccionario)

Ejemplo
>>>
>>> ss == {'a',
{'a', 'b',
'b', 'c'}
'c'}
>>>
>>> ss
set(['a',
set(['a', 'c',
'c', 'b'])
'b'])
>>>
>>> ss == {str(x**2)
{str(x**2) for
for xx in
in
>>>
>>> type(s)
type(s)
<type
<type 'set'>
'set'>
>>>
>>> ss
set(['25',
set(['25', '16',
'16', '36',
'36', '1',
'1',
>>>
>>>

range(7)}
range(7)}

'0',
'0', '4',
'4', '9'])
'9'])

Decoradores

Funciones como objetos de primer nivel


Las funciones se pueden almacenar,
pasar como parmetros...
o ser devueltas como resultado de
una funcin
Una funcin puede devolver otra funcin

Suena raro...
Una funcin
puede devolver
otra funcin

No es tan raro, veamos un ejemplo


>>>
>>>
...
...
...
...
...
...
...
...
>>>
>>>
>>>
>>>
99
>>>
>>>
>>>
>>>
50
50

def
def dame_una_funcion_incremento(inc):
dame_una_funcion_incremento(inc):
def
def funcion_a_retornar(x):
funcion_a_retornar(x):
return
return xx ++ inc
inc
return
return funcion_a_retornar
funcion_a_retornar
inc3
inc3 == dame_una_funcion_incremento(3)
dame_una_funcion_incremento(3)
inc3(6)
inc3(6)
inc47
inc47 == dame_una_funcion_incremento(47)
dame_una_funcion_incremento(47)
inc47(3)
inc47(3)

Qu es un decorador?

Sabiendo que esto es posible, un


decorador es:

Una funcin que acepta como parmetro


una funcin, y devuelve otra funcin, que
normalmente sustituir a la original.

Es decir, un decorador nos permite


modificar una funcin (Normalmente
haciendo algo antes, o despus, o ambas
cosas)

Referencia friki totalmente gratuita

Para qu sirve un decorador?

El uso de decoradores se enfoca a


resolver el siguiente problema:

Tenemos un conjunto de funciones

Queremos que todas ellas hagan una nueva


cosa, algo por lo general ajeno al propio
comportamiento de la funcin, y que todas
lo hagan por igual.

En otras palabras, queremos aadir una


funcionalidad horizontal.

Ejemplo de situacin

Supongamos que tenemos un conjunto


de funciones a(), b(),..., z(), cada una
de ellas con sus parmetros,
particularidades, etc...
Queremos ahora, con el mnimo trabajo
posible, que cada funcin escriba en un
fichero log cuando empieza a trabajar y
cuanto termina.

Opcin A (de A lo bruto)


Reescribir cada una de las funciones
Pasar de esto:

def
def a():
a():
## cdigo
cdigo de
de aa
A esto:

def
def a():
a():
with
with open('/tmp/log.txt',
open('/tmp/log.txt', 'a')
'a') as
as log:
log:
log.write('Empieza
log.write('Empieza la
la funcin
funcin a\n')
a\n')
## codigo
codigo de
de aa
with
with open('/tmp/log.txt',
open('/tmp/log.txt', 'a')
'a') as
as log:
log:
log.write('Acaba
log.write('Acaba la
la funcin
funcin a\n')
a\n')

Pegas de la opcin A

Sencillo, pero trabajoso


Hay que reescribir mucho cdigo
El tamao del cdigo aumenta
La lgica de las funciones queda
difuminada con todas esas llamadas a
escribir el log
Si queremos cambiar la informacin del
log, (incluir fecha y hora, p.e.) hay que
volver a modificar todas las funciones

Opcin D (De decoradores)

Intenta solucionar estos problemas


Un decorador coge las funcin original,
(a(), b(),..., z() en nuestro caso), la
modifica y la reemplaza
Ahora, cuando se llama a a(), se invoca
en realidad a nuestra versin modificada
(que a su vez invocar a la a() original)

Decorador logged

Para el ejemplo de log, primero creamos


una funcin decoradora, que llamaramos
logged()
Para simplificar, en vez de escribir a un
fichero log nos limitaremos a hacer dos
prints, uno antes de que empieze la
funcin y otro despus

Cdigo de logged
def
def logged(func):
logged(func):
def
def inner(*
inner(* args,
args, **kwargs):
**kwargs):
print('Empieza
print('Empieza la
la funcin
funcin {}'.
{}'.
format(func.__name__))
format(func.__name__))
func(*args,
func(*args, **kwargs)
**kwargs)
print('Termina
print('Termina la
la funcin
funcin {}'.
{}'.
format(func.__name__))
format(func.__name__))
return
return inner
inner

Aplicacin del decorador


def
def a():
a(): print('Soy
print('Soy a()')
a()')
def
def
bb ==

b():
b(): print('Soy
print('Soy b()')
b()')
logged(b)
logged(b)

@logged
@logged
def
def c():
c(): print('Soy
print('Soy c()')
c()')
@logged
@logged
def
def d(msg):
d(msg):
print('Soy
print('Soy dd yy digo:
digo: {}'.format(msg))
{}'.format(msg))
a()
a()
b()
b()
c()
c()
d('Hola,
d('Hola, mundo')
mundo')

Aplicacin de decoradores
La forma ms cmoda de aplicar el
decorador es poner el smbolo @ y el
nombre del decorador antes de la definicin
de la funcin

La forma:
@logged
@logged
def
def c():
c(): print('Soy
print('Soy c()')
c()')

Es azucar sintctico para:


def
def
cc ==

c():
c(): print('Soy
print('Soy c()')
c()')
logged(c)
logged(c)

Ventaja de los decoradores

Hay que tocar el cdigo de cada funcin, si,


pero el cambio es mnimo: aadir el decorador
con el simbolo @
El cdigo no se repite. No hay aumento
apreciable de tamao del mismo
El cdigo interno de las funciones decoradas no
se ve perturbado por la nueva funcionalidad.
Podemos aadir nuevas caractersticas a las
funciones "logeadas" modificando solo una
cosa: el decorador

Anda mungkin juga menyukai