Crear APIs REST con Python y Hug

Hug framework

Hola a todos, han sido unas semanas muy divertidas, en las cuales he ido aprendiendo a ser padre, por lo que deje un poco olvidado el blog; pero creí conveniente dedicarle un tiempo. En esta entrada, vamos a aprender a crear APIs REST con Python y Hug, un framework que llevo un tiempo utilizando en mis proyectos, y que me ha dejado un buen sabor de boca, debido a su rendimiento y facilidad para obtener productos finales.

Continuar leyendo

Tipos estáticos en Python con Mypy

tipos estáticos en python

Tipos estáticos en Python

Hola a todos, en esta entrada vamos a hablar sobre los tipos estáticos en Python, utilizando Mypy, esta herramienta desarrollada principalmente por un equipo de Dropbox, incluyendo al creador del lenguaje Guido Van Rossum.

tipos estáticos en python

Tipado estático en lenguajes dinámicos

Desde hace un par de años, han salido a la luz verificadores de tipado estático para lenguajes dinámicos populares como Hack para PHP o Flow y TypeScript para Javascript, los cuales han sido ampliamente adoptados por la comunidad (yo soy un feliz usuario de Flow).

Hace unos años tres años, una sintaxis provisional de anotaciones para tipos estáticos fue añadida a Python 3, sin embargo, los tipos estáticos en Python no habían sido adoptados aún ampliamente, debido a que no existía una herramienta para llevar a cabo la verificación. Es aquí donde llegamos a Mypy.

¿Qué es Mypy?

Como podemos encontrar en su página oficial:

Mypy is an experimental optional static type checker for Python that aims to combine the benefits of dynamic (or “duck”) typing and static typing. Mypy combines the expressive power and convenience of Python with a powerful type system and compile-time type checking. Mypy type checks standard Python programs; run them using any Python VM with basically no runtime overhead.

Cabe decir que a pesar de que Mypy ya soporta la mayoría de las características de Python, aún se encuentra en desarrollo. Y como es bien sabido, nada como un ejemplo para ver de que estamos hablando, también sacado de la página oficial de Mypy.

tipos estáticos en PythonSe puede observar en la imagen la diferencia al utilizar el tipado estático que Mypy nos permite, como mencionaba en las líneas de arriba, se utiliza la sintaxis de anotaciones para tipos estáticos de Python, logrando con esto no afectar el rendimiento de nuestros scripts ni nos hace añadir códigos extraños y dotándonos de los beneficios de tener tipado estático en un lenguaje flexible y dinámico como lo es Python.

Instalando Mypy

Para instalar esta herramienta, debemos tener Python >= 3.3 y desde nuestra línea de comandos debemos ejecutar lo siguiente:


pip install mypy

Empezando con Mypy

Con Mypy contamos con dos tipos de anotaciones diferentes, la mencionada anteriormente (la sintaxis de anotaciones de tipos estáticos de Python) y la sintaxis utilizando comentarios. Veamos un ejemplo:

# Primero desde el módulo typing debemos
# importar los tipos que vamos a necesitar
from typing import List


# Utilizando la sintaxis de anotaciones
def sum_list_to_str(nums: List[int]) -> str:
    """Suma los números de una lista, y retorna el resultado como string"""
    return str(sum(nums))


# Utilizando comentarios
def sum_list_to_str_2(nums):
    # type: (List[int]) -> str
    """Suma los números de una lista, y retorna el resultado como string"""
    return str(sum(nums))

Para ejecutar los scripts con anotaciones de Mypy, podemos realizarlo utilizando cualquier intérprete de Python, esto se debe a que Mypy comparte esta excelente propiedad con el verficador de código de Javascript, Flow. Esto es grandioso ya que significa que podemos añadir Mypy a nuestros proyectos sin preocuparnos de como debemos correr estos proyectos con Python.

De igual manera, podemos utilizar Mypy como un linter, el cual puede mostrar nuestros errores en un formato agradable. Por ejemplo, vamos a cambiar el valor de retorno en nuestra segunda función para comprobar esto, quedando de la siguiente manera:

# Utilizando comentarios
def sum_list_to_str_2(nums):
    # type: (List[int]) -> int
    """Suma los números de una lista, y retorna el resultado como string"""
    return str(sum(nums))

Ahora, desde nuestra línea de comandos ejecutamos:


mypy mi_archivo.py

Output: mi_archivo.py:16: error: Incompatible return value type (got "str", expected "int")

Genial, ¿no crees? Gracias a esto será muy sencillo depurar nuestros scripts y evitar errores durante la ejecución que muchas veces se vuelven una pesadilla para reparar. Si te interesa profundizar sobre las anotaciones de tipo de Mypy, puedes consultar el Cheat Sheet de Mypy y también en PEP-484.

En resumen…

Utilizar Mypy es una excelente opción cuando:

  • Buscamos utilizar tipos estáticos en Python.
  • Verificar tipos en “tiempo de compilación”
  • Lograr un mantenimiento sencillo.
  • Buscamos que nuestro código sea simple de entender y más sencillo de modificar sin introducir “bugs”
  • Llevar nuestros scripts de dinámicos a estáticos.
  • Desarrollar nuestros scripts con tipado dinámico e ir añadiendo tipado estático conforme el código vaya madurando, o migrar código Python existente a tipado estático.
  • Declaraciones de tipo que actúen como documentación verificada por máquina.

Y bien, es todo por esta ocasión, espero la explicación haya sido clara y cualquier comentario no dudes en dejarlo aquí abajo, o con confianza mencionalo por Twitter o en la Fanpage de Facebook, y aprovecho a invitarte para que me sigas en las redes y compartas el contenido del blog, con lo cual me harías un gran favor.

Saludos.

¿Qué es __name__ en Python?

¿Qué es __name__ en Python?

