¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Scripting Python en Linux
  3. Ir mas lejos con el lenguaje Python y la POO
Extrait - Scripting Python en Linux Desarrolle sus herramientas de sistema
Extractos del libro
Scripting Python en Linux Desarrolle sus herramientas de sistema Volver a la página de compra del libro

Ir mas lejos con el lenguaje Python y la POO

Introducción

Después del descubrimiento del lenguaje, de su lado objeto, de algunos módulos y librerías, todavía quedan algunas nociones por abordar en la POO, en el lenguaje Python objeto. Ese es el objetivo de este capítulo.

Algunos conceptos de objeto esenciales

Esta sección cubre solo los casos más comunes. Pero debes saber que hay 23 patrones de diseños en POO (Design Patterns en inglés). Todos se describen en el libro Design Patterns: Elements of Reusable Software publicado en 1994 y escrito por "la banda de los cuatro" (Gang of Four: Gof): Señores Gamma, Helm, Johnson y Vlissides.

1. El polimorfismo

Justo antes de abordar la herencia múltiple, primero debemos conocer el concepto de polimorfismo en POO.

Hay varios tipos, pero para una función en Python, el polimorfismo significa que la misma función se puede usar para diferentes tipos.

Como la función len(), por ejemplo, que le permite averiguar el número de elementos de un objeto.

>>> len("abcd")          # una cadena de caracteres  
4  
>>> len( [1,2,3,4] )     # una lista  
4 

Se dice que la función len() es polimórfica, porque trabaja con varios tipos de datos.

En el caso de Python, se trata del concepto "duck typing" (tipado de pato).

Duck typing es un concepto derivado de la frase atribuida a James Withcomb Riley (escritor y poeta estadounidense de principios del siglo XX): "Si veo un pájaro que vuela como un pato, grazna como un pato y nada como un pato, entonces llamo a este pájaro un Pato ".

Aplicado al caso de nuestra función len(), si un objeto tiene una función que le permite ser contado, entonces es contable.

Y en Python, contar un objeto es pedirle que ejecute una función especial __len__ (las funciones especiales en Python se abordarán pronto).

>>> "abcd".__len__()  
4 

Si un objeto tiene esta función, entonces es contable ("si vuela como un pato y si grazna como un pato…").

En Python, tenemos en cuenta las características de los objetos en lugar del tipo de los objetos.

2. La herencia múltiple

En el capítulo de descubrimiento de la POO, se discutió la noción de herencia.

En POO, algunos lenguajes permiten la herencia múltiple, como Python, C++, Eiffel y otros no, como Java, Ruby o Ada.

Hasta ahora, nadie ha podido demostrar si uno es mejor que el otro. Esto existe en Python, pero tampoco es obligatorio.

Y como siempre ocurre...

Los métodos especiales de instancias

Como acabamos de ver, las instancias de objetos de Python tienen métodos especiales. Cuando queremos contar los elementos de un objeto, usamos la instrucción len().

Esto ejecuta la función __len__ del objeto. Si no existe, es un descuido del programador o la función len() no es aplicable.

Los dobles guiones bajos significan (por convención), que es una función privada.

Solo son especiales en el nombre, porque en realidad son funciones definidas por el lenguaje que se utilizarán en determinados contextos.

Por ejemplo:

  • El contexto de enumeración con __len__

  • El contexto de conversión en cadenas de caracteres con __str__

  • El contexto de inicialización de instancia con __init__

Un pequeño ejemplo con __len__:

>>> class A():  
...   def __len__(self):  
...     return 5  
...  
>>> o = A()  
>>> len(o)  
5 

Hay muchos otros y esta sección detallará los principales.

1. Las funciones especiales clásicas

A continuación se muestra una tabla con las funciones especiales más comunes; algunas se conocen desde el principio.

La mayoría solo necesitan implementarse de una manera tan sencilla como __len__, Otras, por otro lado, se deben estudiar primero.

Función

Contexto

Observaciones sobre su uso

del__

Destrucción del objeto

 

init__

Inicialización del objeto

 

new__

Llamada antes de la creación del objeto

Debe devolver un objeto nuevo

repr__

Representación del objeto

print o en el intérprete

getattr__

Acceso a atributos inexistentes

gettatr(objeto, "nombre")

getattribute__

Acceso a los atributos

Todos los atributos

setattr__

Acceso a los atributos

settatr(objeto, "nombre")

