Logo Studenta

funcional_objetos_python

¡Este material tiene más páginas!

Vista previa del material en texto

Programación funcional. Funciones de orden superior 
Python tiene algunas características de la programación funcional, 
un paradigma de programación basado en un concepto de función 
más cercano al matemático que al que habitualmente estamos 
habituados en la programación imperativa. En el siguiente ejemplo 
vamos a construir un conversor de números anidando varias 
funciones en una función de orden superior.
def conversor(sis):
 def sis_bin(numero):
 print('dec:', numero, 'bin:', bin(numero))
 
 def sis_oct(numero):
 print('dec:', numero, 'oct:', oct(numero))
 
 def sis_hex(numero):
 print('dec:', numero, 'hex:', hex(numero))
 
 sis_func = {'bin': sis_bin, 'oct': sis_oct, 'hex': sis_hex}
 return sis_func[sis]
 
conversorhex = conversor('hex') # crea una instancia del conversor 
hexadecimal
conversorhex(100) # convierte el número 100 dec a hex
print()
conversor('bin')(9) # otra forma de usar el conversor.
La función map()
La función de orden superior map() aplica una función a una lista 
de datos y devuelve un iterador que contiene todos los resultados 
para los elementos de la lista.
En el siguiente ejemplo la función cuadrado calcula el cuadrado de
un número. La lista1 contiene una lista de datos numéricos. Con
map(cuadrado, lista1) se aplica la función cuadrado a cada
elemento de la lista. 
def cuadrado(numero): 
 return numero ** 2
 
lista1 = [-2, 4, -6, 8]
lista2= list(map(cuadrado, lista1)) # devuelve un iterador que 
convertimos a lista
print(lista2) # resultado: 4, 16, 36, 64
Para convertir el iterador a una lista hemos empleado la función 
list().
import math
 
def area_circulo(radio):
 return math.pi * radio ** 2
 
lista3 = [1, 2, 3]
lista4 = list(map(area_circulo, lista3)) # devuelve un iterador 
que convertimos a lista
print(lista4)
La función filter()
La función filter() aplica un filtro a una lista de datos y devuelve un
iterador con los elementos que superan el filtro o den el valor lógico
True en un predicado.
squares = map(lambda x: x**2, range(10))
special_squares = filter(lambda x: x > 5 and x < 50, squares)
print special_squares
La función lambda (función anónima)
La función lambda se utiliza para declarar funciones que sólo 
ocupan un línea. Son objetos que pueden asignarse a variables para
usarse con posterioridad.
cuadrado = lambda x: x*x
 
lista = [1,2,3,5,8,13]
for elemento in lista:
 print(cuadrado(elemento))
print (lambda x, y: x*y)(3, 4)
Comprensión de listas
Es un tipo de construcción que consta de una expresión que 
determina cómo modificar los elementos de una lista, seguida de 
una o varias clausulas for y, opcionalmente, una o varias clausulas 
if. El resultado que se obtiene es una lista. El formato general es:
[ expresión a evaluar for loop-variable in secuencia ]
squares = [ x**2 for x in range(10) ]
print squares
def funcion(x): # define función
 return 1/x # devuelve inverso de un número
 
L = [1, 2, 3] # declara lista
print([funcion(float(i)) for i in L]) # muestra lista con 
inversos de cada número
Generadores
Los generadores funcionan de forma parecida a la comprensión de 
listas pero no devuelven listas sino generadores. Un generador es 
una clase especial de función que genera valores sobre los que 
iterar. La sintaxis usada es como la usada en la comprensión de 
listas pero en vez de usar corchetes se utilizan paréntesis. Para 
devolver los valores se utiliza yield, esta orden devolverá un valor 
(igual que hace return) pero, además, congelará la ejecución de la 
función hasta la próxima vez que le pidamos un valor.
La mejor forma de entenderlo es creando uno. Por ejemplo, un 
generador que nos devuelva múltiplos del número pasado como 
argumento:
def multiplos_de(n):
 index = 1
 while True:
 yield index*n
 index = index + 1