Hola a todos, ya llevo un rato sin escribir, esto se debe a que me encuentro pasando por muchas cosas personales, muy buenas, y me estoy enfocando en ellas, pero sin embargo tengo muchos borradores que espero ya terminar y publicar, como adelanto les puedo decir que tratan sobre Xamarin, Vue, Python y Ruby. Y bien, bienvenidos a esta entrada donde a petición de un usuario en FB voy a intentar explicar qué es __name__ en Python.

que es __name__ en python

Seguro que al estar leyendo un libro sobre Python, o leyendo el código de algún script de tu interés has visto algo como lo siguiente:


class MyComplexClass(object):
    def __init__(self):
    print("Hello World!")
    # Código de la clase con lógica demasiado compleja
    # ...

def init():
    my_complex = MyComplexClass()
    # Resto de la lógica del script
    # ...

if __name__ == '__main__':
    main()

Si eres curioso, o el libro donde viste esto es de un buen autor, seguro explicaron de que se trata, si no es tu caso, o buscando sobre el tema diste con el blog sigue leyendo, no te defraudaré 🙂

Pequeño paréntesis cultural

Muchos lenguajes de programación, normalmente los lenguajes compilados (llámese C, Java, Go, Rust, etc.), requieren de una función que actúe como punto de entrada, esto es, que será lo primero en ejecutarse al correr el programa. Comúnmente esta función de punto de entrada es main() (Espero haber sido breve).

Sigo sin saber qué es __name__ en Python

Lo sé, no desesperes, las líneas anteriores eran necesarias. Python, al igual que muchos de los lenguajes de script, ejecuta el código interpretando cada línea de arriba a abajo. Cuando el intérprete de Python encuentra el bloque if anterior (if __name__ == ‘__main__’), comprueba el atributo especial __name__, con el objetivo de ver si su valor actual es “__main__”.

Tiene sentido…al cumplirse esta condición, estaremos seguros de que nuestro módulo (script) es el primero en ser “cargado” por el intérprete de Python. Suponiendo que el código anterior esté almacenado en el archivo mi_super_script.py y lo ejecutamos:


python mi_super_script.py

El resultado será lo que este definido dentro de la función init(). Por lo tanto, aunque Python no especifica como tal, oficialmente una función de punto de entrada que será ejecutada en primer lugar, el bloque anterior es una forma “idiomática” de lograr el mismo objetivo.

Por lo tanto, con esto nos aseguramos de que ese código en especifico será ejecutado únicamente cuando nuestro script sea ejecutado, y no cuando sea importado. ¿A qué me refiero?


# mi_super_script.py
def main():
    print("Script 1!")

if __name__ == '__main__':
    main()

# mi_super_script_dos.py
import .mi_super_script

def init():
    print("Script 2!")

if __name__ == '__main__':
    init()

Si nosotros ejecutaramos ambos scripts, tendríamos la siguiente salida:


python mi_super_script.py

# Output: "Script 1!"

python mi_super_script_dos.py

# Ouput: "Script 2!"

Como podrás notar, a pesar de que en mi_super_script_dos.py estamos importando el código de mi_super_script.py la función main no ha sido ejecutada.

Generalmente no es útil sobreescribir el valor por defecto que el intérprete le otorga a __name__, pero existe un caso puntual donde esto se lleva a cabo con frecuencia: funciones decoradoras.

Voy a dejar ese tema para otra entrada si les interesa la relación que tienen con el atributo especial __name__ les sugiero leer esto y esto.

Y bien, es todo por esta ocasión, espero la explicación haya sido clara y cualquier comentario no dudes en dejarlo aquí abajo, o con confianza mencionalo por Twitter o en la Fanpage de Facebook, y aprovecho a invitarte para que me sigas en las redes y compartas el contenido del blog, con lo cual me harías un gran favor.

Saludos.

Transpuesta de una matriz con Python

Transpuesta de una matriz con Python

Hola, y bienvenido a esta 5ta y última entrada de la serie de operaciones en matrices con Python. En esta ocasión, es el turno para aprender a obtener la transpuesta de una matriz con Python.

Una vez más aprovecho para invitarte a que me sigas en mis redes Twitter o en la Fanpage de Facebook, donde podrás estar informado de nuevas entradas del blog, o si tienes alguna duda o comentario puedes hacermelo saber.

Operaciones en matrices con python

Hasta el momento las entradas de esta serie han sido las siguientes:

Creando la clase Base

Suma de matrices

Producto por un escalar

Producto de matrices

Transpuesta

Como en las entradas anteriores, para realizar el ejemplo utilizaremos la clase base que creamos en la primera entrada de esta serie, así que si aún no la tienes, lee la primera entrada y regresa, aquí estaré esperando 😀

Un poco de teoría

Como en cada entrada de esta serie, un poco de teoría no viene mal para explicar el problema que tratamos de resolver. Para obtener la transpuesta de una matriz con python, primero debemos conocer como se lleva a cabo este procedimiento.

La transpuesta de una matriz A es otra matriz B tal que las filas de B son las columnas de A. Para ilustrar mejor estas palabras les dejo la siguiente imagen:

transpuesta de una matriz con pythonCódigo: Transpuesta de una matriz con python

Para obtener la matriz transpuesta de A es bastante simple como podrás observar en el siguiente código:

def transpose(self):
    for j in range(self.cols):
        row = []
        for i in range(self.rows):
            row.append(self.get_value_of_position(i,j))
        yield row

Nuevamente, y para mantener un uso eficiente de memoria, se define la función como un método generador. Esto evita tener que almacenar una copia de la matriz completa, la cual podría tener grandes dimensiones y ocupar bastante memoria.

Básicamente recorremos la matriz como lo estuvimos haciendo anteriormente, pero esta vez invirtiendo los indices, de esta forma logramos “voltear” nuestra matriz.

Ahora, si queremos verificar el funcionamiento de la función basta con escribir un código como el siguiente:

