Tema 13. Scripts y Módulos
Explicación del uso y funcionamiento de Scripts y Módulos en Python 3.
Scripts
Script. Es un archivo que contiene líneas de código. En nuestro caso, dichas líneas estarán escritas con el lenguaje de programación Python
.
Un script de Python
tiene la extensión .py
.
Para ejecutar un script de Python
necesitamos un intérprete como bien puede ser Spyder
, Jupyter
, Google Colab
o Python
(en Linux) pero para crearlo nos basta un editor de texto.
Módulos
Módulo. Es una librería de código. Es un script que contiene un conjunto de funciones.
Un módulo de Python
, al ser un script, tiene la extensión .py
.
Para ejecutar un módulo de Python
necesitaremos seguir los mismos pasos que cuando trabajábamos con scripts
Creando un módulo
Hemos dicho que un módulo es un script que contiene funciones. Por tanto, lo primero que hacemos es crear un nuevo documento con el editor de texto al que llamaremos my_first_module.py
.
En él declararemos las siguientes funciones:
def sum(*numbers):
"""
Función que suma los elementos que introduzcamos por parámetro
"""
result = 0
for n in numbers:
result += n
return result
def prod(*numbers):
"""
Función que multiplica los elementos que introduzcamos por parámetro
"""
result = 1
for n in numbers:
result *= n
return result
def description():
print("Este módulo tiene 3 funciones: ")
print("\t- la que muestra la descripción del módulo")
print("\t- la que suma los números que introduzcamos por parámetro")
print("\t- la que multiplica los números que introduzcamos por parámetro")
Importando un módulo
Para importar un módulo de Python dentro de un script deberemos tenerlo alojado en la misma carpeta que el script o en la ruta en la que el interprete los tenga almacenados (en mi caso es /usr/lib/python3.9/
).
import my_first_module
my_first_module.my_description()
total_sum = my_first_module.my_sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
total_prod = my_first_module.my_prod(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
print("\nEl resultado de la suma ha sido {} y el del producto, {}".format(total_sum, total_prod))
Este módulo tiene 3 funciones:
- la que muestra la descripción del módulo
- la que suma los números que introduzcamos por parámetro
- la que multiplica los números que introduzcamos por parámetro
El resultado de la suma ha sido 55 y el del producto, 3628800
Renombrando un módulo
Como recordaréis, cuando vimos por primera vez la palabra reservada import
, si lo combinábamos con la palabra reservada as
, conseguíamos renombrar el objeto que importábamos.
En este caso, my_first_module
es un nombre muy largo. Podemos renombrarlo a mfm
con la siguiente línea de código, de modo que a partir de ahora cuando queramos invocar alguna función de nuestro módulo, en vez de preceder a cada función por my_first_module
, lo haremos por mfm
import my_first_module as mfm
mfm.my_description()
total_sum = mfm.my_sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
total_prod = mfm.my_prod(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
print("\nEl resultado de la suma ha sido {} y el del producto, {}".format(total_sum, total_prod))
Este módulo tiene 3 funciones:
- la que muestra la descripción del módulo
- la que suma los números que introduzcamos por parámetro
- la que multiplica los números que introduzcamos por parámetro
El resultado de la suma ha sido 55 y el del producto, 3628800
Variables en un módulo
Hasta ahora solo hemos visto como acceder a funciones de un módulo, pero los módulos también pueden contener variables. Añadamos a nuestro módulo las dos siguientes líneas de código:
sum1to10 = my_sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
prod1to10 = my_prod(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
import my_first_module as mfm
print("\nEl resultado de la suma de los 10 primeros números enteros es {} y su producto, {}".
format(mfm.sum1to10, mfm.prod1to10))
El resultado de la suma de los 10 primeros números enteros es 55 y su producto, 3628800
Si recordáis, cuando vimos la función import
, observamos que no era necesario importar todo el módulo, sino que podíamos importar funciones o incluso variables concretas del módulo con la sintaxis siguiente, y así evitar tener que indicar el nombre del módulo previo al nombre de la función o la variable en cuestión
from my_first_module import sum1to10, prod1to10
De modo que el print
anterior queda modificado del siguiente modo
print("\nEl resultado de la suma de los 10 primeros números enteros es {} y su producto, {}".
format(sum1to10, prod1to10))
El resultado de la suma de los 10 primeros números enteros es 55 y su producto, 3628800
Finalmente, así como podemos modificar el nombre de los módulos, también podemos modificar el nombre tanto de las funciones como de las variables del módulo:
from my_first_module import my_description as mfm_desc
mfm_desc()
Este módulo tiene 3 funciones:
- la que muestra la descripción del módulo
- la que suma los números que introduzcamos por parámetro
- la que multiplica los números que introduzcamos por parámetro
Módulos de Python
Python
Python
es un software libre que de por sí ya tiene muchos módulos creados que nos son de mucha utilidad:
pwn
para trabajar en la escritura de exploits.pycryptodome
para trabajar con criptografía a bajo nivel.os
para interacción entre el interprete y el sistema.socket
para trabajar con redes.
¡Y muchos más!
En secciones futuras veremos en detalle algunos de estos módulos para saber aprovecharlos al máximo.
La función dir()
dir()
La función dir()
aplicada a un script nos devuelve los métodos y las variables que contiene.
Si la aplicamos a nuestro módulo, al que recordad habíamos renombrado como mfm
, lo que obtenemos es la siguiente lista de métodos y variables.
dir(mfm)
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'my_description',
'my_prod',
'my_sum',
'prod1to10',
'sum1to10']
Observación. Como resultado no solamente hemos obtenido los métodos y las variables creados por nosotros, sino que se muestran algunos atributos extras como .__file__
, que guarda el path donde está guardado el módulo, que son los que se han creado por defecto y a los cuales también podemos acceder:
mfm.__file__
'/content/drive/My Drive/python-basico/scripts/my_first_module.py'
EJERCICIO 1:
Vamos a crear un script llamado geometry.py
y en él vamos a crear la clase RegularPolygon
.
El constructor tomará 2 parámetros:
base
: longitud de la basen
: número de lados
El método
.__str__()
mostrará "Soy un polígono de {} lados de longitud {}".El método de instancia
.apothem()
calculará el apotema. Lo convertiremos en una propiedad.El método de instancia
.area()
calculará el área del polígono. Lo convertiremos en una propiedad.El método de instancia
.perimeter()
calculará el perímetro del polígono. Lo convertiremos en una propiedad.
#Contenido del script geometry.py
#NO EJECUTAR, UTILIZAR EL SCRIPT
class RegularPolygon():
import math
def __init__(self, base, n):
self.base = base
self.n = n
def __str__(self):
return ("Soy un polígono de {} lados de longitud {}".format(self.n, self.base))
@property
def apothem(self):
return (self.base / (2 * tan((360 / self.n)/ 2)))
@property
def perimeter(self):
return self.base * self.n
@property
def area(self):
return ((self.perimeter * self.apothem) / 2)
EJERCICIO 2:
Vamos a crear las clases Triangle
, Square
y Pentagon
, que hereden de la clase RegularPolygon
. Solamente cambiaremos el constructor.
class Triangle(RegularPolygon):
def __init__(self, base):
super().__init__(base, 3)
class Square(RegularPolygon):
def __init__(self, base):
super().__init__(base, 4)
class Pentagon(RegularPolygon):
def __init__(self, base):
super().__init__(base, 5)
EJERCICIO 3:
Vamos a crear las clases Tetrahedron
y Cube
. La primera hereda de la clase Triangle
y la segunda, de la clase Square
. A ambas clases les vamos a añadir el método .volume()
, que convertiremos en ambos casos a propiedad. También tendremos que modificar ligeramente la propiedad .area
y el método .__str__()
.
class Tetrahedron(Triangle):
def __str__(self):
return "Soy un tetraedro con lados de longitud {}".format(self.base)
@property
def area(self):
return 4 * super().area
@property
def volume(self):
return math.sqrt(2) / 12 * self.base
class Cube(Square):
def __str__(self):
return "Soy un cubo con lados de longitud {}".format(self.base)
@property
def area(self):
return 6 * super().area
@property
def volume(self):
return super().area * self.base
EJERCICIO 4:
Vamos a crear la clase Circle
que por parámetro tome el radio r
y tenga 4 métodos: el constructor y las propiedad .diameter()
, .perimeter()
y .area()
.
Finalmente vamos a crear la clase Cylinder
que herede de Circle
y Square
. Vamos a modificar la propiedad .area()
y crear la propiedad .volume()
.
#ME DA PEREZA... YA LO HARÉ.
REPASO
EJERCICIO 1:Crea un script llamado vectors.py. Ábrelo y crea una clase llamada Vector2D. El constructor debe guardar las coordenadas x e y del vector.
from math import sqrt
class vector2D():
def __init__(self, x, y):
self.x = x
self.y = y
EJERCICIO 2: Crea los siguientes métodos:
• La propiedad .module que devuelva el módulo del vector. Recuerda que el módulo de un vector 2D se calcula como p x2 + y2. Para calcular raíces cuadradas, dispones del método math.sqrt().
• El método de instancia .scalar_prod() que multiplique el vector por el número real dado por parámetro, que por defecto vale 1. Configura el método .str para que se nos muestre por pantalla el vector de la forma (x, y).
def __str__(self):
return "({}, {})".format(x, y)
@property
def module(self):
return sqrt(x**2 + y **2)
def scalar_prod(scale = 1):
#Sobreescribe el vector inicial
self.x = x * scale
self.y = y * scale
EJERCICIO 3: Crea los siguientes métodos:
• El método de clase .sum() que dados dos vectores los sume y devuelva un objeto de la clase Vector2D.
• El método de clase .subtract() que dadoas dos vectores los reste y devuelva un objeto de la clase Vector2D.
@classmethod
def sum(vector1, vector2):
return cls(vector1.x + vector2.x, vector1.y + vector2.y)
@classmethod
def substract(vector1, vector2):
return cls(vector1.x - vector2.x, vector1.y - vector2.y)
EJERCICIO 4: Crea los siguientes métodos:
• El método estático .dot_product() que dados dos vectores calcule su producto escalar. Recuerda que el producto escalar de 2 vectores 2D u y v se calcula como u · v = uxvx + uyvy
• El método de clase .distance() que dados dos vectores calcule la distancia entre ellos. Recuerda que la distancia entre 2 vectores 2D u y v se calcula como p(ux − vx)2 + (uy − vy)2
@staticmethod
def dot_product(vector1, vector2):
return vector1.x * vector2.x + vector1.y * vector2.y
@classmethod
def distance(cls, vector1, vector2):
return sqrt((vector1.x - vector2.x) ** 2 + (vector1.y - vector2.y) ** 2 )
EJERCICIO 5: Ahora crea la clase Vector3D que herede de la clase Vector2D. Empieza con el constructor para que además de las coordenadas x e y, también tome la coordenada z. Recuerda que puedes utilizar el método .super() para acceder a métodos de la clase padre.
class Vector3D(Vector2D):
def __init__():
super().__init__(x, y)
self.z = z
EJERCICIO 6: Crea en la clase Vector3D los métodos siguientes:
• el método .str() para que muestre el vector por pantalla de la forma (x, y, z).
• la propiedad .module. Al tener vectores 3D, el módulo se calcula como p x 2 + y 2 + z 2
• el método de instancia .scalar_prod()
• los métodos de clase .sum() y .subtract() para que devuelvan objetos de la clase Vector3D.
• el método estático .dot_product(), pues ahora el producto escalar se calcula como u · v = uxvx + uyvy + uzvz
• el método de clase .distance(), pues ahora la distancia se calcula como q (ux − vx) 2 + (uy − vy) 2 + (uz − vz) 2
Recuerda que dispones del método .super() para evitar repeticiones innecesarias de código.
def __str__():
return super().__str__()[: -1] + ", {})".format(self.z)
@property
def module(self):
return sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
def scalar_prod(self, scale):
super().scalar_prod()
self.z = self.z * scale
@classmethod
def sum(cls):
return cls(vector1.x + vector2.x, vector1.y + vector2.y, vector1.z + vector1.z)
@classmethod
def substract(cls):
return cls(vector1.x - vector2.x, vector1.y - vector2.y, vector1.z - vector1.z)
@classmethod
def distance(cls, vector1, vector2):
sqrt((vector1.x - vector2.x) ** 2 + (vector1.y - vector2.y) ** 2 + (vector1.z - vector2.z) ** 2)
@staticmethod
def dot_product(vector1, vector2):
return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z
EJERCICIO 7: Crea en la clase Vector3D los métodos siguientes:
• el método de clase .zero() que devuelva un objeto Vector3D con todas sus componentes 0.
• el método de clase .horizontal() que devuelva un objeto Vector3D con todas sus componentes 0 salvo la primera que valdrá 1.
• el método de clase .vertical() que devuelva un objeto Vector3D con todas sus componentes 0 salvo la segunda que valdrá 1.
• el método de clase .forward() que devuelva un objeto Vector3D con todas sus componentes 0 salvo la tercera y última que valdrá 1.
@classmethod
def zero(cls):
return cls(0, 0, 0)
@classmethod
def horizontal(cls):
return cls(1, 0, 0)
@classmethod
def vertical(cls):
return cls(0, 1, 0)
@classmethod
def forward(cls):
return cls(0, 0, 1)
EJERCICIO 8: Crea en la clase Vector2D el método de instancia .extend_to_3D() que devuelva un objeto de la clase Vector3D siendo la componente z el valor indicado por parámetro, que por defecto valdrá 0.
def extend_to_3D(self, z):
return Vector3D(self.x, self.y, z)
EJERCICIO 9: En un notebook de Google Colab, importa el script, crea dos objetos de la clase Vector2D y prueba que todos los métodos de la clase Vector2D funcionan correctamente.
#En mi caso tengo permitido el acceso de Colab a Drive permanente por lo que no tengo que montar el disco virtual
from google.colab import drive
drive.mount('/content/drive')
%cd /content/drive/MyDrive/Python/scripts/
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
/content/drive/MyDrive/Python/scripts
from vectors import *
vector2D_1 = Vector2D(1, 1)
vector2D_2 = Vector2D(5, -5)
print(vector2D_1, vector2D_2)
(1, 1) (5, -5)
print(vector2D_1.module)
print(vector2D_2.module)
1.4142135623730951
7.0710678118654755
print(vector2D_1.scalar_prod(3))
print(vector2D_2.scalar_prod(-2))
(3, 3)
(-10, 10)
vector3D_1 = vector2D_1.extend_to_3D(5)
print(vector3D_1)
(3, 3, 5)
print(Vector2D.sum(vector2D_1, vector2D_2))
print(Vector2D.substract(vector2D_1, vector2D_2))
print(Vector2D.distance(vector2D_1, vector2D_2))
print(Vector2D.dot_product(vector2D_1, vector2D_2))
(6, -4)
(-4, 6)
7.211102550927978
0
EJERCICIO 10: Ahora crea dos objetos de la clase Vector3D y prueba que todos los métodos de la clase Vector3D funcionan correctamente.
vector3D_1 = Vector3D(1, 2, 3)
vector3D_2 = Vector3D(-3, 10, -2)
print(vector3D_1, vector3D_2)
(1, 2, 3) (-3, 10, -2)
print(vector3D_1.module)
print(vector3D_2.module)
3.7416573867739413
10.63014581273465
print(vector3D_1.scalar_prod(3))
print(vector3D_2.scalar_prod(-2))
None
None
print(Vector2D.sum(vector3D_1, vector3D_2))
print(Vector2D.substract(vector3D_1, vector3D_2))
print(Vector2D.distance(vector3D_1, vector3D_2))
print(Vector2D.dot_product(vector3D_1, vector3D_2))
(-2, 12)
(4, -8)
8.94427190999916
17
Last updated