if __name__ == '__main__':
 # En este caso genera múltiplos de 7
 for i in multiplos_de(7):
 print i
Usando los generadores vamos a desarrollar una función que 
pasado un número binario lo irá “desgranando” y devolviendo los 
valores decimales por cada bit.
### EJEMPLO SIN USAR GENERADORES
def binToDecClassic(val):
 n = 0
 res = []
 while val > 0:
 res += [(val%2) * (2**n)]
 val /= 10
 n+=1
 
 return res
 
sum = 0
out = binToDecClassic(10011)
for val in out:
 print "+", val,
 sum += val
 
print "=", sum
### EJEMPLO USANDO GENERADORES
def binToDecUsingGenerators(val):
 n = 0
 while val > 0:
 yield (val % 2) * (2**n)
 val = val / 10
 n += 1
 
sum = 0
for val in binToDecUsingGenerators(10011):
 print "+", val ,
 sum += val
 
print "=", sum
Como vemos, el código es muy similar, de hecho, la sintaxis puede 
llevar a confusión, ya que aunque vemos dos bucles, realmente sólo
hay uno, en cada parada del for se corresponde con la instruccion 
“yield” dentro de la función. Esta palabra reservada, “yield” es la 
clave de los generadores. 
Decorador
Es una función que recibe una función como parámetro y devuelve 
otra función como valor de retorno. El formato de un decorador es:
def decorador(a):
 def r(*args, **kwargs):
 # comportamiento previo a la ejecución de a
 a(*args, **kwargs)
 # comportamiento posterior a la ejecución de a
 return r
Un ejemplo trivial es:
def decorador1(funcion):
 def funciondecorada(*param1, **param2):
 print('Inicio', funcion.__name__)
 funcion(*param1, **param2)
 print('Fin', funcion.__name__)
 return funciondecorada
 
def suma(a, b):
 print(a + b)
 
suma2 = decorador1(suma)
suma2(1,2)
suma3 = decorador1(suma)
suma3(2,2)
Otra forma más elegante, usando @:
 
@decorador1
def producto(arg1, arg2):
 print(arg1 * arg2)
 
producto(5,5)
Programación Orientada a Objetos
Un Programa Orientado a Objetos (POO) se basa en una 
agrupación de objetos de distintas clases que interactúan entre sí y 
que, en conjunto, consiguen que un programa cumpla su propósito. 
En este paradigma de programación se intenta emular el 
funcionamiento de los objetos que nos rodean en la vida real. En 
Python cualquier elemento del lenguaje pertenece a una clase y 
todas las clases tienen el mismo rango y se utilizan del mismo 
modo. con la función type() se muestra a qué clase pertenecen 
cada uno:
In [1]: lenguaje = "Python"
In [2]: print(type(lenguaje))
<type 'str'>
In [3]: def mitad(x):
 ...: return float(x)/2
 ...: 
In [4]: print(type(mitad))
<type 'function'>
In [5]: import math
In [6]: print(type(math))
<type 'module'>
In [7]: lista=[1,2,3]
In [8]: print(type(lista))
<type 'list'>
Clases, atríbutos y métodos
Las clases en este contexto permiten definir los atributos y el 
comportamiento, mediante métodos, de los objetos de un 
programa. Una clase es una estructura de datos que se utiliza para 
crear instancias individuales del mismo tipo de objeto.
Los atributos definen las características propias del objeto y 
modifican su estado. Son datos asociados a las clases y a los 
objetos creados a partir de ellas. De ello, se deducen los dos tipos 
de atributos o de variables existentes: variables de clase y 
variables de instancia (objetos). Los métodos son bloques de 
código (o funciones) de una clase que se utilizan para definir el 
comportamiento de los objetos. 
Tanto para acceder a los atributos como para llamar a los métodos 
se utiliza el método denominado de notación de punto que se 
basa en escribir el nombre del objeto o de la clase seguido de un 
punto y el nombre del atributo o del método con los argumentos 
que procedan: clase.atributo, objeto.atributo, 
objeto.método([argumentos]).
Ejemplo:
In [9]: class A1:
 ...: nombre="cosa"
 ...: apellido="tio"
 ...: def telefono(usuario):
 ...: if usuario=="Roberto": TE=4370
 ...: else: TE="desconocido"
 ...: 