# Matriz A
m = Matrix(2,3)
# Primera fila
m.define_elem(0,0,1)
m.define_elem(0,1,2)
m.define_elem(0,2,3)
# Segunda fila
m.define_elem(1,0,7)
m.define_elem(1,1,8)
m.define_elem(1,2,9)

# Imprime la matriz A
print("Matriz A")
m.show_matrix()

# Imprime la transpuesta de la matriz A
print("B: Transpuesta de A")
for e in m.transpose():
    print(e)

Teniendo una salida como la siguiente:


Matriz A
| 1 | 2 | 3 |

| 7 | 8 | 9 |

B: Transpuesta de A
[1, 7]
[2, 8]
[3, 9]

Como vemos, es una salida exactamente igual a la imagen de ejemplo. Hasta este punto nuestra clase debe encontrarse de la siguiente forma (Recuerda que también se encuentra todo el código en el repositorio de Github):

#!/usr/bin/python
# -*- coding: utf-8 -*-
# author: Gabriel Cueto <TheMushr00m - @Mushr00m_Dev>


class Matrix(object):
    _n = 0
    _m = 0
    _elems = None

    def __init__(self, n, m):
        """Inicializa la matriz con valor 0 en cada posición"""
        self._n = n
        self._m = m
        self._elems = []
        for i in range(self._n):
            self._elems.append([])
            for j in range(self._m):
                self._elems[i].append(0)

    def define_elem(self, i, j, v):
        """ Sobreescribe el valor de una celda """
        self._elems[i][j] = v

    def show_matrix(self):
        """ Imprime los valores almacenados en la matriz """
        for i in range(self._n):
            for j in range(self._m):
                # Imprime de una forma elegante la matriz
                print("| {0} ".format(self.get_value_of_position(i, j)), sep=',', end='')
            print('|\n')

    def get_cols(self):
        """ Devuelve el número de columnas en la matriz """
        return self._m

    def get_rows(self):
        """ Devuelve el número de filas en la matriz """
        return self._n

    def get_value_of_position(self, i, j):
        """ Devuelve el valor en la fila(i), columna(j) de la matriz """
        return self._elems[i][j]

    def add(self, *matrix):
        """ Puede recibir varias matrices como argumentos """
        for i in range(self.rows):
            row = []
            for j in range(self.cols):
                tmp = self.get_value_of_position(i, j)
                for m in matrix:
                    tmp += m.get_value_of_position(i, j)
                row.append(tmp)
            yield row

    def scalar_product(self, x):
        """ x: Número real """
        for i in range(self.rows):
            row = []
            for j in range(self.cols):
                row.append(self.get_value_of_position(i, j)*x)
            yield row

    def product(self, m):
        """ Recibe una matriz como argumento
         Args:
             m (Matrix): Represents an instance of Matrix
        """
        if self.rows is not m.cols:
            raise Exception('Number of rows'
                            'and number of columns'
                            "don't match")
        for i in range(self.rows):
            row = []
            for j in range(m.cols):
                add = 0
                for c in range(self.cols):
                    add += self.get_value_of_position(i, c) * \
                           m.get_value_of_position(c, j)
                row.append(add)
            yield row

    def transpose(self):
        for j in range(self.cols):
            row = []
            for i in range(self.rows):
                row.append(self.get_value_of_position(i, j))
            yield row

    cols = property(fget=get_cols)
    rows = property(fget=get_rows)

Y bien, es todo por esta ocasión, espero la explicación haya sido clara y cualquier comentario no dudes en dejarlo aquí abajo, o con confianza mencionalo por Twitter o en la Fanpage de Facebook, y aprovecho a invitarte para que me sigas en las redes y compartas el contenido del blog, con lo cual me harías un gran favor.

Saludos.

Producto de matrices con Python

Producto de matrices con Python

Hola, y bienvenido a esta 4ta entrada en la serie de operaciones en matrices con Python. En esta ocasión, como podrán leer en el título, se trata sobre como podemos calcular el producto de matrices con python.

Una vez más aprovecho para invitarte a que me sigas en mis redes Twitter o en la Fanpage de Facebook, donde podrás estar informado de nuevas entradas del blog, o si tienes alguna duda o comentario puedes decirmelo sin problema.

Operaciones en matrices con python

Hasta el momento las entradas de esta serie han sido las siguientes:

Creando la clase Base

Suma de matrices

Producto por un escalar

Producto de matrices

Transpuesta (Pendiente)

Como en las entradas anteriores, para realizar el ejemplo utilizaremos la clase base que creamos en la primera entrada de esta serie, así que si aún no la tienes, lee la primera entrada y vuelves 😀

Un poco de teoría

El producto de matrices encuentra su origen en el papel que desempeñan las matrices como funciones lineales. Según la forma en que se define este producto, se puede decir que proviene de la composición de funciones lineales.

Si f y g son aplicaciones lineales tal que f: A -> B y g: B -> C (A, B son los dominios respectivos de f, g y B, C son los imágenes que corresponden a estos dominios) entonces la composición de f y g denotada por el operador o, cumple que f o g: A -> C. Los dominios pueden verse como los posibles valores de entrada de la función y pudiera hallarse representado por un conjunto de con las posibles salidas ofrecidas por esta función y también pudiera ser el conjunto de números reales. Por esta razón el producto de la matriz A(m,n) con la matriz B(n,p) sería una matriz C(m,p) lo cual representa una restricción. Dicho de otra forma, solo es posible multiplicar matrices cuando la cantidad de columnas de la primera es igual a la cantidad de filas de la segunda. En la siguiente imagen podemos observar un ejemplo:

Producto de matrices con pythonLa imagen anterior muestra el resultado de realizar el producto de las matrices A y B, operación que se lleva a cabo entre filas de A y columnas de B, lo que justifica que se requiera que esas cantidades sean iguales. Para calcular una celda de C, sea C(i,j), se toman los valores de la fila i de A y se multiplica cada uno con su correspondiente valor en la columna j de B, luego se suman los valores obtenidos y el resultado es C(i,j). De manera general C(i,j) = A(i, 1) * B(1,j) + A(i,2) * B(2,j) + + A(i,m) * B(m,j).