delattr__

Eliminación de un atributo

 

getitem__

objeto [índice]

 

setitem__

objeto [índice] = valor

 

delitem__

del objeto [índice]

 

contains__

objeto ’in’ objeto

 

len__

Recuento, longitud del objeto

 

call__

Hacer un objeto llamable (callable)

 

missing__

La clave no existe en un diccionario

 

iter__

Debe devolver un objeto iterador

 

enter__

Administrador de contexto: entrada

With

exit__

Administrador de contexto: salida

With

hash__

Calcula un valor único

Si utilización...

El administrador de contexto (with) __enter__, __exit__

Las funciones especiales permiten hacer muchas cosas interesantes, pero con estas, __enter__ y __exit__, Python nos lleva un paso más allá.

Estas dos funciones permiten crear clases que se pueden usar con la palabra clave ’with’, que es lo que llamamos un administrador de contexto.

La instrucción with asociada con un bloque de código, crea un contexto específico válido para todo el bloque de código.

El típico ejemplo de uso es abrir un archivo.

# ejemplo con la apertura de un archivo (caso típico)  
with open(archivo) as f:  
   <bloque de código>  
  
# al final del bloque de código el archivo  
# se cerrará y la variable 'f' que representa al archivo  
# se eliminará. 

Esto ya existe, pero si quisiéramos escribir lo mismo tendríamos que implementar la función __enter__ y abrir el archivo, que lo cerraríamos en la función __exit__.

Entendiendo cómo funcionan estas dos funciones especiales, una a la entrada de un contexto y la otra a la salida, inmediatamente pensamos en la medida de tiempo que toma un bloque de código.

De ahí la idea de esta clase timing():

#archivo: f_spe/with1.py  
  
import math  
import time  
  
class timing():  ...

Los objetos mutables y no mutables

Bien podría decirlo de inmediato, esta sección ciertamente no será su sección favorita. Pero dado que este es un concepto importante, debe abordarse.

En el lenguaje Python, todo es objeto. Hay objetos mutables y objetos no mutables.

Por mutable, debemos entender modificable y sobre todo, diferenciar entre modificación y asignación.

Una variable es una referencia a un objeto.

Cuando asignamos una variable, no cambiamos el valor del objeto sino que cambiamos de objeto.

Cuando modificamos un objeto, modificamos el contenido del objeto.

Cuando escribimos:

a = 1  
a = 2 

a hace referencia al objeto 1 (el entero 1), luego referencia al objeto 2 (el entero 2), no es el objeto 1 el que ha cambiado, es el valor de ’a’.

Y es fácil entender que no podamos modificar el objeto ’1’ (el entero 1).

Por el contrario, si escribimos:

>>> a = [1]          # creación de una lista con el objeto 1  
>>> a[0] = 2         # modificación de la lista  
>>> a  
[2] 

modificamos el objeto lista creado, por lo que el objeto lista es modificable.

De ahí la noción importante de objetos mutables y objetos no mutables.

No se vay todavía, pronto entenderá para qué sirve.

1. Los mutables

Las listas, los diccionarios y los conjuntos son objetos mutables, por lo que se pueden modificar. Además, tienen métodos previstos para este propósito.

>>> a = [1,2,3]  
>>> id(a)  
140647764070880      # Id de a  
  
>>> a.append(4)  
>>> a  
[1, 2, 3, 4]  
  
>>> id(a)  
140647764070880      # Es el mismo objeto 

Pero atención, todavía hay algunas trampas:

>>>...

Información adicional sobre las clases en Python

1. Los atributos implícitos

Una clase, incluso vacía de cualquier atributo y método, contiene atributos implícitos para su gestión interna.

Y como siempre con Python, no hay nada oculto, solo necesita saber dónde está.

Sea el siguiente script:

class A():  
   pass  
  
for i in dir(A):  
   print("%20s |" % i, "%s" % getattr(A,i))  

que devolverá todo lo que se encuentra en una clase.

__class__        |  <class 'type'>  
__delattr__      |  <slot wrapper '__delattr__'   
                          of 'object' objects>  
__dict__         |  {'__module__': '__main__',   
                  '__dict__': <attribute '__dict__'   
                          of 'A' objects>,   
                  '__weakref__': <attribute '__weakref__'   
                          of 'A' objects>, '__doc__': None}  