In [10]: a=A1()
In [11]: print(type(a))
<type 'instance'>
In [12]: print(type(A1))
<type 'classobj'>
In [13]: print(type(a.apellido))<type 'str'>
In [14]: a.telefono("pepe")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-14-7ab387672808> in <module>()
----> 1 a.telefono("pepe")
TypeError: telefono() takes exactly 1 argument (2 given)
En la instrucción última ocurrió una excepción debido a que los 
métodos son privados de la clase a menos que se los haga públicos 
de manera explícita. Para hacer público el método se usa la llamada
self y el método constructor __init__ (luego se retomará el tema)
In [16]: class D1:
 nombre="cosa"
 apellido="tio"
 def __init__(self, usuario):
 if usuario=="Roberto": self.TE=4382
 else: self.TE="desconocido"
 ....: 
In [17]: d1=D1("Roberto")
In [18]: d2=D1("pepe")
In [19]: print d1.TE, d2.TE
4382 desconocido
Variables de Clases y Variables de Instancias 
En lenguajes que crean objetos a partir de clases, un objeto es una 
instancia de una clase. Y de una misma clase se pueden mantener 
activas en un programa más de una instancia al mismo tiempo.
• Una variable de clase es compartida por todas las instancias 
de una clase. Se definen dentro de la clase (después del 
encabezado de la clase) pero nunca dentro de un método. Este
tipo de variables no se utilizan con tanta frecuencia como las 
variables de instancia.
• Una variable de instancia se define dentro de un método y 
pertenece a un objeto determinado de la clase instanciada. 
Crear clases 
Una clase consta de dos partes: un encabezado que comienza con 
el término class seguido del nombre de la clase (en singular) y dos 
puntos (:) y un cuerpo donde se declaran los atributos y los 
métodos: 
class NombreClase:
 'Texto para documentar la clase (opcional)'
 varclase1 = "variable de clase1"
 
 def nombremetodo1(self, var1):
 self.var1 = var1
 
 def nombremetodo2(self):
 self.var1 += 1
La documentación de una clase debe situarse después del 
encabezado y justo antes del lugar donde se declaren las variables 
y los métodos de la clase. Desde cualquier lugar de un programa se
puede acceder a la cadena de documentación de una clase 
accediendo al atributo especial: NombreClase.__doc__. Todo lo 
que se incluye en una clase es opcional. De hecho, la clase más 
elemental aunque no tenga mucha utilidad puede estar vacía: 
class Clase:
 pass
En el siguiente ejemplo se define una clase mucho más completa: 
class Alumno:
 'Clase para alumnos'
 numalumnos = 0
 sumanotas = 0
 
 def __init__(self, nombre, nota):
 self.nombre = nombre
 self.nota = nota
 Alumno.numalumnos += 1
 Alumno.sumanotas += nota
 
 def mostrarNombreNota(self):
 return(self.nombre, self.nota)
 
 def mostrarNumAlumnos(self):
 return(Alumno.numalumnos)
 
 def mostrarSumaNotas(self):
 return(Alumno.sumanotas)
 
 def mostrarNotaMedia(self):
 if Alumno.numalumnos > 0:
 return(Alumno.sumanotas/Alumno.numalumnos)
 else:
 return("Sin alumnos")