Código: producto de matrices con python

La implementación de la función con la que llevaremos a cabo esta operación, será una función generadora como las que hemos realizado en anteriores entradas de esta serie:

def product(self, m):
    """ Recibe una matriz como argumento """
    if self.rows is not m.cols:
        raise Exception('Number of rows'
                        'and number of columns'
                        "don't match")
    for i in range(self.rows):
        row = []
        for j in range(m.cols):
            add = 0
            for c in range(self.cols):
                add += self.get_value_of_position(i, c) * \
                       m.get_value_of_position(c, j)
            row.append(add)
        yield row

El código es sencillo, primero se hace una comparación para verificar si el número de filas de la matriz A es diferente al número de columnas de la matriz pasada como argumento B, en caso de que esto se cumpla lanzamos una excepción, caso contrario utilizando bucles recorremos las matrices, multiplicando y sumando los valores tal y como se explico anteriormente y añadiendolos a la nueva matriz C.

Si deseamos verificar el comportamiento de nuestra nueva función podemos realizarlo con el siguiente código:

ma = Matrix(2,3)
# First row
ma.define_elem(0,0,1)
ma.define_elem(0,1,2)
ma.define_elem(0,2,3)
# Second row
ma.define_elem(1,0,7)
ma.define_elem(1,1,8)
ma.define_elem(1,2,9)

mb = Matrix(3,2)
# First col
mb.define_elem(0,0,1)
mb.define_elem(1,0,7)
mb.define_elem(2,0,13)
# Second col
mb.define_elem(0,1,2)
mb.define_elem(1,1,8)
mb.define_elem(2,1,14)

for e in ma.product(mb):
    print(e)

Teniendo la siguiente salida:


[54, 60]
[180, 204]

Que como verás corresponden a los valores de las matrices representadas en la imagen anterior. Hasta este punto nuestra clase debe encontrarse de la siguiente forma (Recuerda que también se encuentra todo el código en el repositorio de Github):

#!/usr/bin/python
# -*- coding: utf-8 -*-
# author: Gabriel Cueto <TheMushr00m - @Mushr00m_Dev>


class Matrix:
    _n = 0
    _m = 0
    _elems = None

    def __init__(self, n, m):
        """Inicializa la matriz con valor 0 en cada posición"""
        self._n = n
        self._m = m
        self._elems = []
        for i in range(self._n):
            self._elems.append([])
            for j in range(self._m):
                self._elems[i].append(0)

    def define_elem(self, i, j, v):
        """ Sobreescribe el valor de una celda """
        self._elems[i][j] = v

    def show_matrix(self):
        """ Imprime los valores almacenados en la matriz """
        for i in range(self._n):
            for j in range(self._m):
                # Imprime de una forma elegante la matriz
                print("| {0} ".format(self.get_value_of_position(i, j)), sep=',', end='')
            print('|\n')

    def get_cols(self):
        """ Devuelve el número de columnas en la matriz """
        return self._m

    def get_rows(self):
        """ Devuelve el número de filas en la matriz """
        return self._n

    def get_value_of_position(self, i, j):
        """ Devuelve el valor en la fila(i), columna(j) de la matriz """
        return self._elems[i][j]

    def add(self, *matrix):
        """ Puede recibir varias matrices como argumentos """
        for i in range(self.rows):
            row = []
            for j in range(self.cols):
                tmp = self.get_value_of_position(i, j)
                for m in matrix:
                    tmp += m.get_value_of_position(i, j)
                row.append(tmp)
            yield row

    def scalar_product(self, x):
        """ x: Número real """
        for i in range(self.rows):
            row = []
            for j in range(self.cols):
                row.append(self.get_value_of_position(i, j)*x)
            yield row

    def product(self, m):
        """ Recibe una matriz como argumento """
        if self.rows is not m.cols:
            raise Exception('Number of rows'
                            'and number of columns'
                            "don't match")
        for i in range(self.rows):
            row = []
            for j in range(m.cols):
                add = 0
                for c in range(self.cols):
                    add += self.get_value_of_position(i, c) * \
                           m.get_value_of_position(c, j)
                row.append(add)
            yield row

    cols = property(fget=get_cols)
    rows = property(fget=get_rows)

Y bien, es todo por esta ocasión, espero la explicación haya sido clara y cualquier comentario no dudes en dejarlo aquí abajo, o con confianza mencionalo por Twitter o en la Fanpage de Facebook, y aprovecho a invitarte para que me sigas en las redes y compartas el contenido del blog, con lo cual me harías un gran favor.

Saludos.

Producto de matrices por un escalar con Python

Producto de matrices por un escalar con Python

Hola, y bienvenido a esta 3er entrada en la serie de operaciones en matrices con Python. En esta ocasión, como podrán leer en el título, se trata sobre como podemos calcular el producto de matrices por un escalar con python.

Una vez más aprovecho para invitarte a que me sigas en mis redes Twitter o en la Fanpage de Facebook, donde podrás estar informado de nuevas entradas del blog, o si tienes alguna duda o comentario puedes decirmelo sin problema.

Operaciones con matrices en python

Hasta el momento las entradas de esta serie han sido las siguientes:

Creando la clase Base

Suma de matrices

Producto por un escalar

Producto de matrices (Pendiente)

Transpuesta (Pendiente)

Por si no tienes aún la clase Base, aquí la tienes:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# author: Gabriel Cueto <TheMushr00m - @Mushr00m_Dev>


