Python
Python
Python
Completo
Incompleto
Compilado
Interpretado
Estructurado
OO
Propósito
Python:
Python, creado por Guido van Rossum a principios de los años 90, nombre inspirado en el grupo de cómicos ingleses
“Monty Python”.
1 Características
- Propósito general.
- Interpretado.
- Scripts:
En Python, como en Java y muchos otros lenguajes, el código fuente se traduce a un pseudo código máquina
intermedio llamado bytecode la primera vez que se ejecuta, generando archivos .pyc o .pyo (bytecode optimizado),
que son los que se ejecutarán en sucesivas ocasiones.
- Programación funcional, no imperativa.
- Orientado a Objetos.
- Tipado dinámico.
- Fuertemente tipado.
- Multiplataforma.
- Múltiples instalaciones.
Existen varias implementaciones distintas de Python: CPython, Jython, IronPython, PyPy, etc.
2 Tipos Básicos
Se dividen en:
• Números como, 4 (entero), 15.57 (de coma flotante) o 7 + 5j (complejos)
• Cadenas de texto como, “Hola Mundo”
• Valores booleanos: True (cierto) y False (falso).
Ejemplos:
#comentarios
# esto es una cadena
c = "Hola Mundo"
# y esto es un entero
e = 23
print(type(c))
print(type(e))
Ejemplo en Java:
String c = “Hola Mundo”;
int e = 23;
Ejemplo en C/C++
char *c=”Hola Mundo”;
int e=23;
2.1 Números
Enteros:
Se pueden representar mediante int o long de C. El rango de los valores que puede representar depende de la
plataforma.
El tipo long de Python permite almacenar números de cualquier precisión, estando limitados solo por la memoria
disponible en la máquina. Al asignar un número a una variable esta pasará a tener tipo int, a menos que el número sea
tan grande como para requerir el uso del tipo long.
entero = 23
Podemos indicar a Python que un número se almacene usando long añadiendo una L al final:
entero = 23L
El literal que se asigna a la variable también se puede expresar como un octal, anteponiendo un cero: octal = 027 o
bien en hexadecimal, anteponiendo un 0x: hexadecimal = 0×17
Reales:
Se expresan mediante el tipo float y se implementa a bajo nivel mediante una variable de tipo double de C, es decir,
utilizando 64 bits, utilizando el estándar IEEE 754: 1 bit para el signo, 11 para el exponente, y 52 para la mantisa. Esto
significa que los valores que podemos representar van desde ±2,2250738585072020 x 10-308 hasta
±1,7976931348623157×10-308.
También se puede utilizar notación científica, y añadir una e (de exponente) para indicar un exponente en base 10:
Complejos:
La mayor parte de lenguajes de programación carecen de este tipo, llamado complex en Python, también se almacena
usando coma flotante, debido a que estos números son una extensión de los números reales. En concreto se almacena
en una estructura de C, compuesta por dos variables de tipo double, sirviendo una de ellas para almacenar la parte
real y la otra para la parte imaginaria. Los números complejos en Python se representan de la siguiente forma:
complejo = 2.1 + 7.8j
print(type(complejo))
Operadores:
Por defecto.
Para operaciones más complejas podemos recurrir al módulo math.
Operadores aritméticos:
Operador Descripción Ejemplo
+ Suma r = 3 + 2 # r es 5
- Resta r = 4 - 7 # r es -3
- Negación r = -7 # r es -7
* Multiplicación r = 2 * 6 # r es 12
** Exponente r = 2 ** 6 # r es 64
/ División r = 3.5 / 2 # r es 1.75
// División entera r = 3.5 // 2 # r es 1.0
% Módulo r = 7 % 2 # r es 1
Hay que tener en cuenta que, si utilizamos dos operandos enteros, Python determinará que queremos que la variable
resultado también sea un entero, por lo que el resultado de, por ejemplo, 3 / 2 y 3 // 2 sería el mismo: 1.
Si quisiéramos obtener los decimales necesitaríamos que al menos uno de los operandos fuera un número real,
indicando los decimales: r = 3.0 / 2 o utilizando la función float: r = float(3) / 2
Si se mezclan tipos de números, Python convierte todos los operandos al tipo más complejo de entre los tipos de los
operandos.
2.2 Cadenas:
También es posible encerrar una cadena entre triples comillas (simples o dobles). De esta forma podremos escribir el
texto en varias líneas, y al imprimir la cadena, se respetarán los saltos de línea que introdujimos sin tener que recurrir
al carácter \n, así como las comillas sin tener que escaparlas.
a = “uno”
b = “dos”
c = a + b # c es “unodos”
c = a * 3 # c es “unounouno”
2.3 Booleanos:
Precedencia de operadores
Operador Descripción
** Exponenciación
~ Not bit a bit
*, /, %, // Multiplicación, división, módulo,
división entera
+, - Adición, sustracción
>>, << Desplazamiento bit a bit a derecha e
izquierda
& And bit a bit
^ Or exclusivo bit a bit
| Or bit a bit
in, not in, is, is not, <, Operadores de comparación, igualdad,
<=, >, >=, !=, == identidad y pertenencia
not Not lógico
And And lógico
Or Or lógico
=, %=, /=, //=, -=, +=, Operadores de asignación
*=, **=
3. Colecciones
Colecciones de datos: listas, tuplas y diccionarios.
Si queremos acceder a un elemento de una lista incluida dentro de otra lista tendremos que utilizar dos veces este
operador, primero para indicar a qué posición de la lista exterior queremos acceder, y el segundo para seleccionar el
elemento de la lista interior:
l = [[1, 2],[3,4]]
print(l)
print(l[0][0])
print(l[0][1])
print(l[1][0])
print(l[1][1])
l[1][1]=5
print(l)
l = [[1,2,3],[4,5,6],[7,8,9]]
print(l)
print(l[0][0])
print(l[0][1])
print(l[0][2])
print(l[1][0])
print(l[1][1])
print(l[1][2])
l[1][1]=22
l[2][2]=33
print(l)
En el operador [] de Python podemos utilizar también números negativos como índice: esto se traduce en que el índice
empieza a contar desde el final, hacia la izquierda; es decir, con [-1] accederíamos al último elemento de la lista, con [-2]
al penúltimo, con [-3], al antepenúltimo, y así sucesivamente.
Slicing o particionado: consiste en ampliar este mecanismo para permitir seleccionar porciones de la lista. Si en lugar de
un número escribimos dos números inicio y fin separados por dos puntos (inicio:fin) Python interpretará que queremos
una lista que vaya desde la posición inicio a la posición fin, sin incluir este último.
Si escribimos tres números (inicio:fin:salto) en lugar de dos, el tercero se utiliza para determinar cada cuantas posiciones
añadir un elemento a la lista.
lista=[[1,2,3],[4,5,6],[7,8,9]]
mi_var=lista[0:2]
print (mi_var)
Los números negativos también se pueden utilizar en un slicing, con el mismo comportamiento comentado.
No es necesario indicar el principio y el final del slicing, sino que, si estos se omiten, se usarán por defecto las posiciones
de inicio y fin de la lista, respectivamente:
se puede incluso modificar el tamaño de la lista si la lista de la parte derecha de la asignación tiene un tamaño menor o
mayor que el de la selección de la parte izquierda de la asignación:
En todo caso las listas ofrecen mecanismos más cómodos para ser modificadas a través de las funciones de la clase
correspondiente.
3.2 Tuplas
Todo lo explicado sobre las listas se aplica a las tuplas, a excepción de la forma de definirla, para lo que se utilizan
paréntesis en lugar de corchetes.
El constructor de la tupla es la coma, no el paréntesis, pero el intérprete muestra los paréntesis. Se usan por claridad.
t = 1, 2, 3
print(type(t))
# type “tuple”
Es necesario añadir una coma para tuplas de un solo elemento, para diferenciarlo de un elemento entre paréntesis.
>>> t = (1)
>>> type(t)
type "int"
>>> t = (1,)
>>> type(t)
type "tuple"
Para referirnos a elementos de una tupla, como en una lista, se usa el operador []:
t = 1, 2, 3
mi_var = t[0] # mi_var es 1
mi_var = t[0:2] # mi_var es (1, 2)
Podemos utilizar el operador [] debido a que las tuplas, al igual que las listas, forman parte de un tipo de objetos llamados
secuencias. Las cadenas de texto también son secuencias.
c = “hola mundo”
c[0] #h
print(c[0])
c[5:] # mundo
c[::3] # hauo
Las tuplas a diferencia de las listas no poseen estos mecanismos de modificación a través de funciones tan útiles de los
que hablábamos al final de la anterior sección.
Además son inmutables, es decir, sus valores no se pueden modificar una vez creada; y tienen un tamaño fijo.
A cambio de estas limitaciones las tuplas son más “ligeras” que las listas, por lo que si el uso que le vamos a dar a una
colección es muy básico, puedes utilizar tuplas en lugar de listas y ahorrar memoria.
3.3 Diccionarios
También llamados matrices asociativas, deben su nombre a que son colecciones que relacionan una clave y un valor. Por
ejemplo, veamos un diccionario de películas y directores:
d = {"Love Actually ": "Richard Curtis", "Kill Bill": "Tarantino", "Amélie": "Jean-Pierre Jeunet"}
El primer valor se trata de la clave y el segundo del valor asociado a la clave. Como clave podemos utilizar cualquier valor
inmutable: podríamos usar números, cadenas, booleanos, tuplas, … pero no listas o diccionarios, dado que son mutables.
Esto es así porque los diccionarios se implementan como tablas hash, y a la hora de introducir un nuevo par clave-valor
en el diccionario se calcula el hash de la clave para después poder encontrar la entrada correspondiente rápidamente. Si
se modificara el objeto clave después de haber sido introducido en el diccionario, evidentemente, su hash también
cambiaría y no podría ser encontrado.
La diferencia principal entre los diccionarios y las listas o las tuplas es que a los valores almacenados en un diccionario se
les accede no por su índice, porque de hecho no tienen orden, sino por su clave, utilizando de nuevo el operador [].
Al igual que en listas y tuplas también se puede utilizar este operador para reasignar valores.
La lista de clase:
d = {1: "Carlos Perez", 2: "Natalia Navas", 3: "Nairo Quintana"}
print (d[1])
Sin embargo en este caso no se puede utilizar slicing, entre otras cosas porque los diccionarios no son secuencias, si no
mappings (mapeados, asociaciones).
4 Control de flujo
Condicionales y los bucles.
if
if seguido de la condición a evaluar, dos puntos (:) y en la siguiente línea e indentado, el código a ejecutar en caso de que
se cumpla dicha condición. En Python se trata de una obligación, y no de una elección. De esta forma se obliga a los
programadores a indentar su código para que sea más sencillo de leer.
fav = "geek.net"
if (fav != "geek.net"):
print("Tienes buen gusto!")
print("Gracias")
print("No pasa nada")
fav = "geek.net"
if fav == "geek.net":
print("Tienes buen gusto!")
print("Gracias")
print("No pasa nada")
if … else
fav = "mundogeek.net"
if fav == "mundogeek.net":
print("Tienes buen gusto! ")
print("Gracias")
else:
print("Vaya, que lástima")
A if C else B
También existe una construcción similar al operador ? de otros lenguajes, que no es más que una forma compacta de
expresar un if else.
En esta construcción se evalúa el predicado C y se devuelve A si se cumple o B si no se cumple: A if C else B.
num=3
var = "par" if (num % 2 == 0) else "impar"
print("var:"+var)
4.2 Bucles
while
El bucle while ejecuta un fragmento de código mientras se cumpla una condición.
edad = 0
while edad < 18:
edad = edad + 1
print(“Felicidades, tienes “ + str(edad)
print("Ya no mas")
salir = False
while not salir:
entrada = input("Valor> ")
if entrada == "adios":
salir = True
else:
print(entrada)
Otra palabra clave que nos podemos encontrar dentro de los bucles es continue que pasa directamente a la siguiente
iteración del bucle.
edad = 0
while edad < 18:
edad = edad + 1
if edad % 2 == 0:
continue
print("Felicidades, tienes " + str(edad))
Con esta modificación el programa sólo imprimiría felicitaciones cuando la edad fuera impar.
for … in
En Python for se usa como una forma genérica de iterar sobre una secuencia.
Para cada elemento que tengamos en la secuencia, ejecuta estas líneas de código: obtener el siguiente elemento de la
secuencia sec y almacenarlo en una variable de nombre elemento. Por esta razón en la primera iteración del bucle
elemento valdrá “uno”, en la segunda “dos”, y en la tercera “tres”.
En C o C++, por ejemplo, lo que habríamos hecho sería iterar sobre las posiciones, y no sobre los elementos:
Si quisiéramos utilizar el for como si estuviéramos en C o en Java, por ejemplo, para imprimir los números de 30 a 50 no
necesitamos crear una lista y añadir uno a uno los números del 30 al 50: Python proporciona una función llamada range,
la cual permite generar una lista que vaya desde el primer número que le indiquemos al segundo. Lo veremos después al
ver el manejo de las funciones.
5. Funciones
Una función es un fragmento de código con un nombre asociado que realiza una serie de tareas y devuelve un valor. A los
fragmentos de código que tienen un nombre asociado y no devuelven valores se les suele llamar procedimientos. En
Python no existen los procedimientos, ya que cuando el programador no especifica un valor de retorno la función devuelve
el valor None (nada), equivalente al null de Java.
Además de ayudarnos a programar y depurar dividiendo el programa en partes las funciones también permiten reutilizar
código.
La palabra reservada def seguida del nombre de la función y entre paréntesis los argumentos separados por comas. A
continuación, en otra línea, indentado y después de los dos puntos tendríamos las líneas de código que conforman el
código a ejecutar por la función.
También podemos encontrarnos con una cadena de texto como primera línea del cuerpo de la función. Estas cadenas se
conocen con el nombre de docstring (cadena de documentación) y sirven, como su nombre indica, a modo de
documentación de la función.
Todos los objetos pueden tener docstrings, no solo las funciones, como veremos más adelante.
Al declarar la función lo único que hacemos es asociar un nombre al fragmento de código que conforma la función, de
forma que podamos ejecutar dicho código más tarde referenciándolo por su nombre. Para llamar a la función y ejecutar
su código se escribiría:
mi_funcion("hola", 2)
La asociación de los parámetros y los valores pasados a la función se hace normalmente de izquierda a derecha.
Sin embargo también es posible modificar el orden de los parámetros si indicamos el nombre del parámetro al que asociar
el valor a la hora de llamar a la función:
El número de valores que se pasan como parámetro al llamar a la función tiene que coincidir con el número de parámetros
que la función acepta según la declaración de la función. En caso contrario Python se quejará:
mi_funcion(“hola”)
También es posible, no obstante, definir funciones con un número variable de argumentos, o bien asignar valores por
defecto a los parámetros para el caso de que no se indique ningún valor para ese parámetro al llamar a la función.
Los valores por defecto para los parámetros se definen situando un signo igual después del nombre del parámetro y a
continuación el valor por defecto:
En el ejemplo anterior si no indicamos un valor para el segundo parámetro se imprimirá una sola vez la cadena que le
pasamos como primer parámetro:
imprimir("hola") #hola
imprimir("hola", 2) #holahola
Para definir funciones con un número variable de argumentos colocamos un último parámetro para la función cuyo
nombre debe precederse de un signo *:
def varios(param1, param2, *otros):
for val in otros:
print (val)
varios(1, 2)
varios(1, 2, 3) #3,
varios(1, 2, 3, 4) #3,4
Esta sintaxis funciona creando una tupla (de nombre otros en el ejemplo) en la que se almacenan los valores de todos los
parámetros extra pasados como argumento. Para la primera llamada, varios(1, 2), la tupla otros estaría vacía dado que no
se han pasado más parámetros que los dos definidos por defecto, por lo tanto no se imprimiría nada. En la segunda
llamada otros valdría (3, ), y en la tercera (3, 4).
También se puede preceder el nombre del último parámetro con **, en cuyo caso en lugar de una tupla se utilizaría un
diccionario. Las claves de este diccionario serían los nombres de los parámetros indicados al llamar a la función y los
valores del diccionario, los valores asociados a estos parámetros.
En el siguiente ejemplo se utiliza la función items de los diccionarios, que devuelve una lista con sus elementos, para
imprimir los parámetros que contiene el diccionario.
En el paso por valor, por el contrario, lo que se pasa como argumento es el valor que contenía la variable.
La diferencia entre ambos radica en que en el paso por valor los cambios que se hagan sobre el parámetro no se ven fuera
de la función, dado que los argumentos de la función son variables locales a la función que contienen los valores indicados
por las variables que se pasaron como argumento. Es decir, en realidad lo que se le pasa a la función son copias de los
valores y no las variables en si.
Si quisiéramos modificar el valor de uno de los argumentos y que estos cambios se reflejaran fuera de la función
tendríamos que pasar el parámetro por referencia.
En C los argumentos de las funciones se pasan por valor, aunque se puede simular el paso por referencia usando
apuntadores. En Java también se usa paso por valor, aunque para las variables que son objetos lo que se hace es pasar
por valor la referencia al objeto, por lo que en realidad parece paso por referencia.
En Python también se utiliza el paso por valor de referencias a objetos, como en Java, aunque en el caso de Python, a
diferencia de Java, todo es un objeto (para ser exactos lo que ocurre en realidad es que al objeto se le asigna otra etiqueta
o nombre en el espacio de nombres local de la función).
Sin embargo, no todos los cambios que hagamos a los parámetros dentro de una función Python se reflejarán fuera de
esta, ya que hay que tener en cuenta que en Python existen objetos inmutables, como las tuplas, por lo que si
intentáramos modificar una tupla pasada como parámetro lo que ocurriría en realidad es que se crearía una nueva
instancia, por lo que los cambios no se verían fuera de la función.
Veamos un pequeño programa para demostrarlo. En este ejemplo se hace uso del método append de las listas. Un método
no es más que una función que pertenece a un objeto, en este caso a una lista; y append, en concreto, sirve para añadir
un elemento a una lista.
25 [22, 23]
22 [22, 23]
Como vemos la variable x no conserva los cambios una vez salimos de la función porque los enteros son inmutables en
Python. Sin embargo la variable y si los conserva, porque las listas son mutables. En resumen: los valores mutables se
comportan como paso por referencia, y los inmutables como paso por valor.
Como vemos esta función tan sencilla no hace otra cosa que sumar los valores pasados como parámetro y devolver el
resultado como valor de retorno.
Sin embargo esto no quiere decir que las funciones Python puedan devolver varios valores, lo que ocurre en realidad es
que Python crea una tupla al vuelo cuyos elementos son los valores a retornar, y esta única variable es la que se devuelve.
Recursividad:
def factorial(n):
if (n==0):
return 1
else:
return n*factorial(n-1)
def main():
n=eval(input("Introduzca un entero positivo para calcular el factorial: "))
print("El factorial de ",n,"es: ",factorial(n))
main()
Taller:
import operator
#leyendo desde archivo
baseDatos=open("BD.txt","r")
ind01=baseDatos.read(1)
val01=baseDatos.read(1)
ind02=baseDatos.read(1)
val02=baseDatos.read(1)
ind03=baseDatos.read(1)
val03=baseDatos.read(1)
ind04=baseDatos.read(1)
val04=baseDatos.read(1)
baseDatos.close()
c0=ind01+str(val01)+ind02+str(val02)+ind03+str(val03)+ind04+str(val04)
print("cad leida ",c0)
albumMusical={ind01:val01, ind02:val02, ind03:val03, ind04:val04}
print("albumMusical inicial como diccionario")
print(albumMusical)
#lectura alternativa
with open("BD.txt") as baseDatos:
ind01=baseDatos.read(1)
val01=baseDatos.read(1)
ind02=baseDatos.read(1)
val02=baseDatos.read(1)
ind03=baseDatos.read(1)
val03=baseDatos.read(1)
ind04=baseDatos.read(1)
val04=baseDatos.read(1)
c0=ind01+str(val01)+ind02+str(val02)+ind03+str(val03)+ind04+str(val04)
print("cad leida alterna ",c0)
albumMusical={ind01:val01, ind02:val02, ind03:val03, ind04:val04}
print("albumMusical alterno como diccionario")
print(albumMusical)
#creo la lista por con los valores ingresados por teclado ordenada por clave
albumMusical={ind1:val1, ind2:val2, ind3:val3, ind4:val4}
print("albumMusical como diccionario")
print(albumMusical)
# ordenado al contrario
resultado.reverse()
print ("ordenado al contrario "+str(resultado))
baseDatos.close()
baseDatos=open("BD.txt","r")
print("Final, lo que leyo: ",baseDatos.read(None))
baseDatos.close()
print("bye ")
func()
Orientación a Objetos
La Programación Orientada a Objetos es un paradigma de programación en el que los conceptos del mundo real relevantes
para nuestro problema se modelan a través de clases y objetos, y en el que nuestro programa consiste en una serie de
interacciones entre estos objetos.
Clases y objetos
1. Declaración: Una clase, es un tipo de dato que representa a un objeto del mundo real. Es una plantilla genérica en la
que se definen los atributos o variables que caracterizan a la clase y los métodos o procedimientos para manejar los
atributos de esa clase.
2. Instanciación: Un objeto, es una variable de tipo clase, que agrupa un estado y una funcionalidad relacionadas. El estado
del objeto se define a través de sus atributos (variables), mientras que la funcionalidad se modela a través de funciones a
las que se les conoce con el nombre de métodos del objeto.
Un ejemplo de objeto podría ser un Coche, en el que tendríamos atributos como la marca, el número de puertas o el tipo
de combustible y métodos como arrancar y parar. O cualquier otra combinación de atributos y métodos según lo que
fuera relevante para nuestro programa.
En el mundo real existe un conjunto de objetos a los que llamamos Coches y que tienen un conjunto de atributos comunes
y un comportamiento común, esto es a lo que llamamos clase. Sin embargo, mi Coche no es igual que el Coche de mi
vecino, y aunque pertenecen a la misma clase de objetos, son objetos distintos.
En Python las clases se definen mediante la palabra clave class seguida del nombre de la clase, dos puntos (:) y a
continuación, indentado, el cuerpo de la clase. Como en el caso de las funciones, si la primera línea del cuerpo se trata de
una cadena de texto, esta será la cadena de documentación de la clase o docstring.
class Coche:
"""Abstraccion de los objetos coche."""
def __init__(self, gasolina):
self.gasolina = gasolina
print ("Tenemos", gasolina, "litros")
def arrancar(self):
if self.gasolina > 0:
print("Arranca")
else:
print("No arranca")
def conducir(self):
if self.gasolina > 0:
self.gasolina -= 1
print ("Quedan", self.gasolina, "litros")
else:
print ("No se mueve")
Lo primero que llama la atención en el ejemplo anterior es el nombre que tiene el método __init__. Este nombre es una
convención y no un capricho.
El método __init__, con una doble barra baja al principio y final del nombre, se ejecuta cada vez que se crea un nuevo
objeto a partir de la clase (instanciación). El método __init__ sirve para realizar cualquier proceso de inicialización que sea
necesario.
El primer parámetro de __init__ y del resto de métodos de la clase es siempre self.
Esta es una idea inspirada en Modula-3 y sirve para referirse al objeto actual. Se usa para poder acceder a los atributos y
métodos del objeto diferenciando, por ejemplo, una variable local mi_var de un atributo del objeto self.
mi_var.
Si volvemos al método __init__ de nuestra clase Coche veremos cómo se utiliza self para asignar al atributo gasolina del
objeto (self.gasolina) el valor que el programador especificó para el parámetro gasolina. El parámetro gasolina se destruye
al final de la función, mientras que el atributo gasolina se conserva (y puede ser accedido) mientras el objeto viva.
Para crear un objeto se escribiría el nombre de la clase seguido de cualquier parámetro que sea necesario entre paréntesis.
Estos parámetros son los que se pasarán al método __init__, que como decíamos es el método que se llama al instanciar
la clase.
mi_coche = Coche(3)
La pregunta es cómo es posible que a la hora de crear nuestro primer objeto pasemos un solo parámetro a __init__, el
número 3, cuando la definición de la función indica claramente que precisa de dos parámetros (self y gasolina). Esto es así
porque Python pasa el primer argumento (la referencia al objeto que se crea) automáticamente.
Podemos acceder a sus atributos y métodos utilizando el operador de punto (.) mediante la sintaxis objeto.atributo y
objeto. metodo():
En Python todo son objetos. Las cadenas, por ejemplo, tienen métodos como upper(), que devuelve el texto en mayúsculas
o count(sub), que devuelve el número de veces que se encontró la cadena sub en el texto.
class Empleado:
def __init__(self):
self.nombre=None
self.edad=None
self.antiguedad=None
self.cualificacion=None
def Sueldo(self):
return(1000+self.antiguedad*25+self.cualificacion*100)
def main():
a=Empleado()
a.nombre="Zoe"
a.edad=21
a.antiguedad=2
a.cualificacion=1
b=Empleado()
b.nombre="Jorge"
b.edad=37
b.antiguedad=9
b.cualificacion=4
print("El sueldo de ",a.nombre," con ",a.antiguedad,\
"años en la empresa y con cualificación de grado ",a.cualificacion,\
" es de ",a.Sueldo()," pesos", sep=' ')
print("El sueldo de ",b.nombre," con ",b.antiguedad,\
"años en la empresa y con cualificación de grado ",b.cualificacion,\
" es de ",b.Sueldo()," pesos", sep=' ')
main()
Hay tres conceptos que son básicos para cualquier lenguaje de programación orientado a objetos: el encapsulamiento, la
herencia y el polimorfismo.
Herencia
En un lenguaje orientado a objetos cuando hacemos que una clase (subclase) herede de otra clase (superclase) estamos
haciendo que la subclase contenga todos los atributos y métodos que tenía la superclase. El acto de heredar de una clase
también se le llama a menudo “extender una clase”.
Ejemplo: Supongamos que queremos modelar los instrumentos musicales de una banda, tendremos entonces una clase
Guitarra, una clase Batería, una clase Bajo, etc. Cada una de estas clases tendrá una serie de atributos y métodos, pero
por el mero hecho de ser instrumentos musicales, estas clases compartirán muchos de sus atributos y métodos; un
ejemplo sería el método tocar().
Es más sencillo crear una clase Instrumento (superclase) con las atributos y métodos comunes e indicar al programa que
Guitarra, Batería y Bajo son tipos de instrumentos (subclases), haciendo que hereden de Instrumento.
Para indicar que una clase hereda de otra se coloca el nombre de la clase de la que se hereda entre paréntesis después
del nombre de la clase:
class Instrumento:
def __init__(self, precio):
self.precio = precio
def tocar(self):
print ("Estamos tocando musica")
def romper(self):
print ("Eso lo pagas tu")
print ("Son", self.precio, "$$$")
class Bateria(Instrumento):
pass
class Guitarra(Instrumento):
pass
miGuitarra=Guitarra(100)
miBateria=Bateria(200)
miBateria.tocar()
miBateria.romper()
miGuitarra.romper()
Como Bateria y Guitarra heredan de Instrumento, ambos tienen un método tocar() y un método romper(), y se inicializan
pasando un parámetro precio.
Pass se usa cuando una sentencia es requerida sintácticamente, pero no se desea que algún comando o código se ejecute.
¿qué ocurriría si quisiéramos especificar un nuevo parámetro tipo_cuerda a la hora de crear un objeto Guitarra?
Bastaría con escribir un nuevo método __init__ para la clase Guitarra que se ejecutaría en lugar del __init__ de
Instrumento. Esto es lo que se conoce como sobreescribir métodos.
class Instrumento:
def __init__(self, precio):
self.precio = precio
def tocar(self):
print ("Estamos tocando musica")
def romper(self):
print ("Eso lo pagas tu")
print ("Son", self.precio, "$$$")
class Bateria(Instrumento):
pass
class Guitarra(Instrumento):
def __init__(self, precio, precio_cuerda):
self.precio = precio
self.precio_cuerda = precio_cuerda
miGuitarra=Guitarra(100, 5)
miBateria=Bateria(200)
miBateria.tocar()
miBateria.romper()
miGuitarra.romper()
print("El precio de la cuerda: ",miGuitarra.precio_cuerda)
print("El precio de la cuerda: ",miBateria.precio_cuerda) #ERROR
Puede ocurrir también que en algunos casos que necesitemos sobreescribir un método de la clase padre, pero que en ese
método queramos ejecutar el método de la clase padre porque nuestro nuevo método no necesite más que ejecutar un
par de nuevas instrucciones extra. En ese caso usaríamos la sintaxis SuperClase.metodo(self, args) para llamar al método
de igual nombre de la clase padre. Por ejemplo, para llamar al método __init__ de Instrumento desde Guitarra usaríamos
Instrumento.__init__(self, precio)
Herencia múltiple
En Python, a diferencia de otros lenguajes como Java o C#, se permite la herencia múltiple, es decir, una clase puede
heredar de varias clases a la vez.
Ejemplo: la clase Cocodrilo que hereda de la clase Terrestre, con métodos como caminar() y atributos como
velocidad_caminar y de la clase Acuatico, con métodos como nadar() y atributos como velocidad_nadar. Basta con
enumerar las clases de las que se hereda separándolas por comas:
En el caso de que alguna de las clases padre tuvieran métodos con el mismo nombre y número de parámetros las clases
sobreescribirían la implementación de los métodos de las clases más a su derecha en la definición.
En el siguiente ejemplo, como Terrestre se encuentra más a la izquierda, sería la definición de desplazar de esta clase la
que prevalecería, y por lo tanto si llamamos al método desplazar de un objeto de tipo Cocodrilo lo que se imprimiría sería
“El animal anda”.
class Terrestre:
def desplazar(self):
print ("El animal anda")
class Acuatico:
def desplazar(self):
print ("El animal nada")
c = Cocodrilo()
c.desplazar()
Polimorfismo
En ocasiones se utiliza el término polimorfismo para referirse a la sobrecarga de métodos, término que se define como la
capacidad del lenguaje de determinar qué método ejecutar de entre varios métodos con igual nombre según el tipo o
número de los parámetros que se le pasa.
En Python no existe sobrecarga de métodos (el último método sobreescribiría la implementación de los anteriores),
aunque se puede conseguir un comportamiento similar recurriendo a funciones con valores por defecto para los
parámetros o a la sintaxis *params o **params explicada en el capítulo sobre las funciones en Python.
Esto se puede conseguir a través de la herencia: un objeto de una clase derivada es al mismo tiempo un objeto de la clase
padre, de forma que allí donde se requiere un objeto de la clase padre también se puede utilizar uno de la clase hija.
Python, al ser de tipado dinámico, no impone restricciones a los tipos que se le pueden pasar a una función, por ejemplo,
más allá de que el objeto se comporte como se espera: si se va a llamar a un método f() del objeto pasado como parámetro,
por ejemplo, evidentemente el objeto tendrá que contar con ese método. Por ese motivo, a diferencia de lenguajes de
tipado estático como Java o C++, el polimorfismo en Python no es de gran importancia.
Encapsulamiento
El encapsulamiento se refiere a encerrar en la clase sus características y métodos e impedir el acceso a determinados
métodos y atributos de los objetos estableciendo así qué puede utilizarse desde fuera de la clase.
Esto se consigue en otros lenguajes de programación como Java y C++ utilizando modificadores de acceso que definen si
cualquiera puede acceder a esa función o variable (public) o si está restringido el acceso a la propia clase (private).
En Python no existen los modificadores de acceso, y lo que se suele hacer es que el acceso a una variable o función viene
determinado por su nombre: si el nombre comienza con dos guiones bajos (y no termina también con dos guiones bajos)
se trata de una variable o función privada, en caso contrario es pública.
Los métodos cuyo nombre comienza y termina con dos guiones bajos son métodos especiales que Python llama
automáticamente bajo ciertas circunstancias, que veremos más adelante.
En el siguiente ejemplo sólo se imprimirá la cadena correspondiente al método publico(), mientras que al intentar llamar
al método __privado() Python lanzará una excepción quejándose de que no existe (evidentemente existe, pero no lo
podemos ver porque es privado).
class Ejemplo:
def publico(self):
print ("Publico")
def __privado(self):
print ("Privado")
ej = Ejemplo()
ej.publico()
ej.__privado()
Este mecanismo se basa en que los nombres que comienzan con un doble guión bajo se renombran para incluir el nombre
de la clase (característica que se conoce con el nombre de name mangling). Esto implica que el método o atributo no es
realmente privado, y podemos acceder a él mediante una pequeña trampa:
class Ejemplo:
def publico(self):
print ("Publico")
def __privado(self):
print ("Privado")
ej = Ejemplo()
ej.publico()
ej._Ejemplo__privado()
En ocasiones también puede suceder que queramos permitir el acceso a algún atributo de nuestro objeto, pero que este
se produzca de forma controlada. Para esto podemos escribir métodos cuyo único cometido sea este, métodos que
normalmente, por convención, tienen nombres como getVariable y setVariable; de ahí que se conozcan también con el
nombre de getters y setters.
Ejemplo 1:
class Fecha():
def __init__(self):
self.__dia = 1
def getDia(self):
return self.__dia
def setDia(self, dia):
if dia > 0 and dia < 31:
self.__dia = dia
else:
print ("Error")
mi_fecha = Fecha()
mi_fecha.setDia(30)
print("fecha: ",mi_fecha.getDia())
Ejemplo 2:
class Fecha():
def __init__(self):
self.__dia = 1
def getDia(self):
return self.__dia
def setDia(self, dia):
if dia > 0 and dia < 31:
self.__dia = dia
else:
print ("Error")
mi_fecha = Fecha()
mi_fecha.setDia(33)
Esto se podría simplificar mediante propiedades, que abstraen al usuario del hecho de que se está utilizando métodos
para obtener y modificar los valores del atributo:
class Fecha(object):
def __init__(self):
self.__dia = 1
def getDia(self):
return self.__dia
def setDia(self, dia):
if dia > 0 and dia < 31:
self.__dia = dia
else:
print ("Error")
dia = property(getDia, setDia)
mi_fecha = Fecha()
mi_fecha.dia = 33
Clases de “nuevo-estilo”
En el ejemplo anterior llama la atención el hecho de que la clase Fecha derive de object. La razón de esto es que para
poder usar propiedades la clase tiene que ser de “nuevo-estilo”, clases enriquecidas introducidas en Python 2.2 que serán
el estándar en Python 3.0 pero que aún conviven con las clases “clásicas” por razones de retrocompatibilidad. Además de
las propiedades las clases de nuevo estilo añaden otras funcionalidades como descriptores o métodos estáticos.
Para que una clase sea de nuevo estilo es necesario, por ahora, que extienda una clase de nuevo-estilo. En el caso de que
no sea necesario heredar el comportamiento o el estado de ninguna clase, como en nuestro ejemplo anterior, se puede
heredar de object, que es un objeto vacio que sirve como base para todas las clases de nuevo estilo.
La diferencia principal entre las clases antiguas y las de nuevo estilo consiste en que a la hora de crear una nueva clase
anteriormente no se definía realmente un nuevo tipo, sino que todos los objetos creados a partir de clases, fueran estas
las clases que fueran, eran de tipo instance.
Taller en programación estructurada
import operator
def leerArchivo(BD):
#leyendo desde archivo
with open(BD) as baseDatos:
ind01=baseDatos.read(1)
val01=baseDatos.read(1)
ind02=baseDatos.read(1)
val02=baseDatos.read(1)
ind03=baseDatos.read(1)
val03=baseDatos.read(1)
ind04=baseDatos.read(1)
val04=baseDatos.read(1)
c0=ind01+str(val01)+ind02+str(val02)+ind03+str(val03)+ind04+str(val04)
print("cad leida alterna ",c0)
albumMusical={ind01:val01, ind02:val02, ind03:val03, ind04:val04}
print("albumMusical alterno como diccionario: ",albumMusical)
def ingresarPorTeclado():
#ingreso por teclado
ind1=input("ind1:")
val1=eval(input("val1:"))
ind2=input("ind2:")
val2=eval(input("val2:"))
ind3=input("ind3:")
val3=eval(input("val3:"))
ind4=input("ind4:")
val4=eval(input("val4:"))
baseDatos=open("BD.txt","a")
almacenarEnArchivo(ind1,val1,ind2,val2,ind3,val3,ind4,val4,baseDatos)
crearDiccionario(ind1,val1,ind2,val2,ind3,val3,ind4,val4,baseDatos)
def almacenarEnArchivo(ind1,val1,ind2,val2,ind3,val3,ind4,val4,baseDatos):
#almaceno la cadena en el archivo
c=ind1+str(val1)+ind2+str(val2)+ind3+str(val3)+ind4+str(val4)
print("cad ",c)
baseDatos.write(ind1+str(val1)+ind2+str(val2)+ind3+str(val3)+ind4+str(val4))
def crearDiccionario(ind1,val1,ind2,val2,ind3,val3,ind4,val4,baseDatos):
#creo la lista por con los valores ingresados por teclado ordenada por clave
albumMusical={ind1:val1, ind2:val2, ind3:val3, ind4:val4}
print("albumMusical como diccionario: ", albumMusical)
imprimirDiccionario(albumMusical,baseDatos)
def imprimirDiccionario(albumMusical,baseDatos):
#imprimir el diccionario como una cadena
st=""
for key,val in albumMusical.items():
st = st + key + str(val)
print ("El diccionario como cadena es ",st)
baseDatos.write(st)
ordenarPorClave(albumMusical)
ordenarPorValor(albumMusical)
baseDatos.close()
baseDatos=open("BD.txt","r")
print("Final, lo que leyo: ",baseDatos.read(None))
baseDatos.close()
print("bye ")
def ordenarPorClave(albumMusical):
#ordenado por clave
resultado = sorted(albumMusical.items(), key=operator.itemgetter(0))
print ("ordenado por clave "+str(resultado))
def ordenarPorValor(albumMusical):
# ordenado por su valor
resultado = sorted(albumMusical.items(), key=operator.itemgetter(1))
print ("ordenado por valor "+str(resultado))
ordenarAlContrario(resultado)
def ordenarAlContrario(resultado):
# ordenado al contrario
resultado.reverse()
print ("ordenado al contrario "+str(resultado))
def main():
BD=input("Nombre de la BasedeDatos: ")
leerArchivo(BD)
ingresarPorTeclado()
main()
Taller en POO
import operator
class Ejemplo():
def __init__(self):
pass
def leerArchivo(self,BD):
#leyendo desde archivo
self.BD=BD
with open(BD) as baseDatos:
self.__ind01=baseDatos.read(1)
self.val01=baseDatos.read(1)
self.ind02=baseDatos.read(1)
self.val02=baseDatos.read(1)
self.ind03=baseDatos.read(1)
self.val03=baseDatos.read(1)
self.ind04=baseDatos.read(1)
self.val04=baseDatos.read(1)
self.c0=self.__ind01+str(self.val01)+self.ind02+str(self.val02)+self.ind03+str(self.val03)+self.ind04+str(self.val04)
print("cad leida alterna ",self.c0)
self.albumMusical={self.__ind01:self.val01, self.ind02:self.val02, self.ind03:self.val03, self.ind04:self.val04}
print("albumMusical alterno como diccionario: ",self.albumMusical)
def setind01(self,ind01):
self._ind01=ind01
def getind01():
return(self._ind01)
def ingresarPorTeclado(self):
#ingreso por teclado
self.ind1=input("ind1:")
self.val1=eval(input("val1:"))
self.ind2=input("ind2:")
self.val2=eval(input("val2:"))
self.ind3=input("ind3:")
self.val3=eval(input("val3:"))
self.ind4=input("ind4:")
self.val4=eval(input("val4:"))
self.baseDatos=open("BD.txt","a")
self.__almacenarEnArchivo(self.ind1,self.val1,self.ind2,self.val2,self.ind3,self.val3,self.ind4,self.val4,self.baseDatos)
self.__crearDiccionario(self.ind1,self.val1,self.ind2,self.val2,self.ind3,self.val3,self.ind4,self.val4,self.baseDatos)
def __almacenarEnArchivo(self,ind1,val1,ind2,val2,ind3,val3,ind4,val4,baseDatos):
#almaceno la cadena en el archivo
self.ind1=ind1
self.val1=val1
self.ind2=ind2
self.val2=val2
self.ind3=ind3
self.val3=val3
self.ind4=ind4
self.val4=val4
self.baseDatos
self.c=self.ind1+str(self.val1)+self.ind2+str(self.val2)+self.ind3+str(self.val3)+self.ind4+str(self.val4)
print("cad ",self.c)
baseDatos.write(self.ind1+str(self.val1)+self.ind2+str(self.val2)+self.ind3+str(self.val3)+self.ind4+str(self.val4))
def __crearDiccionario(self,ind1,val1,ind2,val2,ind3,val3,ind4,val4,baseDatos):
#creo la lista por con los valores ingresados por teclado ordenada por clave
self.ind1=ind1
self.val1=val1
self.ind2=ind2
self.val2=val2
self.ind3=ind3
self.val3=val3
self.ind4=ind4
self.val4=val4
self.baseDatos
self.albumMusical={self.ind1:self.val1}
print("albumMusical como diccionario pos 1: ", self.albumMusical)
self.albumMusical={self.ind1:self.val1, self.ind2:self.val2}
print("albumMusical como diccionario pos 2: ", self.albumMusical)
self.albumMusical={self.ind1:self.val1, self.ind2:self.val2, self.ind3:self.val3, self.ind4:self.val4}
print("albumMusical como diccionario: ", self.albumMusical)
self.__imprimirDiccionario(self.albumMusical,self.baseDatos)
def __imprimirDiccionario(self,albumMusical,baseDatos):
#imprimir el diccionario como una cadena
self.albumMusical=albumMusical
self.baseDatos=baseDatos
self.st=""
for key,val in self.albumMusical.items():
self.st = self.st + key + str(val)
print ("El diccionario como cadena es ",self.st)
self.baseDatos.write(self.st)
self.__ordenarPorClave(albumMusical)
self.__ordenarPorValor(albumMusical)
self.baseDatos.close()
self.baseDatos=open("BD.txt","r")
print("Final, lo que leyo: ",self.baseDatos.read(None))
self.baseDatos.close()
print("bye ")
def __ordenarPorClave(self,albumMusical):
#ordenado por clave
self.albumMusical=albumMusical
self.resultado = sorted(self.albumMusical.items(), key=operator.itemgetter(0))
print ("ordenado por clave "+str(self.resultado))
def __ordenarPorValor(self,albumMusical):
# ordenado por su valor
self.albumMusical=albumMusical
self.resultado = sorted(self.albumMusical.items(), key=operator.itemgetter(1))
print ("ordenado por valor "+str(self.resultado))
self.__ordenarAlContrario(self.resultado)
def __ordenarAlContrario(self,resultado):
# ordenado al contrario
self.resultado=resultado
self.resultado.reverse()
print ("ordenado al contrario "+str(self.resultado))
class Ejemplo2(Ejemplo):
pass
def main():
BD=input("Nombre de la BasedeDatos: ")
miEjemplo=Ejemplo()
miEjemplo.leerArchivo(BD)
miEjemplo.ingresarPorTeclado()
miEjemplo2=Ejemplo2()
miEjemplo2.leerArchivo(BD)
miEjemplo2.ingresarPorTeclado()
main()
Métodos especiales
Ya vimos al principio del artículo el uso del método __init__. Existen otros métodos con significados especiales, cuyos
nombres siempre comienzan y terminan con dos guiones bajos. A continuación se listan algunos especialmente útiles.
__init__(self, args)
Método llamado después de crear el objeto para realizar tareas de inicialización.
__new__(cls, args)
Método exclusivo de las clases de nuevo estilo que se ejecuta antes que __init__ y que se encarga de construir y devolver
el objeto en sí. Es equivalente a los constructores de C++ o Java. Se trata de un método estático, es decir, que existe con
independencia de las instancias de la clase: es un método de clase, no de objeto, y por lo tanto el primer parámetro no es
self, sino la propia clase: cls.
__del__(self)
Método llamado cuando el objeto va a ser borrado. También llamado destructor, se utiliza para realizar tareas de limpieza.
__str__(self)
Método llamado para crear una cadena de texto que represente a nuestro objeto. Se utiliza cuando usamos print para
mostrar nuestro objeto o cuando usamos la función str(obj) para crear una cadena a partir de nuestro objeto.
__cmp__(self, otro)
Método llamado cuando se utilizan los operadores de comparación para comprobar si nuestro objeto es menor, mayor o
igual al objeto pasado como parámetro. Debe devolver un número negativo si nuestro objeto es menor, cero si son iguales,
y un número positivo si nuestro objeto es mayor. Si este método no está definido y se intenta comparar el objeto mediante
los operadores <=, > o >= se lanzará una excepción. Si se utilizan los operadores == o != para comprobar si dos objetos son
iguales, se comprueba si son el mismo objeto (si tienen el mismo id).
__len__(self)
Método llamado para comprobar la longitud del objeto. Se utiliza, por ejemplo, cuando se llama a la función len(obj) sobre
nuestro objeto. Como es de suponer, el método debe devolver la longitud del objeto.
Existen bastantes más métodos especiales, que permite entre otras cosas utilizar el mecanismo de slicing sobre nuestro
objeto, utilizar los operadores aritméticos o usar la sintaxis de diccionarios, pero un estudio exhaustivo de todos los
métodos queda fuera del propósito del capítulo
Revisitando Objetos
En los capítulos dedicados a los tipos simples y las colecciones veíamos por primera vez algunos de los objetos del lenguaje
Python: números, booleanos, cadenas de texto, diccionarios, listas y tuplas.
Ahora que sabemos qué son las clases, los objetos, las funciones, y los métodos es el momento de revisitar estos objetos
para descubrir su verdadero potencial.
Veremos a continuación algunos métodos útiles de estos objetos. Evidentemente, no es necesario memorizarlos, pero si,
al menos, recordar que existen para cuando sean necesarios.
Diccionarios
D.get(k[, d])
Busca el valor de la clave k en el diccionario. Es equivalente a utilizar D[k] pero al utilizar este método podemos indicar un
valor a devolver por defecto si no se encuentra la clave, mientras que con la sintaxis D[k], de no existir la clave se lanzaría
una excepción.
D.has_key(k)
Comprueba si el diccionario tiene la clave k. Es equivalente a la sintaxis k in D.
D.items()
Devuelve una lista de tuplas con pares clave-valor.
D.keys()
Devuelve una lista de las claves del diccionario.
D.pop(k[, d])
Borra la clave k del diccionario y devuelve su valor. Si no se encuentra dicha clave se devuelve d si se especificó el
parámetro o bien se lanza una excepción.
D.values()
Devuelve una lista de los valores del diccionario.
Cadenas
S.join(sequence)
Devuelve una cadena resultante de concatenar las cadenas de la secuencia seq separadas por la cadena sobre la que se
llama el método.
S.partition(sep)
Busca el separador sep en la cadena y devuelve una tupla con la subcadena hasta dicho separador, el separador en si, y la
subcadena del separador hasta el final de la cadena. Si no se encuentra el separador, la tupla contendrá la cadena en si y
dos cadenas vacías.
S.split([sep [,maxsplit]])
Devuelve una lista conteniendo las subcadenas en las que se divide nuestra cadena al dividirlas por el delimitador sep. En
el caso de que no se especifique sep, se usan espacios. Si se especifica maxsplit, este indica el número máximo de
particiones a realizar.
Listas
L.append(object)
Añade un objeto al final de la lista.
L.count(value)
Devuelve el número de veces que se encontró value en la lista.
L.extend(iterable)
Añade los elementos del iterable a la lista.
L.insert(index, object)
Inserta el objeto object en la posición index.
L.pop([index])
Devuelve el valor en la posición index y lo elimina de la lista. Si no se especifica la posición, se utiliza el último elemento
de la lista.
L.remove(value)
Eliminar la primera ocurrencia de value en la lista.
L.reverse()
Invierte la lista. Esta función trabaja sobre la propia lista desde la que se invoca el método, no sobre una copia.
El parámetro reverse es un booleano que indica si se debe ordenar la lista de forma inversa, lo que sería equivalente a
llamar primero a L.sort() y después a L.reverse().
Por último, si se especifica, el parámetro key debe ser una función que tome un elemento de la lista y devuelva una clave
a utilizar a la hora de comparar, en lugar del elemento en si.