La clase Alumno consta de dos variables de clase 
(Alumno.numalumnos y Alumno.sumanotas) que son 
accesibles desde los métodos de la clase. Además, sus valores son 
compartidos por todas las instancias que existan de esta clase.
A continuación, se declaran varios métodos (funciones) que 
incluyen como primer argumento a self que contiene la referencia 
del objeto especifico que llama al método en un momento dado. 
Como su valor es implícito cuando se llama a un método no es 
necesario pasar este argumento.
El método __init__() es especial porque se ejecuta 
automáticamente cada vez que se crea una nuevo objeto. Este 
método, que es opcional, se llama constructor y se suele utilizar 
para inicializar las variables de las instancias (en este caso para 
inicializar las variables self.nombre y self.nota).
El resto de métodos se utilizan para acceder y mostrar el valor de 
las variables de clase y de instancia. Por último, el método 
mostrarNotaMedia() realiza un cálculo y después muestra su 
resultado. 
Accediendo a los atributos y llamando a los métodos 
Para crear instancias de una clase se llama a la clase por su propio 
nombre pasando los argumentos que requiera el método 
constructor __init__ si existe. 
alumno1 = Alumno("Maria", 8)
alumno2 = Alumno("Carlos", 6)
Todos los argumentos se pasan escribiéndolos entre paréntesis y 
separados por comas (","). El primer argumento self se omite 
porque su valor es una referencia al objeto y es implícito para todos
los métodos. 
Para modificar la variable de un objeto se utiliza la misma notación 
para referirse al atributo y después del signo igual (=) se indica la 
nueva asignación: 
alumno1.nombre = "Carmela"
Para suprimir un atributo:
del alumno1.nombre
Funciones para atributos: getattr(), hasattr(), setattr() y 
delattr()
La función getattr() se utiliza para acceder al valor del atributo de 
un objeto Si un atributo no existe retorna el valor del tercer 
argumento (es opcional). 
nota_alumno2 = getattr(alumno2, 'nota', 0)
edad_alumno2 = getattr(alumno2, 'edad', 0) 
print(nota_alumno2) # 6
print(edad_alumno2) # 0
La función hasattr() devuelve True o False dependiendo si existe 
o no el atributo indicado
if not hasattr(alumno2, 'edad'):
 print("El atributo 'edad' no existe")
setattr() se utiliza para asignar un valor a un atributo. La función 
delattr() es para borrar el atributo de un objeto. Si el atributo no 
existe se producirá una excepción del tipo AttributeError. 
Atributos de clase (Built-In) 
Todas las clases Python incorporan los siguientes atributos 
especiales: 
__dict__ 
Devuelve un diccionario que contiene el espacio de nombres de la 
clase (o de un objeto instanciado). 
In [20]: print(D1.__dict__)
{'nombre': 'cosa', '__module__': '__main__', '__init__': <function __init__ at 
0x7f048484ab90>, 'apellido': 'tio', '__doc__': None}
In [21]: print(d1.__dict__)
{'TE': 4382}
In [22]: print(d2.__dict__)
{'TE': 'desconocido'}
__name__ Devuelve el nombre de la clase. __doc__ Devuelve la 
cadena de documentación de la clase o None si no se ha definido. 
__module__ Devuelve el nombre del módulo donde se encuentra la
clase definida. __bases__ Devuelve una tupla (posiblemente vacía)
que contiene las clases base, en orden de aparición. 
In [24]: print(D1.__name__)
D1
In [25]: print(D1.__module__)
__main__
In [26]: print(D1.__bases__)
()
In [27]: print(D1.__doc__)
None
Destrucción de objetos (Recolección de basura) 
Python elimina objetos que no son necesarios (del tipo Built-In o 
instancias de clase) automáticamente para liberar espacio de 
memoria. El proceso por el cual Python reclama periódicamente 
bloques de memoria que ya no están en uso se denomina 
Recolección de Basura.
La Recolección de Basura de Python se ejecuta durante la 
ejecución de un programa y se activa cuando el contador de 
referencia de un objeto llega al valor cero. Este contador va 
cambiando su valor en función del uso que se haga del objeto. Si se
asigna un nuevo nombre o se utiliza en una lista, tupla o diccionario
el contador irá en aumento y si se borra el objeto con del, su 
referencia es reasignada o bien se queda fuera de ámbito, va 
disminuyendo. En el momento que se alcanza el valor cero el 
recolector comienza con la tarea de "rescate".
pizza1 = "Margarita" # Crear objeto margarita
pizza2 = pizza1 # Incrementa el contador de referencia de margarita
 
