Python y algunos métodos especiales de clase

Este post es para las personas que están comenzando a utilizar clases en python, es un poco más que lo básico, voy a mostrar algunos ejemplos de métodos especiales de clase como __str__ y __call__. Los ejemplos están probados con python 3.5.

__ str __

Primero creemos una clase Animal

class Animal:
    def __init__(self, nombre):
        self.nombre = nombre

Y un objeto de la clase Animal llamado perro

>>> perro = Animal('bobby')

Para mostrar el contenido del atributo nombre, es necesario llamarlo directamente

>>> print(perro.nombre)
bobby

Si usamos print sobre el objeto veríamos algo como

>>> print(perro)
<__main__.Animal object at 0x1053fab38>

Cambiemos el mensaje que muestra el objeto al usar print, agregando el método __str__ a la clase

class Animal:
    def __init__(self, nombre):
        self.nombre = nombre
    def __str__(self):
        return nombre

Ahora al usar print

>>> perro = Animal('bobby')
>>> print(perro)
bobby

__ call __

Para interactuar con un objeto podemos crear métodos, por ejemplo, uno llamado hablar

class Animal:
    def __init__(self, nombre):
        self.nombre = nombre
    def __str__(self):
        return nombre
    def hablar(self, mensaje):
        return 'Hola, mi nombre es {}, {}'.format(self.nombre, mensaje)

Ahora puedo llamar el método hablar

>>> perro = Animal('bobby')

>>> perro.hablar('mucho gusto!')
'Hola, mi nombre es bobby, mucho gusto!'

Pero si intento llamar directamente al objeto (notar los “()” después del nombre del objeto)

>>> print(perro())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  TypeError: 'Animal' object is not callable
  'Animal' object is not callable

Obtengo ese mensaje, no puedo llamar directamente al objeto de la clase Animal, para esto es necesario agregar el método __call__

class Animal:
    def __init__(self, nombre):
        self.nombre = nombre
        self.llamados = 0
    def __str__(self):
        return nombre
    def hablar(self, mensaje):
        return 'Hola, mi nombre es {}, {}'.format(self.nombre, mensaje)
    def __call__(self):
        self.llamados +=1
        return self.hablar('he sido llamado {} veces.'.format(self.llamados))

Con esto cada vez el que objeto sea llamado retornará un mensaje e incrementará un atributo.

>>> perro = Animal('bobby')

>>> print(perro())
Hola, mi nombre es bobby, he sido llamado 1 veces.

>>> print(perro())
Hola, mi nombre es bobby, he sido llamado 2 veces.

Espero que estos ejemplos ayuden a crear formas más fáciles de interactuar con los objetos de tus clases.


Bittorrent en el navegador con webtorrent

En este post pretendo mostrar un ejemplo simple de como utilizar el protocolo Bittorrent en el navegador utilizando webtorrent. Prefiero omitir la explicación de conceptos como seed o leechers utilizados en Bittorrent, esos detalles se pueden leer el wikipedia.

La base de los siguientes ejemplos son del get started de webtorrent, solo intento mostrar un poco más de detalle en cada paso.

Para utiliza los ejemplos es necesario disponer de nodejs y algunas otras utilidades como browserify y browser-sync.

Seed

El ejemplo será una página la cual al arrastrar un archivo sobre ella comenzara a hacer de seed.

Además de webtorrent, para poder arrastrar archivos sobre la página se necesita drag-drop

npm install webtorrent drag-drop

Para que el seed sirva el contenido creamos el archivo javascript

// browser-seed.js
var dragDrop = require('drag-drop')
var WebTorrent = require('webtorrent')

var client = new WebTorrent()
console.log('webtorrent created')

// When user drops files on the browser, create a new torrent and start seeding it!
dragDrop('body', function (files) {
  client.seed(files, function (torrent) {
    console.log('Client is seeding ' + torrent.infoHash)
    console.log(torrent.magnetURI)
  })
})

En las últimas lineas se envía a consola en hash del torrent y la magnetURI, esta última deberemos usarla en el leecher, es la dirección con la cual el leecher sabe que descargar.

Para utilizar el archivo javascript desde el navegador es necesario instalar browserify

npm install -g browserify

Y ejecutar

browserify browser-seed.js -o js/browser-seed.js

Ahora podemos llamar a js/browser-seed.js desde index.html

<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <style media="screen">
      body {
        height: 600px;
      }
    </style>
  </head>
  <body>
    <script src="js/browser-seed.js" charset="utf-8"></script>
  </body>
</html>

Como servidor del archivo html voy a usar browser-sync

browser-sync start --server --files='*.html,*.js'

Con la página funcionando solo falta arrastrar un archivo sobre ella, para lo cual creé un archivo llamado fecha.txt

date > fecha.txt

Después de arrastrar el archivo, en la consola del navegador se debería ver algo como

webtorrent created
Client is seeding 0d12d731c700a28fb80bb9e90e5baa3829ec7db6
magnet:?xt=urn:btih:0d12d731c700a28fb80bb9e90e5baa3829ec7db6&dn=fecha.txt&tr=wss%3A%2F%2Ftracker.webtorrent.io

Lo cual indica que el seed está arriba. La última linea es la magnetURI a utilizar en el leecher.

Leecher

Para el leecher, el que se encarga de la descarga, en un directorio diferente al seed creamos el archivo javascript

// client.js
var client = new WebTorrent()

var torrentId = 'magnet:?xt=urn:btih:0d12d731c700a28fb80bb9e90e5baa3829ec7db6&dn=fecha.txt&tr=wss%3A%2F%2Ftracker.webtorrent.io'

client.add(torrentId, function (torrent) {

  console.log(torrent)
  // Torrents can contain many files. Let's use the first.
  var file = torrent.files[0]

  file.appendTo('body')
})

Y el respectivo index.html, en este caso no usé webtorrent con browserify, sino lo cargué directamente desde un CDN.

<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <script src="https://cdn.jsdelivr.net/webtorrent/latest/webtorrent.min.js"></script>
  </head>
  <body>

    <script src="client.js" charset="utf-8"></script>
  </body>
</html>

Al abrir este archivo html en el navegador, debería comenzar a hacer la descarga desde el seed y cuando este completo presentar el contenido obtenido en el body, mostrando algo similar a la imagen siguiente

leecher

Con lo descrito en el post es posible, desde el navegador, servir y descargar contenido sobre el protocolo Bittorrent.

Referencias