class Matrix:
    _n = 0
    _m = 0
    _elems = None

    def __init__(self, n, m):
        """Inicializa la matriz con valor 0 en cada posición"""
        self._n = n
        self._m = m
        self._elems = []
        for i in range(self._n):
            self._elems.append([])
            for j in range(self._m):
                self._elems[i].append(0)

    def define_elem(self, i, j, v):
        """ Sobreescribe el valor de una celda """
        self._elems[i][j] = v

    def show_matrix(self):
        """ Imprime los valores almacenados en la matriz """
        for i in range(self._n):
            for j in range(self._m):
                # Imprime de una forma elegante la matriz
                print("| {0} ".format(self.get_value_of_position(i, j)), sep=',', end='')
            print('|\n')

    def get_cols(self):
        """ Devuelve el número de columnas en la matriz """
        return self._m

    def get_rows(self):
        """ Devuelve el número de filas en la matriz """
        return self._n

    def get_value_of_position(self, i, j):
        return self._elems[i][j]

    def add(self, *matrix):
        """ Puede recibir varias matrices como argumentos """
        for i in range(self.rows):
            row = []
            for j in range(self.cols):
                tmp = self.get_value_of_position(i, j)
                for m in matrix:
                    tmp += m.get_value_of_position(i, j)
                row.append(tmp)
            yield row

    cols = property(fget=get_cols)
    rows = property(fget=get_rows)

Un poco de teoría

Un escalar x es un elemento que por lo general se encuentra en el conjunto de los números reales. El producto de una matriz A de m * n por un escalar x, es una operación binaria que toma por operandos a la matriz A y al escalar x y tiene por resultado una matriz C de m * n, donde para todo i, j indices válidos de C se cumple C(i,j) = x*A(i,j). Por ejemplo:

producto de matrices por un escalar con pythonLlevando la teoría a código

La implementación del método como una función generadora es bastante simple como podemos observar a continuación:

def scalar_product(self, x):
    for i in range(self.rows):
        row = []
        for j in range(self.cols):
            row.append(self.get_value_of_position(i, j)*x)
        yield row

Ahora, si deseamos probar el nuevo método, necesitamos escribir algo como lo siguiente:

m = Matrix(3, 3)
m.define_elem(0, 0, 1)
m.define_elem(0, 1, 2)
m.define_elem(0, 2, 3)

for e in m.scalar_product(3):
    print(e)

Si observamos con atención, estamos definiendo una matriz con los valores de la primera fila, en correspondencia a los de la figura anterior, ahora, al ejecutar el código, los valores de la primera fila de la matriz resultante, deben coincidir con los valores de la matriz C de la figura anterior.


[3, 6, 9]
[0, 0, 0]
[0, 0, 0]

Hasta este momento, el código completo de nuestra clase quedaría de esta forma. Recuerda que todo el código está disponible en el repositorio de Github:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# author: Gabriel Cueto <TheMushr00m - @Mushr00m_Dev>


class Matrix:
    _n = 0
    _m = 0
    _elems = None

    def __init__(self, n, m):
        """Inicializa la matriz con valor 0 en cada posición"""
        self._n = n
        self._m = m
        self._elems = []
        for i in range(self._n):
            self._elems.append([])
            for j in range(self._m):
                self._elems[i].append(0)

    def define_elem(self, i, j, v):
        """ Sobreescribe el valor de una celda """
        self._elems[i][j] = v

    def show_matrix(self):
        """ Imprime los valores almacenados en la matriz """
        for i in range(self._n):
            for j in range(self._m):
                # Imprime de una forma elegante la matriz
                print("| {0} ".format(self.get_value_of_position(i, j)), sep=',', end='')
            print('|\n')

    def get_cols(self):
        """ Devuelve el número de columnas en la matriz """
        return self._m

    def get_rows(self):
        """ Devuelve el número de filas en la matriz """
        return self._n

    def get_value_of_position(self, i, j):
        return self._elems[i][j]

    def add(self, *matrix):
        """ Puede recibir varias matrices como argumentos """
        for i in range(self.rows):
            row = []
            for j in range(self.cols):
                tmp = self.get_value_of_position(i, j)
                for m in matrix:
                    tmp += m.get_value_of_position(i, j)
                row.append(tmp)
            yield row

    def scalar_product(self, x):
        for i in range(self.rows):
            row = []
            for j in range(self.cols):
                row.append(self.get_value_of_position(i, j)*x)
            yield row

    cols = property(fget=get_cols)
    rows = property(fget=get_rows)

Y bien, es todo por esta ocasión, espero la explicación haya sido buena, cualquier comentario no dudes en dejarlo aquí abajo, o con confianza mencionalo por Twitter o en la Fanpage de Facebook, y aprovecho a invitarte para que me sigas en las redes.

 

Suma de matrices con Python

Suma de matrices con python

Hola a todos, bienvenidos a esta entrada, siguiendo la serie sobre operaciones con matrices en python, en esta ocasión veremos como llevar a cabo la suma de matrices con python.

Aprovecho para invitarte a que me sigas en mis redes Twitter o en la Fanpage de Facebook, donde podrás estar informado de nuevas entradas del blog, o si tienes alguna duda o comentario puedes decirmelo sin problema.

Operaciones con matrices en python

Hasta el momento las entradas de esta serie han sido las siguientes:

Creando la clase Base

Suma de matrices

Producto por un escalar

Producto de matrices (Pendiente)

Transpuesta (Pendiente)

Para esta entrada utilizaremos la clase base Matrix que realizamos en la entrada anterior.

Un poco de teoría

La suma de un conjunto de matrices A1, A2, …, An, es una de las operaciones más sencillas que se pueden realizar sobre esta estructura. Llevar a cabo esta suma depende en gran medida de que todas las matrices tengan igual dimensión y compartan el mismo valor para cada dimensión. Con esto, se da por entendido que no será posible sumar una matriz A de 3 x 2 con una matriz B de 4 x 2. Esto se justifica por la manera en que la suma de matrices se realiza.