cena['lunes'] = pizza2 # Incrementa el contador de referencia de margarita
 
del pizza1 # Decrementa el contador de referencia de margarita
pizza2 = "Cuatro Estaciones" # Decrementa el contador de referencia de 
margarita
cena['lunes'] = pizza2 # Decrementa el contador de referencia de margarita
No obstante, una clase puede incluir el método __del__(), llamado 
destructor, que se invoca cuando una instancia está a punto de ser 
destruida con del, o si no ha sido destruida con esta sentencia, 
cuando el programa finalice su ejecución.Ejemplo:
class Robot:
 'Clase Robot'
 def __init__(self, nombre):
 self.nombre = nombre
 
 def saludo(self):
 print("Hola, mi nombre es", self.nombre)
 
 def __del__(self):
 print("Se han terminado mis baterias. Me voy a dormir.")
 
robot1 = Robot("C7PQ")
robot1.saludo() # Hola, mi nombre es C7PQ
print(id(robot1)) # Muestra el ID del objeto, Ejem.: 3070861004
del robot1 # Se han terminado mis baterias. Me voy a dormir.
Herencia 
La herencia es una de las características más importantes de la 
Programación Orientada a Objetos. Consiste en la posibilidad de 
crear una nueva clase a partir de una o más clases existentes, 
heredando de ellas sus atributos y métodos que podrán utilizarse 
como si estuvieran definidos en la clase hija. Las clases derivadas 
se declaran como cualquier clase con la diferencia de incluir 
después de su nombre el nombre de la clase superior (entre 
paréntesis) de la que heredará sus características: 
class NombreSubClase (NombreClaseSuperior):
 'Cadena de documentación opcional'
 Declaración de atributos y métodos...
Ejemplo:
>>> # subclases
>>> # clase padre o raiz
>>> class Empleados:
def computarSueldo(self):
self.sueldo=2000
def darBonos(self):
self.bonos='SI'
def promocionar(self):
self.promociones='NO'
def retiro(self):
self.jubilaciom='82%'
>>> Pepe=Empleados()
>>> Pepe.computarSueldo()
>>> print Pepe.sueldo
2000
>>> # Se crea una subclase Ingenieros con otro sueldo, que hereda 
el resto
>>> class Ingenieros (Empleados):
def computarSueldo(self):
self.sueldo=3000
>>> Jose=Ingenieros()
>>> Jose.computarSueldo()
>>> print "jose sueldo", Jose.sueldo
jose sueldo 3000
>>> # pero el resto de las instancias se heredan y no cambian
>>> Jose.promocionar()
>>> print "jose promocion", Jose.promociones
jose promocion NO
Herencia múltiple 
La herencia múltiple se refiere a la posibilidad de crear una clase 
a partir de múltiples clases superiores. Es importante nombrar 
adecuadamente los atributos y los métodos en cada clase para no 
crear conflictos:
class Telefono:
 "Clase teléfono"
 def __init__(self):
 pass
 def telefonear(self):
 print('llamando')
 def colgar(self):
 print('colgando') 
 
class Camara:
 "Clase camara fotográfica"
 def __init__(self):
 pass
 def fotografiar(self):
 print('fotografiando') 
 
class Reproductor:
 "Clase Reproductor Mp3"
 def __init__(self):
 pass
 def reproducirmp3(self):
 print('reproduciendo mp3') 
 def reproducirvideo(self):
 print('reproduciendo video') 
 
