Código CSS Eficiente: Cuida el uso de IDs

código css eficiente

Hola a todos, esta ocasión también voy a escribir este post un poco corto, pero intentaré que sea lo más explicativo posible al problema. Planeo que se convierta en una serie de varias entradas que serán englobadas bajo el tema “Código CSS Eficiente“.

código css eficiente

Esta primera entrada hablaré sobre los IDs en los elementos HTML, y una situación “curiosa” con la que me encontré hace poco, siendo esta la premisa a mi recomendación de evitar en lo posible utilizarlos.

Continuar leyendo

Trabajando con WebSockets

Programación Python

Hola a todos, debido al trabajo he descuidado el blog estás últimas semanas, así que hoy decidí esforzarme en sacar tiempo y escribir este pequeño ejemplo de un tema qué alguien me preguntó hace unos días mediante Twitter.. En esta entrada llamada “Trabajando con WebSockets”, explicaré de forma breve pero concisa como trabajar con Web Sockets y JS.

A modo de ejemplo, crearemos lo más conocido para este tipo de “features”, un chat. Tal vez eres un dev que ha experimentado en el pasado con Socket.io y buscas saber un poco más sobre lo que hay por detrás, o simplemente estás intentando añadir carácteristicas “real-time” a tu app y no sabes por donde empezar, este post fue hecho pensando en ti. La aplicación final tendrá una apariencia como la siguiente:

Trabajando con WebSocketsLuce bien, ¿cierto? 😀 No sean tan duros, deben recordar que esto fue hecho en unos minutos, solo para poder ejemplificar la teoría, y poder centrarnos en lo realmente importante de este post.

¿Qué es un Web Socket?

Intentando ser simple, WebSocket es un protocolo nuevo para la web que funciona bajo TCP, por el cual, a diferencia de las conexiones HTTP (las que se utilizan normalmente en internet), este es bi-direccional, ¿que significa esto? Como te menciono, hoy por hoy en internet estamos usando una conexión en una sola dirección, solicitamos un recurso al servidor y esperamos la respuesta (o viceversa con ServerEvents, de los cuales planeo escribir pronto). Pero con web sockets el servidor te habla también, te puede llamar y mandar un mensaje en cualquier momento, cool ¿cierto?

Esto nos permite realizar cosas tan “mágicas” en nuestro sitios web, como podamos imaginar, siempre y cuando tengamos en cuenta algunos puntos importantes:

  • La comunicación se inicia con un handshake HTTP, por lo que si la conexión HTTP no se puede establecer, tampoco habrán WebSockets.
  • El soporte debe existir tanto en el cliente como en el servidor. (Tenemos HTML5 en el cliente y Socket.io o WS en el servidor 😀 )
  • A diferencia de los protocolos convensionales de TCP, donde podemos transmitir streams de bytes, en WebSockets solo se pueden transmitir texto/JSON.
  • Al igual que ocurre con HTTP, donde tenemos su versión segura (HTTPS), en los WebSockets contamos con la versión segura WSS, aunque debemos estar en el entendido que no todos los navegadores que soportan los WebSockets, soportarán WSS.

Lo interesante de los WebSockets es que nos dan este comportamiento “real-time”, que nos da la posibilidad de crear aplicaciones complejas de forma sencilla, ¿has querido hacer un juego multijugador? con los WS esto será algo “sencillo”.

Bien, muy interesante todo pero, ¿cómo funciona?

Trabajando con WebSocketsComo mencionaba anteriormente, para iniciar una conexión con el protocolo WS primero el cliente (el browser) le pide al servidor que quiere iniciar esta conexión (handshake: aquí se pasan unos datos secretos), si este proceso es satisfactorio, el servidor responde un “todo en orden, prosigue” y a partir de ahí dejan de utilizar HTTP y pasan a WS.

Además de darnos la posibilidad de una comunicación bi-direccional, también nos ahorramos el “payload”, es decir, esos bytes que se envían en las transmisiones que hacemos por HTTP, que contienen la información para que tanto el cliente, como el servidor sepan que estan recibiendo, qué tipo de conexión utilizamos, etc. Este “payload” es mejor conocido como “cabeceras HTTP”. Esto ya que recordemos que únicamente podemos transmitir texto y nos quedamos “conectados” al servidor hasta que nosotros (los clientes) decidamos que no queremos seguir conectados.