El resultado de sumar dos matrices A y B es una nueva matriz C, que tiene en cada celda el resultado de sumar las correspondientes celdas de A y B, de modo que la primera celda de C, será C (0,0) = A (0,0) + B (0,0) y de manera general C (i,j) = A (i,j) + B (i,j) para todo par de indices válidos de A y B. Este procedimiento justifica el pre-requisito de que las dimensiones de todas las matrices coincidan. En resumen, la suma de n matrices se obtiene mediante la fórmula, C (i,j) = A1 (i,j) + A2 (i,j) + + An (i,j).

suma de matrices con pythonPuedes comprobar que los valores de las celdas de la primera fila de C coinciden con la suma de los valores de las celdas equivalentes en A y B.

5 = C (0,0) = A (0,0) + B (0,0) = 1 + 4

7 = C (0,1) = A (0,1) + B (0,1) = 2 + 5

9 = C (0,2) = A (0,2) + B (0,2) = 3 + 6

Pasemos al código

Para poder llevar a cabo la suma de matrices con python, el desarrollo del método suma queda de la siguiente manera:

def add(self, *matrix):
    """ Puede recibir varias matrices como argumentos """
    for i in range(self.rows):
        row = []
        for j in range(self.cols):
            tmp = self.get_value_of_position(i, j)
            for m in matrix:
                tmp += m.get_value_of_position(i, j)
            row.append(tmp)
        yield row

Como pueden observar, la función se implementa como un generador que retorna una fila (row) de la matriz C, cada que se alcanza la sentencia yield. Debo hacer mensión al ahorro de memoria que esto representa, ya que no estamos almacenando en memoria la estructura completa de C, lo cual es un beneficio cuando trabajamos sobre matrices de dimensiones considerables, esto se deriva de las bondades que nos brinda el uso de generadores.

Si visualizamos el código completo se encuentra de la siguiente manera:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# author: Gabriel Cueto <TheMushr00m - @Mushr00m_Dev>


class Matrix:
    _n = 0
    _m = 0
    _elems = None

    def __init__(self, n, m):
        """Inicializa la matriz con valor 0 en cada posición"""
        self._n = n
        self._m = m
        self._elems = []
        for i in range(self._n):
            self._elems.append([])
            for j in range(self._m):
                self._elems[i].append(0)

    def define_elem(self, i, j, v):
        """ Sobreescribe el valor de una celda """
        self._elems[i][j] = v

    def show_matrix(self):
        """ Imprime los valores almacenados en la matriz """
        for i in range(self._n):
            for j in range(self._m):
                # Imprime de una forma elegante la matriz
                print("| {0} ".format(self.get_value_of_position(i, j)), sep=',', end='')
            print('|\n')

    def get_cols(self):
        """ Devuelve el número de columnas en la matriz """
        return self._m

    def get_rows(self):
        """ Devuelve el número de filas en la matriz """
        return self._n

    def get_value_of_position(self, i, j):
        return self._elems[i][j]

    def add(self, *matrix):
        """ Puede recibir varias matrices como argumentos """
        for i in range(self.rows):
            row = []
            for j in range(self.cols):
                tmp = self.get_value_of_position(i, j)
                for m in matrix:
                    tmp += m.get_value_of_position(i, j)
                row.append(tmp)
            yield row

    cols = property(fget=get_cols)
    rows = property(fget=get_rows)

Recuerda que todo el código lo puedes encontrar en el repositorio en Github. Ahora, para poder ver en funcionamiento el método que creamos, podemos ejecutar algo como lo siguiente:

m1 = Matrix(3,3)
m1.define_elem(0,0,1)
m1.define_elem(0,1,2)
m1.define_elem(0,2,3)

m2 = Matrix(3,3)
m2.define_elem(0,0,4)
m2.define_elem(0,1,5)
m2.define_elem(0,2,6)

# Recorre el iterador generado
for e in m1.add(m2):
    print(e)

Seguro habrás observado, que la primera fila de m1 y m2 corresponden a los mismos valores mostrados en las matrices de la imagen de ejemplo A y B, la cual ilustraba la operación de suma de matrices. Por lo tanto, la primera fila del iterador resultante corresponde con la primera fila de la matriz C de la imagen. El resultado es el siguiente:


[5, 7, 9]
[0, 0, 0]
[0, 0, 0]

Y bien, es todo por esta ocasión, espero la explicación haya sido buena, cualquier comentario no dudes en dejarlo aquí abajo, o con confianza mencionalo por Twitter o en la Fanpage de Facebook, y aprovecho a invitarte para que me sigas en las redes.

Saludos.

Operaciones en matrices con Python – Creando la clase Base

matriz

Operaciones en matrices con Python

Hola a todos, luego de un tiempo sin escribir aquí les traigo esta entrada, la cual espero los ayude a entender este tema tan recurrente, como es el realizar operaciones en matrices con Python. En esta entrada veremos que es una matriz, y crearemos la clase que nos servirá de base para realizar las operaciones en las pr

Hasta el momento las entradas de esta serie han sido las siguientes:

Creando la clase Base

Suma de matrices

Producto por un escalar

Producto de matrices (Pendiente)

Transpuesta (Pendiente)

¿Qué es una matriz?

Las matrices son arreglos k-dimensionales donde se almacenan valores que pueden ser accedidos por indices. En las matemáticas, se conocen desde el año 200 a.C. y siempre han estado vinculadas al estudio de sistemas de ecuaciones. Toda una rama de esta ciencia está dedicada al estudio de matrices y durante mucho tiempo han sido ampliamente invetigadas.

Probablemente el caso más conocido de matriz se tenga cuando k = 2 (bidimensional), con n filas y m columnas, básicamente lo que se entiende por una tabla. A continuación podemos observar una matriz cuadrada donde n = m = 3.

operaciones en matrices con python

operaciones en matrices con python

Entre las operaciones más comunes en matrices se encuentran la suma, la resta y la multiplicación. Para comenzar el estudio de dichas operaciones, primero crearemos una clase matrix que represente a la estructura de datos y que en su momento servirá como contendor para diferentes funciones aplicadas sobre matrices.