class Movil(Telefono, Camara, Reproductor):
 def __del__(self):
 print('Móvil apagado')
 
movil1 = Movil()
print(dir(movil1))
movil1.reproducirmp3()
movil1.telefonear()
movil1.fotografiar()
del movil1
Funciones issubclass() y isinstance() 
La función issubclass(SubClase, ClaseSup) se utiliza para 
comprobar si una clase (SubClase) es hija de otra superior 
(ClaseSup), devolviendo True o False según sea el caso. La función 
booleana isinstance(Objeto, Clase) se utiliza para comprobar si 
un objeto pertenece a una clase o clase superior. 
Polimorfismo: Sobrecarga de métodos (Overriding Methods) 
La sobrecarga de métodos se refiere a la posibilidad de que una 
subclase cuente con métodos con el mismo nombre que los de una 
clase superior pero que definan comportamientos diferentes.
Ejemplo, del anterior:
class Movil(Telefono, Camara, Reproductor):
 def __init__(self):
 print('Móvil encendido')
 def reproducirmp3(self):
 print('Reproduciendo lista mp3')
 def __del__(self):
 print('Móvil apagado')
 
movil3 = Movil() # Móvil encendido
movil3.reproducirmp3() # Reproduciendo lista mp3
del movil3 # Móvil apagado
Polimorfismo: Sobrecarga de Operadores (Overloading 
Operators) 
La sobrecarga de operadores trata básicamente de lo mismo 
que la sobrecarga de métodos pero pertenece en esencia al ámbito 
de los operadores aritméticos, binarios, de comparación y lógicos. 
Ejemplo:
class Punto:
 def __init__(self,x = 0,y = 0):
 self.x = x
 self.y = y
 
 def __add__(self,other):
 x = self.x + other.x
 y = self.y + other.y
 return x, y
 
punto1 = Punto(4,6)
punto2 = Punto(1,-2)
print(punto1 + punto2) # (5, 4)
Ocultación de datos (Encapsulación) 
Los atributos de un objeto pueden ocultarse (superficialmente) para
que no sean accedidos desde fuera de la definición de una clase. 
Para ello, es necesario nombrar los atributos con un prefijo de doble
subrayado: __atributo 
Ejemplo:
class Factura:
 __tasa = 19
 
 def __init__(self, unidad, precio):
 self.unidad = unidad
 self.precio = precio
 
 def a_pagar(self):
 total = self.unidad * self.precio
 impuesto = total * Factura.__tasa / 100
 return(total + impuesto)
 
compra1 = Factura(12, 110)
print(compra1.unidad)
print(compra1.precio)
print(compra1.a_pagar(), "euros") 
print(Factura.__tasa) # Error: 
	Programación funcional. Funciones de orden superior
	La función map()
	La función filter()
	La función lambda (función anónima)
	Comprensión de listas
	Generadores
	Decorador
	Programación Orientada a Objetos
	Clases, atríbutos y métodos
	Variables de Clases y Variables de Instancias
	Crear clases
	Accediendo a los atributos y llamando a los métodos
	Funciones para atributos: getattr(), hasattr(), setattr() y delattr()
	Atributos de clase (Built-In)
	Destrucción de objetos (Recolección de basura)
	Herencia
	Herencia múltiple
	Funciones issubclass() y isinstance()
	Polimorfismo: Sobrecarga de métodos (Overriding Methods)
	Polimorfismo: Sobrecarga de Operadores (Overloading Operators)
	Ocultación de datos (Encapsulación)

Continuar navegando

Contenido elegido para ti

182 pag.
Introdução ao Java

User badge image

Aprenda aquí

23 pag.
Semana-3---Clases-en-java

UBAM

User badge image

Contenidos Muy Locos

47 pag.
U2---Modularizacion

UBAM

User badge image

Contenidos Muy Locos

31 pag.