Pequeño ejemplo: Trabajando con WebSockets

Bien, tal vez tanta teoría te abruma, así que aquí te dejo el código del ejemplo para que puedas reproducirlo. y ver un poco de que se trata. Como el código cuenta con comentarios, voy a dejar implicita la explicación del mismo por esta ocasión (es un post express) 🙂

Primeramente y ya teniendo un proyecto creado, instalamos el paquete wsdesde NPM.

yarn add ws

Teniendo el siguiente index.html:


<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, maximum-scale=1.0">
    <title>Simple Chat App - VueJS and Web Sockets</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/lumen/bootstrap.min.css">
    <style>
        .container {margin-top: 40px;}
        #messages { height: auto; min-height: 150px; }
    </style>
</head>
<body>
    <div class="container">
        <div class="col-xs-12 col-md-offset-3 col-md-6">
            <div class="panel panel-primary">
                <div class="panel-heading">Join the conversation!</div>
                <div class="panel-body">
                    <div class="form-group">
                        <label for="messages" class="form-control-label">Messages</label>
                        <div id="messages" class="form-control"></div>
                    </div>
                    <div class="form-inline">
                        <div class="form-group form-group-sm">
                            <label for="message_text" class="form-control-label">Write your message:</label>
                            <input type="text" class="form-control" id="message_text" />
                        </div>
                        <button class="btn btn-primary">Send</button>
                    </div>
                </div>
            </div>
            <p id="username-label"></p>
        </div>
    </div>

    <script src="app.js"></script>
</body>
</html>

Vemos que llama un script, cuyo contenido es el siguiente:


// app.js
// URL dónde se encuentra ejecutando nuestro servidor de WS
const wsUri = "ws://localhost:5001"
// Creamos un nuevo "cliente" de WS
let socket = new WebSocket(wsUri)
// Ya que la aplicación de ejemplo es una sala de chat
// al iniciar nos pide un nombre de usuario para utilizarlo
const name = prompt("What is your username?")
document.getElementById('username-label').innerText = `You are: ${name}`

// Controla la conexión al WS
socket.onopen = event => {
  // Descomentar para ver que recibimos por parte del WS
  //console.log(event)
  socket.send(JSON.stringify({
    type: "name",
    data: name
  }))
  console.log("Socket connected successfully...")
}

let messages = document.getElementById("messages")

// Nos suscribimos a los eventos de transferencia de mensajes
// por parte del servidor, si tuvieramos mensajes de diferentes tipos
// podríamos llevar a cabo una validación
socket.onmessage = event => {
  // Descomentar para ver que recibimos por parte del WS
  //console.log(event)
  const data = JSON.parse(event.data)
  messages.innerHTML += `<span class="text-success">${data.name}:</\span> ${data.data}<br /\>`
}

document.querySelector(".btn").onclick = event => {
  const text = document.getElementById("message_text").value
  socket.send(JSON.stringify({
    type: "message",
    data: text
  }))
  messages.innerHTML += `<span class="text-warning">You:</\span> ${text}<br /\>`
}

Creamos nuestro archivo server.js, que será el encargado de ejecutar el servidor con soporte para WS.


// server.js
// Referenciamos las librerías a utilizar

// Para manejar los WebSockets utilizaremos la librería 'ws'
// que nos facilita bastante el trabajo con estos.
const WS = require("ws").Server
// Puerto a donde escuchará nuestro servidor WS
const port = 5001
// Creamos nuestro servidor de WS
const server = new WS({ port })

// Nos suscribimos al evento de conexión
// el cual es llamado cuando un cliente se conecta
server.on("connection", ws => {
  // El método callback es llamado cuando
  // se conecta un nuevo cliente y en el argumento "ws"
  // vamos a tener un "enlace" a este cliente

  // Podemos enviar un mensaje al cliente
  // de bienvenida apenas se conecte
  // los parámetros son el nombre del mensaje y un json con los datos
  //ws.send('welcome', { greeting: 'Welcome WS client!' })

  // Los WebSockets se comunican en base a mensajes
  // por lo que debemos suscribirnos a cada uno de los
  // posibles mensajes que los clientes puedan enviar
  ws.on("message", message => {
    console.log(`Received: ${message}`)
    message = JSON.parse(message)
    // Ya que el cliente puede enviar mensajes de diferentes tipos
    // Validamos que el mensaje recibido sea del tipo "name"
    if(message.type === "name") {
      // Store the username that send the email
      ws.userName = message.data
      return
    }

    for(let client of server.clients) {
      // Cómo está será una aplicación de chat
      // enviamos el mensaje a todos los clientes
      // evitando enviarlo a "nosotros"
      if(client !== ws)
        client.send(JSON.stringify({
          type: "message",
          name: ws.userName,
          data: message.data
        }))
    }
  })

  // Nos suscribirnos también al evento
  // que se ejcuta cuando un cliente decide terminar la conexión
  ws.on("close", event => {
    console.log("A client was disconnected...")
  })

  // Todos los llamados a console.log son del servidor
  // con própositos de depuración
  console.log("New client connected...")
})