#!/usr/bin/python
# -*- coding: utf-8 -*-
# author: Gabriel Cueto <TheMushr00m - @Mushr00m_Dev>


class Matrix:
    _n = 0
    _m = 0
    _elems = None

    def __init__(self, n, m):
        """Inicializa la matriz con valor 0 en cada posición"""
        self._n = n
        self._m = m
        self._elems = []
        for i in range(self._n):
            self._elems.append([])
            for j in range(self._m):
                self._elems[i].append(0)

    def define_elem(self, i, j, v):
        """ Sobreescribe el valor de una celda """
        self._elems[i][j] = v

    def show_matrix(self):
        """ Imprime los valores almacenados en la matriz """
        for i in range(self._n):
            for j in range(self._m):
                # Imprime de una forma elegante la matriz
                print("| {0} ".format(self.get_value_of_position(i, j)), sep=',', end='')
            print('|\n')

    def get_cols(self):
        """ Devuelve el número de columnas en la matriz """
        return self._m

    def get_rows(self):
        """ Devuelve el número de filas en la matriz """
        return self._n

    def get_value_of_position(self, i, j):
        return self._elems[i][j]

    cols = property(fget=get_cols)
    rows = property(fget=get_rows)

Como podrás observar, la matriz se inicializa con valor 0 en cada celda, y la modificación de estos valores tiene lugar mediante el método define_elem. Ahora es el momento de escribir el código para poder lograr nuestro objetivo, realizar operaciones en matrices. Esta de más decir, que los próximos métodos serán declarados como parte de la clase anterior.

Si deseas probar el código y visualizar como se genera la matriz, corre lo siguiente ya que tengas el código de la clase:

m = Matrix(3, 3)
m.show_matrix()

Espero el código de nuestra clase base haya quedado claro, si tienes alguna pregunta no dudes en hacerla en los comentarios, o mediante twitter.

Nuevamente te hago la invitación de que me sigas en mis redes, Twitter, Facebook, para que estes pendiente de nuevas entradas, charlas, proyectos, compartas contenido, etc.

Te pido una disculpa por si el código no se visualiza correctamente (sin identación), ya me encuentro trabajando en revisar esto, aunque creo que voy a enfocarme en el rediseño que estoy llevando a cabo para poder tenerlo pronto, sin embargo puedes encontrar todo el código en su repositorio de GitHub.

Gracias por leer la entrada, y no te pierdas las próximas donde realizaremos las operaciones con matrices. Saludos.

Estructura de Datos – Pilas en Python

Pila

Estructura de Datos: Pilas en Python

Hola a todos, primero quiero agradecer su apoyo al seguir el blog. Luego de unos días de descanso para poder pensar algunas cosas, aquí traigo un rápido ejemplo, del que me sugirieron escribir vía Twitter. El tema que vamos a revisar en esta ocasión, será el tema perteneciente a la asignatura de Estructuras de Datos: Pilas.

Para esto, los ejemplos que veremos serán llevados a cabo utilizando el lenguaje Python. Esto debido a que el objetivo de esta entrada es que el concepto quede claro para todos, y así pueda ser aplicado en cualquier lenguaje.

Pilas en Python¿Qué es una Pila?

Bien, una pila como estructura de datos, funciona exactamente cómo sucede en el mundo real en un conjunto de objetos superpuestos verticalmente. Imagina que eres estudiante, y estás buscando un objeto en tu mochila. Por más que mueves las cosas no logras encontrar lo que buscas, así que comienzas a sacar tus libros y los apilas, uno sobre otro. Con esto lograste ubicar el objeto que buscabas, paso siguiente, pones los libros de regreso en tu mochila, comenzando por el libro que se encuentra en el tope de la pila, y así hasta llegar al último. (Ya que confío en que eres ordenado).

Libros_apilados: Pilas en PythonSi analizamos un poco el ejemplo, podemos notar ciertas cosas, al agregar un nuevo elemento (un libro) a la pila, pasará a colocarse en el tope, ya que siempre en una pila, el último elemento en ingresar a la pila, será el tope de la misma. Mientras que para eliminar un elemento (guardar un libro) comenzaremos por el último en haber sido introducido a la pila (que se encuentra en el tope).

Esto es debido a que las pilas, son una estructura de datos del tipo LIFO (“Last In, First Out”), lo que nos dice que el último elemento en ingresar a la pila, será el primero en salir de ella. Espero el concepto haya quedado claro, de no ser así, tal vez con el siguiente ejemplo lo puedan comprender mejor.

Las operaciones distintivas de una pila son: pop(), para sacar (remover) el elemento en el tope de la pila y push() para empujar (añadir) un elemento en el tope. Cada vez que se realiza una operación push() la pila aumenta de tamaño y el tope se modifica siendo ahora el elemento añadido. En el caso de la operación pop() disminuye el tamaño de la pila, y el tope ahora será el elemento debajo del elemento a ser removido, generalmente esta operación devuelve el elemento removido. Otra operación que generalmente se implementa en una pila, es peek(), que devuelve el elemento que se encuentra como tope de la pila, sin removerlo.

Creando una pila con Python

class Stack:
elements = []

def __init__(self):
self.elements = []

def get_size(self):
return len(self.elements)

def push(self, x):
self.elements.append(x)

def pop(self):
return self.elements.pop()

def get_peek(self):
return self.elements[-1]

peek = property(fget=get_peek)
size = property(fget=get_size)


s = Stack()
s.push("a")
s.push("b")
s.push("c")
s.push("d")
s.push("e")
s.push("f")
s.push("h")

El código es algo simple, definimos una clase, la cual será nuestra Pila, que contiene un atributo elements, el cual será una lista vacía. Con __init__() establecemos la lista como vacía al crear una nueva instancia. Contiene un método get_size(), que retorna el tamaño de la pila, push() para añadir un elemento al tope de la pila, pop() para remover el elemento en el tope y devolverlo y get_peek(), que nos devuelve el elemento en el tope sin removerlo, en este caso utilizamos la expresión elements[-1], que es la forma de obtener el último elemento de una lista (recuerdan que en Python, una pila no es más que una lista).