__dir__          |  <method '__dir__' of 'object' objects>  
__doc__          |  None  
__eq__           |  <slot wrapper '__eq__' of 'object' objects>  
__format__       |  <method '__format__' of 'object' objects>  
__ge__           |  <slot wrapper '__ge__' of 'object' objects>  
__getattribute__ |  <slot wrapper '__getattribute__'   
                     ...

Las docstrings - cadenas de documentación

1. Definición

La docstring, cadena de caracteres de documentación, le permite documentar su código. No es necesario asignarlo, es suficiente con colocar las explicaciones en el lugar correcto, como regla general, al comienzo del objeto que desea documentar.

Es una buena costumbre, especialmente si tiene que trabajar con otras personas en un proyecto.

Escribir docstrings tiene muchas ventajas.

  • Hay una función help() en el intérprete que usa las docstrings.

  • La mayoría de las herramientas con un opción "python" muestran esta documentación.

  • Herramientas que permiten generar un documento en HTML a partir de las docstring.

  • Es un mecanismo de documentación estandarizado en el mundo de Python.

  • El código Python puede usar la docstring para leerlo o mostrarlo.

  • Podemos poner pruebas en las docstring, que luego sirven como ejemplos de uso.

  • En un archivo tenemos el código, la documentación y las pruebas.

  • Dos niveles simples y avanzados.

2. Uso

A continuación se muestra un ejemplo rápido:

>>> def f(arg1, arg2):  
...   """  
...   Documentación de la función f()  
...   """  
...   pass  
...  
>>> help(f)  
python docstring  
Help on function f in module __main__:  
  
f(arg1, arg2)  
   Documentación de la función f()  
(END) 

Nada demasiado complicado, solo hay que documentarlo usando comillas triples.

Es posible insertar docstrings al comienzo de un script o módulo, al comienzo de una clase o función.

A continuación se muestra un ejemplo un poco más completo:

# archivo: clase/docstr1.py  
  
# -----------------------  
# Un módulo documentado  
# -----------------------  
  