Ahora únicamente debemos ejecutar el servidor

node server.js

Tan sólo abrimos nuestro index.html en cualquier navegador que soporte WS (Chrome, Firefox, etc) y podremos ver nuestra aplicación funcionando.

Al principio expliqué que tanto el cliente como el servidor deben soportar web sockets, tenemos que tener en cuenta que es necesario HTML5 con web sockets, te dejo este enlace a Caniuse.com con el detalle de los navegadores que lo soportan.

Opciones como SocketIO se encargan de que la conexión funcione sin importar el navegador, esto lo hace intentando con otros mecanismos de transporte (XHR Pooling, Flash Sockets, etc.). No va a ser con web sockets pero va a funcionar como si lo fuera.

Creo que es todo por ahora, si gustan saber más al respecto de “ws”, pueden visitar su documentación, que siendo honestos es bastante clara. Recuerda que todo el código fuente puedes encontrarlo en el siguiente repositorio.

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.

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.

Crear un blog con NodeJS y Hexo

crear un blog con nodejs

Crear un blog con NodeJS

Hola a todos, en esta entrada vengo a escribir sobre Hexo, una herramienta de línea de comandos que nos permitirá crear un blog con nodejs, y con esto, crear la estructura de un blog de forma muy sencilla.

crear un blog con nodejs

¿Qué es Hexo?

Bien, como en su propia página de documentación nos adelantan:

Hexo is a fast, simple and powerful blog framework. You write posts in Markdown (or other languages) and Hexo generates static files with a beautiful theme in seconds.

Como podemos apreciar Hexo nos permitirá crear un blog de una forma sencilla y rápida. Nos da la posibilidad de crear los post de nuestro blog utilizando Markdown (lo cual es grandioso) o algún otro lenguajes y generar archivos estáticos y con un hermoso tema en segundos.

Generando nuestro blog

Lo primero que debemos hacer es instalar hexo, para esto debemos tener instalado NodeJS previamente, y ejecutar alguno de los siguientes comandos:


# Utilizando npm

npm install -g hexo-cli

# Utilizando yarn (recomendado)

yarn global add hexo-cli

Con esto ya tendremos el binario de hexo listo para utilizar, ahora lo que debemos hacer es ejecutar el siguiente comando:


hexo init my_blog

Luego nos posicionamos en el directorio que nos ha creado el comando anterior:


cd my_blog

Instalamos las dependencias:


# Utilizando npm

npm install

# Utilizando yarn

yarn install

Si todo se instaló correctamente, simplemente ejecutamos el servidor de hexo:


hexo server

Ahora si navegamos a la url que nos muestra en la salida (http://localhost:4000/), podremos visualizar nuestro blog, que debería verse parecido a lo siguiente:

crear un blog con nodejsSencillo y rápido, ¿no? Cumplen lo que prometen 😀

Creando un post

Tal vez, te estás preguntando, ¿cómo se crean los post?

La respuesta es simple. Únicamente debemos ejecutar el siguiente comando:


hexo new post 'Titulo de tu post'

Donde debemos reemplazar ‘Titulo de tu post’ con el titulo que quieras que tenga tu post. Para realizar esto no es necesario que detengas el servidor de hexo. Y listo, solo debes comenzar a escribir el contenido de tu post en el archivo generado con el titulo que escribiste dentro de la carpeta source.

Y deberías tener un resultado como el siguiente:

crear un blog con nodejsSi desean saber más sobre este grandioso framework siempre pueden darle un vistazo a su buena documentación, que aunque aún tiene mucho por delante, es una buena opción si quieres algo simple y que te permita llevar a cabo el objetivo de un blog: escribir.

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.