Paso seguido creamos una instancia y añadimos algunos valores a nuestra pila, quedando de esta forma:

Pila recién creada: Pilas en PythonComo se observa, h es el elemento que se encuentra en el tope de la pila. Si ejecutamos las operaciones mencionadas anteriormente. get_size() devuelve el valor de 8, con get_peek() obténdremos “h”, y con pop() también obtendremos “h”, la diferencia es que ahora nuestra pila se encuentra de la siguiente forma.

Pila luego de Pop: Pilas en PythonSi observas, ahora el tope de la pila es “g”, y el tamaño actual es de 7.

Este ejemplo podría mejorarse un poco. Se podría añadir una validación para evitar que se intente hacer pop() si la lista se encuentra vacía, te invito a realizarlo a modo de práctica y lo expongas en los comentarios.

La pila es probablemente una de las estructuras de datos más utilizadas en el ámbito de la programación, y de la informática en general me atrevería a decir. Son utilizadas en ocasiones de manera inconsciente, cuando definimos algoritmos recursivos, dado que los lenguajes de programación implementan este mecanismo mediante una pila que almacena los llamados recursivos.

Y bien, es todo por ahora, espero haber sido lo suficientemente claro, y que los conceptos hayan quedado claros. Cualquier comentarios no dudes en escribirme, ya sea a mi cuenta de Twitter, en donde te invito a que me sigas, como en la FanPage de Facebook, la cual te invito a que le des Me gusta para que te mantengas al tanto de anuncios y nuevas entradas que iré publicando.

Saludos.

Técnicas de programación: Patrón memoize

Patrón Memoize

Hola a todos, bienvenidos a una nueva entrada de este blog, y en esta ocasión vengo a escribir sobre un tema que a muchos les será de utilidad. Conoceremos qué es el patrón memoize o de memorización con ayuda de nuestro buen amigo, el lenguaje Python en su versión 3.6.0.

¿Qué es el patrón memoize o de memorización?

El patrón memoize consiste en almacenar las soluciones calculadas previamente, para evitar que sean recalculadas, logrando con esto, reducir el tiempo computacional de nuestros algoritmos.

Este patrón es empleado en diversas técnicas de programación, entre ellas, la programación dinámica, ¿sencillo, no lo creen?

Creo que un ejemplo permitirá que esto quede más claro. Para ilustrar esto, escribiremos un decorador que siga este patrón, por lo tanto, almacenará las soluciones en un diccionario para ser reutilizadas al ser requeridas nuevamente.


#!/usr/bin/env python
# -*- coding: utf-8 -*-


def memoize(f):
memory = {}

def wrapper(n):
if n not in memory:
memory[n] = f(n)
return memory[n]
return wrapper

 

Como podemos observar, el código no debería resultar demasiado complejo. Es la definición de una función decorator. Si miramos detenidamente el código, observamos que la función memoize recibe un argumento f, que se trata de la función que será “decorada”.

Paso siguiente, se define un diccionario, el cual almacenará los valores que se vayan calculando en la función. Tenemos nuestra función de envoltura (wrapper), la cual recibe un argumento n, que se trata del valor que está siendo calculado.

Dentro de nuestra función de envoltura, se verifica que el elemento no se encuentra en nuestro diccionario, si la condición se cumple entonces lo añade al mismo, de lo contrario, simplemente retorna el elemento desde el diccionario. Y por último, devuelve la función de envoltura.

Poniendo en práctica nuestra función memoize

Un problema que aprovecha enormemente este patrón, es el conocido problema de encontrar el n-ésimo número de la sucesión de Fibonacci. Así que, primero vamos a crear nuestra función para lograr tal objetivo.


def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

 

Como podemos apreciar, estamos utilizando la versión recursiva de la función para realizar la operación. Con esto, podemos apreciar de mejor forma las ventajas del patrón memoize. En principio este algoritmo genera un árbol recursivo, el cual se halla plagado de subárboles que corresponden a casos resueltos en diferentes ramas, y por lo tanto, están siendo recalculados.  En la siguiente imagen podemos ver la explosión arbórea cuando n = 6.

qué es el patrón memoizePara evitar esta situación, podemos valernos de una solución de ramificación y poda, utilizando el decorador memoize que creamos previamente, evitando así cálculos innecesarios.


# Utilizando Memoize
@memoize
def fib_memoize(n):
if n < 2:
return n
return fib_memoize(n - 1) + fib_memoize(n - 2)

 

Lo sé, lo sé, seguro están pensando que no hay forma de comprobar que realmente esto funciona. Hagamos algo, modifiquemos un poco el código del decorador para que indique cuando una solución ha sido calculada previamente, con esto podremos apreciar que la memorización se está llevando a cabo.


# Decorador mejorado, para visualización
def memoize_debug(f):
memory = {}

def wrapper(n):
if n not in memory:
memory[n] = f(n)
else:
print("Result was previously calculated: {0}".format(n))
return memory[n]
return wrapper

 

De igual forma, vamos a modificar nuestra función fib_memoize para que utilice nuestro nuevo decorador.


@memoize_debug
def fib_memoize(n):
if n < 2:
return n
return fib_memoize(n - 1) + fib_memoize(n - 2)

 

Si realizamos la prueba de nuestra función, el resultado para n=6 será.

In[1]: fib_memoize(6)

Result was previously calculated: 1
Result was previously calculated: 2
Result was previously calculated: 3
Result was previously calculated: 4

Out[1]: 8

Les dejo el código en el repositorio en Github.

Por esta ocasión es todo, espero los conceptos hayan quedado claros, de lo contrario, no duden en escribir sus dudas en los comentarios. No olviden seguirme en mis redes, ya sea twitter, o en la fanpage en Facebook. Y bien, hasta la próxima entrada.