"""  
   Un...

Los decoradores

Python ofrece muchas funcionalidades, módulos, funciones, clases y este libro le permite repasar, con suerte, los conceptos básicos.

Con los decoradores abordamos las nociones avanzadas de programación, pero como es muy práctico y de uso común, hay que saber al menos qué es.

Un decorador en Python no es ni más ni menos que una función que se ejecutará antes que una función.

Esto permite, entre otras cosas, modificar el comportamiento de esta función antes de su ejecución.

La sintaxis es la siguiente:

@decorador  
def la_función_a_decorar():  
      pass 

Pero cuidado, no es tan intuitivo como parece.

Primera trampa: el decorador se ejecuta durante la definición de la función; se le da una función y debe devolver una función, que sustituirá a la función "decorada".

Ejemplo de decorador que no hace nada

#archivo: deco/deco0.py  
  
def mi_deco(funcion):  o
   return funcion  
  
@mi_deco  
def funcion1():  
    print("Soy la función 1 ")  
  
@mi_deco  
def funcion2():  
    print("Soy la función 2 ")  
  
@mi_deco  
def funcion3():  
    print("Soy la función...

Los iteradores, generadores y otras expresiones generadoras

Con el lenguaje Python, es posible crear todos los objetos informáticos imaginables, y más. En el "más" encontramos, entre otras cosas, las nociones de iteradores y generadores.

1. Los iteradores

Un iterador es un objeto que le permite examinar todos los objetos contenidos en otro objeto. Con Python es posible crear sus propios iteradores.

Pero primero hay que estudiar un poco lo que ocurre en un ejemplo más clásico, el bucle ’for’:

for <variable> in <object iterable>:  
   <bloque de código> 

¿Cómo funciona este pilar de la programación?

En Python, esquemáticamente, sucede de esta manera:

Primero, el intérprete comienza invocando la función iter(<object_iterable>), para obtener un objeto iterable a cambio.

Una vez que el objeto ha sido capturado, el bucle ’for’ buscará obtener el siguiente valor llamando a la función next() en el objeto y asignar este valor a la variable prevista para este propósito.

El bucle llamará a la función next() hasta que no haya más valor y la función next() devuelva una excepción.

Para comprender completamente cómo funciona, es posible hacerlo manualmente.

>>> objeto_iterable = iter([1,2,3])  
>>> objeto_iterable  
<list_iterator object at 0x7fda31b8c870>  
  
>>> next(objeto_iterable)  
1  
>>> next(objeto_iterable)  
2  
>>> next(objeto_iterable)  
3  
  
>>> next(objeto_iterable)  
Traceback (most recent call last):  
 File "<stdin>", line 1, in <module>  
StopIteration  
  
>>> dir(objeto_iterable)  
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__',   
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', 
'__init__', '__init_subclass__', '__iter__', '__le__',  
'__length_hint__', '__lt__', '__ne__', '__new__', '__next__',  
'__reduce__', '__reduce_ex__'...

Gestionar sus propias excepciones

Es un hecho, todo es objeto con Python y por supuesto, las excepciones también lo son.

Es posible que esto nunca suceda, especialmente con scripts pequeños, pero en proyectos grandes, las excepciones son una forma de comunicarse con los otros programadores y usuarios.

Después de todo, a veces escuchamos que podemos juzgar la calidad de un producto informático, por la cantidad de mensajes de error que puede manejar.

En Python, las excepciones provienen de la clase… Exception.

Y para crear sus propias excepciones, es suficiente con crear una clase que herede de la clase Exception y redefina la función __str__.

A continuación se muestra un script de ejemplo:

# archivo: except/except4.py  
  
class MiExcepcion(Exception):  
   """  
   La documentación es primordial sobre todo para las excepciones  
   que, recuerdo, no son objetos sin importancia 
   """  
   def __init__(self, message):  
       self.message = message  
  
   def __str__ (self):  
       return "MiExcepcion msg=%s" % self.message  
  
  
try:  
   print("Antes del error")  
   raise MiExcepcion("MENSAJE...

Las funciones nativas

Python tiene algunas funciones nativas, que se pueden usar directamente. Aquí está la lista, ordenada por temática.

Para obtener una lista alfabética, consulte la documentación oficial: https://docs.python.org/es/3/library/functions.html

1. Las funciones nativas in clasificables

input([prompt])

Lee en la entrada estándar.

Si el módulo readline está cargado, input() lo usará para manejar un historial.

open()

Abra un archivo (consulte el capítulo El lenguaje Python - Las entradas/salidas (archivo y otros)).

print()

La función de impresión (ver capítulo El lenguaje Python - La función print()).

breakpoint()

Usado con el depurador, establece un punto de interrupción para detener la ejecución.

compile(code, filename, mode)

Compila el código fuente de Python.

Para ser utilizado de la siguiente manera:

>>> code = compile("2+2", "test",  
"single")   
>>> exec(code)   4 

mode puede ser:

  • eval: evaluación de una expresión.

  • exec: la fuente puede ser bloques completos de código.

  • single: la fuente se compone de una instrucción interactiva como en el intérprete.

eval(expression, [globals[, locals]])

Evalúa una expresión.

>>> eval("3+2")  
5 

exec()

Ejecuta código Python compilado con compile().

help()

Llama al sistema de ayuda nativo de Python.

format(valor[,format])

Da formato a un valor (ver capítulo El lenguaje Python - La función print() - Print() formateo cadena.format ()).

globals()

Devuelve la tabla de las variables globales (ver capítulo El lenguaje Python - Las funciones - Las funciones y el alcance de las variables).

locals()

Devuelve la tabla de las variables locales (ver capítulo El lenguaje Python - Las funciones - Las funciones y el alcance de las variables).

2. Las funciones nativas binarias

Las funciones binarias nativas permiten la manipulación de bytes y tablas de bytes.

bytearray()

Crea una tabla de bytes.

bytes()

Devuelve un nuevo objeto bytes.

memoryview()

Permite manejar un objeto como una secuencia de bytes sin hacer una copia.

Para más detalles visite: https://docs.python.org/es/3/library/stdtypes.html#bytes-methods

3. Las funciones nativas de conversión o creación de tipo

Estas funciones nativas permiten...

Resumen

En este punto, es hora de pasar a un nivel superior y dejar de considerarse un "principiante" en el lenguaje Python. Los tipos de datos, el lenguaje, la POO, la librería estándar, etc. todo esto se ha abordado. Ahora es necesario practicar, con la mayor regularidad posible, el lenguaje Python.

Los siguientes cinco capítulos tratan temas mucho más prácticos, en particular lo que Python puede proporcionar como información sobre un sistema, el manejo de diferentes formatos de archivo o la simulación de actividad en una base de datos, pasando por la generación de informes en PDF.