Saltar a contenido

Publicaciones

Curso de Python - Imprimir valores y funciones

En esta entrega veremos: * Cómo imprimir valores de múltiples formas * Iteradores * Cómo trabajar con una función básica * Estamentos de return, yield * Asignando variables a la función * Asignando valores por defecto en dichas variables * Closures * Generadores * Función lambda

Sustitución de tipos de datos en sentencias print()

Cuando queremos incluir un valor que proviene de variables, listas, sets, tuplas... se pueden hacer de múltiples formas. Por ejemplo:

variable = "Susana"
print("Hola me llamo:", variable)
Hola me llamo Susana.

Podemos hacerlo de esta forma:

variable = "Susana"
print("Hola me llamo: %s" % variable)
Hola me llamo Susana.

Tenemos que tener en cuenta que de esta manera, hay que definir si el dato que vamos a sustituir es str = %s%, int = %i, float = %f. NOTA: Los valores de tipo complex = %c no tienen sustitución directa en esta forma, por lo que hay que utilizar otro método como en el anterior.

O de esta otra:

variable = Susana
print(f'Hola me llamo {variable}')

También tenemos esta otra:

variable = "Susana"
print("Hola me llamo {}".format(variable))

En fin, hay muchas formas de hacer sustituciones en los str y en otros tipos de datos que puedes consultar en la documentación oficial.

Iteradores

Un iterador es una especie de puntero que permite devolver valores que le especifiquemos.

lista= ["Hola","esto","es","un","ejemplo","de","iterador"]

# Inicializamos el iterador
mi_iterador = iter(lista)

# Se puede imprimir el valor de esta manera, que devuelve la palabra Hola
print(next(mi_iterador))

# Pasamos al siguiente iterador que contiene la palabra "esto", pero no la imprimimos.
next(mi_iterador)

# Imprimimos la palabra "es"
print(next(mi_iterador))

# Imprimir todos los elementos del iterador:
for item in mi_iterador:
  print(item)

Funciones

Las funciones son un conjunto de código organizado y que contienen una serie de instrucciones que pueden reutilizarse. Por ejemplo, dormir es una función que tenemos, hay múltiples variables como el lugar, la intensidad de la luz, si estamos cómodos... pero que al final el resultado es descansar. Lo mismo sucede con las funciones.

Declaración de ejemplo de una función:

def my_funcion():
  # Bloque de código

return

return permite devolver un valor de la función y darla por terminada más que utilizar un print()

def func(a):
  return a

valor=func(12)
print(valor)

yield

En contra posición de return, yield permite seguir aplicando el código que venga más adelante de la función creando una especie de co-rutina o gestión de resultados o ejecución por turnos, como si fuera un corredor de atletismo que le pasa el testigo a otro y su marca de tiempo es el valor de retorno. Hacemos uso de los iteradores para extraer los datos.

def func(a):
  print("Devolvemos el primer resultado: %i" % (a)) 
  yield a
  c = a - 2
  print("Devolvemos el segundo valor: %i" % (c))
  yield c

abc = func(20)

mi_iter = iter(abc)

for item in mi_iter:
    print(item)

Tipos de funciones en Python

En Python, tenemos 2 tipos de funciones, una que creamos nosotros y otras que vienen integradas. Las que nosotros creamos las definimos en nuestra aplicación, script... mientras que las integradas, vienen con la instalación de Python que ya fueron elaboradas y que puedes utilizar como: len(), max(), min(),type(),range()`...

Veamos un ejemplo:

>>> def hola_mundo():
>>>   print("Hola Mundo")
>>> hola_mundo()
Hola Mundo

Podemos pasar todo tipo de valores, por ejemplo, pasaremos una lista como valor y un número entero:

>>> lugares = [ "Toronto", "Tokio", "Nueva Zelanda" ]

>>> def mostrar_lugares(valores):
>>>   for lugar in valores:
>>>     print(lugar)
>>> mostrar_lugares(lugares)
Toronto
Tokio
Nueva Zelanda

Asignar una función a una variable

Podemos asignar una función a una variable y llamarla como función desde la propia variable:

def mensaje(msg):
    print(msg)

segundo=mensaje

# Imprime "Mundo"
segundo("Mundo")

Asignar valores por defecto a los argumentos

Si no le decimos o especificamos un valor cuando llamamos a la función, podemos hacer que tome parámetros por defecto.

def test(variable="Valor que queremos")
  print(variable)

test()

Esto devolverá "Valor que queremos", si especificamos un valor tal que así:

test("Hola mundo")

Devolverá Hola mundo.

Closures

Los closures es un objeto especial que permite obtener información de otras funciones hijas que forman parte de una función padre, permiten dar más seguridad al código ya que todo lo que tenga que ejecutarse se hará dentro de ese ámbito o scope.

Ejemplo de closure

def func(x):
    def func_a():
        print(x)
    return func_a()

Generadores

Son funciones que crean iteradores, estas funciones realmente son objetos en Python. Si hacemos un print sobre la función, veremos <generator object mensaje at 0x7f31a4957250>, es muy importante saberlo. No solo podemos trabajar con return, también con yield.

def mensaje(msg):
    def lector():
        print(msg, "%s" % ("leido por un lector."))
    def escritor():
        print(msg, "%s" % ("escrito por un escritor."))

    yield lector()
    yield escritor()

ejemplo = mensaje("Alguien escribió este mensaje y ha sido")

for item in ejemplo:
    if item is None:
        pass
    else:
        print(item)

Función Lambda

Esta función no tiene nombre también se le conoce como función anónima, sin embargo, no puede contener más de una expresión:

>>> x = lambda a : a + 10
>>> resultado = x(5)
>>> print(resultado)
15

Si lo pasamos a función esto sería así:

>>> def sumar(a,b):
>>>   return a + b
>>> sumar(5,10)
15

En la próxima entrega veremos cómo un usuario introduce datos y el control de excepciones.

Curso de Python - Intérprete, comentarios, identificadores y palabras clave

En esta entrega aprenderás:

  • Algunos consejos básicos para trabajar con el intérprete de Python y crear archivos dónde trabajar con nuestro código
  • Comentar en Python que es lo primero que deberíamos aprender cuando estamos aprendiendo lenguajes de programación
  • Qué son los identificadores y algunas pautas que debemos seguir
  • Qué son las palabras clave y por qué no se usan

¿Cómo usar Python?

Sin duda un elemento imprescindible para poder trabajar con Python son los modos en los qué podemos trabajar con él. Podemos hacerlo de dos formas, mediante intérprete de comandos o elaborando un archivo de Python.

Intérprete de comandos

Si estas en Linux o BSD, puedes ver las múltiples (si hay), versiones instaladas en tu sistema si haces un simple: whereis python verás una salida como esta:

python: /usr/bin/python3.7-config 
/usr/bin/python2.7 
/usr/bin/python3.7m-x86_64-config 
/usr/bin/python /usr/bin/python3.7 
/usr/bin/python3.7m 
/usr/bin/python3.7m-config 
/usr/lib/python2.7 
/usr/lib/python3.7 
/usr/lib64/python2.7 
/usr/lib64/python3.7 
/usr/include/python2.7 
/usr/include/python3.7m

Como podemos ver, en mi sistema tengo instalados una versión de Python 3.7 como una 2.7. Es importante destacar que en el curso SIEMPRE trabajaremos con la rama 3.7 o superior porque la versión 2.7 quedó fuera de soporte desde el 31 de diciembre de 2019 y que prestaré más atención a SO Linux que *BSD.

¿Cómo sé que versión tengo por defecto en el sistema?

Si hacemos un ls /usr/bin |grep python veremos que un enlace simbólico está apuntado a una versión concreta.

lrwxrwxrwx.  1 root root              9 Jan 30 10:18 python -> ./python3
lrwxrwxrwx.  1 root root              9 Oct 21 15:13 python2 -> python2.7
-rwxr-xr-x.  1 root root          16072 Oct 21 15:13 python2.7

También se puede saber desde el gestor de paquetes que utilices a menudo en tu distribución de Linux o sistemas *BSD y/o ejecutando python -V que este te devolverá la versión:

$ python -V
Python 3.7.6

Acceder al intérprete de Python

Basta con que ejecutemos el comando python para empezar a trabajar.

$ python
Python 3.7.6 (default, Jan  8 2020, 19:59:22) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Vemos una información de todo un poco, con qué compilador fue compilado Python, qué versión estamos utilizando...etc

Hay una sintaxis mínima que tenemos que entender del promp cuando usamos el intérprete: >>> se refiere a un código que puede ejecutarse sin identación por ejemplo cuando definimos una variable, una función...

>>> variable = 1

... aquí está mencionando que tenemos que indentar, es decir, añadir los espacios (4) correspondientes para respetar la sintaxis de Python.

>>> def func():
...    variable = 1

'SALIDA': Cuando solo salen estas comillas simples, está informando de un valor o imprimiendo la salida de una función, variable...etc

>>> import sys
>>> sys.version
'3.7.6 (default, Jan  8 2020, 19:59:22) \n[GCC 7.3.0]'
Usarlo como calculadora

Se pueden realizar operaciones aritméticas (ya las veremos más adelante) como suma, resta, división, multiplicación...

>>> 3 + 2
5
¿Cómo salir de la terminal?

Para salir podemos usar la función exit() o pulsar la combinación de teclas Ctrl + C

REPL tu intérprete en línea

REPL.it es una página que utilizaré mucho para dinamizar los ejemplos, se trata de un intérprete en línea que permite crear un entorno pequeño de Python y poder trabajar con él. En casi todos los ejemplos pondré un enlace para aquellas personas que quieran probar el código y no dispongan de intérprete.

Creando un archivo Python

Los archivos de Python tienen la extensión .py, y comienzan siempre declarando la ruta del intérprete, a esto se le conoce como SheBang. Por lo general, se suele utilizar la siguiente cabecera para evitar solapamientos con la versión 2.7. #!/usr/bin/env python3

Python 3 utiliza la codificación UTF-8 por defecto, por lo que si queremos trabajar con otro tipo de codificación tendremos que especificarla justamente debajo del SheBang.

#!/usr/bin/env python3
# -*- coding: latin-1 -*-

A partir de aquí añadimos nuestro código, que iremos aprendiendo a lo largo de las entregas de Python.

También es importante, que se le den permisos de escritura al archivo de Python para poderlo ejecutar desde terminal, si no obtendremos el error de permisos:

$ ./hola.py
bash: ./hola.py: Permission denied
$ chmod +x hola.py
$ ./hola.py
Hello world

Ya que sabemos esto, cuando en los comandos de ejemplo haga referencia a los diferentes tipos de prompt (>>>, ... o ' ') usamos directamente el intérprete y cuando no lo haga, estamos haciendo un archivo en Python.

¡Una vez que sabemos esto, adelante!

Comentarios

Algo muy importante sobre todo en nuestro código es comentar. Comentar nos permite recordar los cambios que hemos hecho en el código o tener al menos un punto de retorno porque cuando llevas muchas horas programando y programando, y revisando... dejas el código unos días, y te costará mucho entender y comprender todo el código de nuevo sin tener una ayuda de por qué decidimos hacer un cambio en la estructura, en el flujo del código...etc

¿Cómo se comenta en Python?

Si quieres comentar una línea, puedes usar:

# Comentamos con la almohadilla

Cuando queremos comentar múltiples líneas, utilizaremos esta sintaxis:

"""
Este es un comentario multilínea.
:D
"""

Ver ejemplo dinámico aquí.

Identificadores

Los identificadores son el nombre que se utilizan para identificar una variable, función, clases o un objeto. Las principales reglas que se deben respetar son las siguientes: * No se utilizan caracteres especiales o numéricos menos ( _ ) que se puede utilizar como un identificador * Las palabras clave no se utilizan * Python discrimina de mayúsculas y minúsculas por lo que no es lo mismo llamar a una función o variable var si está definida como Var. * Indentar es obligatorio, hay que respetar los espacios cuando se crean funciones, clases o métodos.

Variable

Las variables permiten almacenar un valor que le asignes como una cadena, un dato númerico, listas, tuplas... el tipo de valor lo veremos con más profundidad en entregas posteriores, pero hay que saber lo que hace este identificador y para que nos puede servir.

>>> variable = "Hello world"

Funciones

Permiten ejecutar un fragmento de código específico el cuál puede solicitar o no datos para la ejecución de dicho código.

>>> def nombre_funcion(argumentos):
...    # Bloque de código

Los argumentos se separan por comas.

Bucles

Permiten recorrer un conjunto de datos como un set|lista|tupla|collection...etc o ejecutarse desde un rango específico como por ejemplo, de 0 a 100 o de 1 a 5...etc

Bucle for

>>> for x in a:
...    # Bloque de código

Es el más común, pero no nos alarmemos que veremos más sobre los bucles en otras entregas posteriores.

Clases

Las clases son la plantilla de los objetos, Python es un lenguaje de programación orientado a objetos, por lo que es indispensable este identificador.

Propiedades

Son los atributos que tiene una clase, a fin de cuentas es como usar variables en otro contexto de programación.

Métodos

Son las funciones o acciones que tiene una clase.

Módulo

Son fragmentos de códigos externos o integrados en Python que realizan una serie de cosas, se puede conocer en otros lenguajes como librerías.

Pero no nos alarmamemos, todos estos identificadores los veremos más adelante de una forma más profunda.

Palabras clave

Python como otros lenguajes, tienen una serie de palabras que no se deben utilizar bajo ningún concepto para utilizarlos como identificadores. Podemos averiguar cuáles son desde su documentación oficial o importando el módulo keyword y su propiedad .kwlist

>>> import keyword
>>> print(keyword.kwlist)

Nos devolverá esta salida:

['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

NOTA: Ver ejemplo dinámico aquí. Todas estas palabras no se podrán utilizar como variables, nombres de lista, tuplas, diccionarios... porque su uso está reservado para otras finalidades.

También podemos averiguar si la palabra que estamos utilizando puede estar dentro de esta lista con su método .iskeyword().

>>> keyword.iskeyword('pass')

Esto devolverá como resultado True porque es una palabra reservada.

Y con esto nos veremos en la siguiente entrega, espero que os haya gustado, un saludo.

Curso de Python - Introducción a la programación orientada a objetos

En este post aprenderás los principios básicos del POO o también llamada programación orientada a objetos que es uno de los paradigmas de la programación. En los post siguientes iré explicando más sobre lo que podemos hacer con ella además de mostrar algunos ejercicios.

Programación orientada a objetos o POO

Python es un lenguaje como ya mencionamos anteriormente orientado a objetos. Los objetos contienen una serie de métodos y propiedades que lo definen. Si pensamos en un humano, tenemos como propiedades el color de ojos, color de piel, altura... y los métodos serían las acciones como el baile, leer, saltar, correr...

Esta metodología de programación nos permite reusar el código las veces que queramos sin tener que estar escribiendo múltiples líneas con funciones específicas para determinadas partes de nuestro código si lo hicieramos en una programacioń estructurada o procedural.

Principios básicos de POO

En Python tenemos los siguientes principios: * Clase: Tipo de dato que contiene un esquema de métodos y propiedades que se usarán para construir un objeto. * Objeto: Una clase que se ha inicializado, es decir, existe y tiene nombres y apellidos propios en su ejecucioń. * Herencia: Propiedades o métodos que ha recibido una clase hija por parte de una clase padre, esto no quiere decir que vaya a heredar todos los métodos y propiedades, solo aquellos que se le permitan. * Encapsulación: La habilidad de enseñar aquello que solo se pueda mostrar y esconder lo que no nos interesa visibilizar. * Polimorfismo: La capacidad que tiene un objeto para cambiar su rol al mismo tiempo, puede actuar de un rol y en otro rol al mismo tiempo.

¿Qué es una clase?

Una clase es la plantilla que tendrá el objeto que queremos crear, contiene una serie de méotodos y propiedades que los utilizaremos una vez generemos el objeto. Por supuesto, una clase es un tipo de dato dentro de Python.

¿Cómo declaramos una clase?

Para generar una clase seguiremos esta sintaxis:

class NombreClase:
  # Bloque de código

¿Cómo podemos añadirle propiedades y métodos a una clase?

De la siguiente forma:

class Coche:
  propiedad = valor

  def método(self):
    # Bloque de código

Un ejemplo podría ser:

class Coche:
  marca = "Toyota"
  modelo = "Corolla"

  def publicidad(self):
    print("De 0 a 100 en 10 segundos")

Tenemos la clase Coche, con unas propiedades definidas que son la marca y el modelo, además, tenemos un método llamado publicidad que muestra un mensaje. En el siguiente apartado, veremos como trabajar con esta clase pero desde la vista de un objeto.

¿Qué es un objeto?

Un objeto es la materialización de una clase, es decir, cuando lo generamos a partir de unas instrucciones ya empieza a existir en nuestro programa que estemos desarrollando. Por ejemplo, tenemos la clase Casa, evidentemente, las casas que declaremos tiene propiedades y métodos diferentes, por ejemplo, tiene una dirección, un número, una elevación, una función, un espacio diferente. Si creamos 5 casas, hemos creado 5 objetos partiendo de una sola clase que es la clase Casa.

En resumen: * Es la unidad básica de POO * Representa una instancia particular partiendo de una clase * Puede haber más instancias partiendo de una misma clase * Cada objeto puede contener y mantener su información

¿Cómo declaramos un objeto?

La sintaxis es:

NombreObjeto = NombreClase()

En este ejemplo creamos 3 objetos diferentes partiendo de la clase anterior.

NombreObjeto = NombreClase()
NombreObjeto2 = NombreClase()
NombreObjeto3 = NombreClase()

Para acceder a sus propiedades:

NombreObjeto.propiedad1

Para acceder a sus métodos:

NombreObjeto.método1()

Utilizando el ejemplo que hemos creado antes:

class Coche:
  marca = "Toyota"
  modelo = "Corolla"
  publicidad = "De 0 a 100 en 10 segundos"

  def eslogan(self):
    print("Este es un método",self.publicidad)

Sara = Coche()
Ionela = Coche()

Tenemos a dos personas que utilizan el mismo coche Ionela y Sara y lo vemos:

print(Ionela.marca,Ionela.modelo)
Toyota Corolla
Ionela.eslogan()
De 0 a 100 en 10 segundos

print(Sara.marca,Sara.modelo)
Toyota Corolla
Sara.eslogan()
De 0 a 100 en 10 segundos

¿Qué pasa si Sara quiere cambiar de coche?

Sara.marca = "Citröen"
Sara.modelo = "Xsara"
Sara.publicidad = "El confort no es discutible."
print(Sara.marca,Sara.modelo)
Citröen Xsara
Sara.eslogan()
El confort no es discutible.

¿Pero Ionela ha cambiado de coche?

print(Ionela.marca,Ionela.modelo)
Toyota Corolla
Ionela.eslogan()
De 0 a 100 en 10 segundos

¡Ya lo tenemos! Podemos instanciar objetos diferentes partiendo de una misma clase y cambiar sus propiedades sin afectar al resto de objetos. ¿A qué es sencillo? Pero, ¿Qué pasa si queremos cualquier persona pueda tener un coche diferente desde el principio?

Pues a pesar de que se puede hacer creando un método que le asigne el valor a las propiedades, lo más correcto es utilizando el método __init__. Este método constructor (que permite inicializar un objeto) asigna valores a las propiedades del objeto cuando se construye, por ejemplo.

class NombreClase:
  def __init__(self, valor_propiedad1, valor_propiedad2):
    self.propiedad1 = valor_propiedad1
    self.propiedad2 = valor_propiedad2

  def metodo1(self):
    # Bloque de código

Para crear el objeto:

Variable=NombreClase("Valor de ejemplo n1","Valor de ejemplo n2")

Si parece dificultoso de entender, no pasa nada, este es otro ejemplo con comentarios (¡Será por ejemplos!):

# Definimos la clase Coche
class Coche:

  # Definimos el constructor que sustituirá los 
  # valores de las propiedades cuando las definamos al inicializar el objeto.

  def __init__(self,marca,modelo, velocidad):
    self.marca = marca
    self.modelo = modelo
    self.velocidad = velocidad

  # Cuando se llama a este método la velocidad se incrementa +1.
  def acelerar(self):
    self.velocidad += 1
    print(self.velocidad)

  # Cuando se llama a este método la velocidad disminuye en -1.  
  def frenar(self):
   self.velocidad -= 1
   print(self.velocidad)

# Creamos los objetos:

Chami = Coche("Nissan", "Almera", 0)
Jose = Coche("Toyota","Corolla AE92 GTi Twin Cam", 0)

# Imprimimos los valores que tienen las propiedades de cada objeto:

print("El coche de Chami es un:",Chami.marca,Chami.modelo,"y ahora va a",Chami.velocidad,"km/h.")
print("El coche de Jose es un:",Jose.marca,Jose.modelo,"y ahora va a",Jose.velocidad,"km/h.")

# Aumentamos la velocidad a uno de los objetos:
Chami.acelerar()

# Si queremos aumentar más veces la velocidad, podemos usar un bucle
for x in range(0, 100):
  Chami.acelerar()

# Imprimimos la velocidad actual
print("Chami va a una velocidad de %i" % Chami.velocidad)

Este ejemplo si lo ejecutamos dará como resultado:

El coche de Chami es un: Nissan Almaera y ahora va a 0 km/h.
El coche de Jose es un: Toyota Corolla AE92 GTi Twin Cam y ahora va a 0 km/h.

Curso de Python - Introducir datos, control de excepciones, trabajar con archivos

Bienvenidos al nuevo post del curso de Python, al terminar este post ya sabrás como introducir datos por el teclado, cómo gestionar el control de excepciones y mostrar lo que te interesa según el error y sobre todo, trabajar con archivos de texto.

Obtener datos introducidos por el usuario

Tenemos una función que es input() y podemos solicitar al usuario que introduzca un dato y que este se almacene en una variable, esto nos puede ayudar a dinamizar los programas interactuando con los usuarios:

input('Aquí va un mensaje')

NOTA: Los datos introducidos se almacenan como un str, por lo que si quieres almacenar datos tipo float, int tendrás que usar la función de conversión que hemos hablado en el apartado de Tipos de datos en Python. Vamos hacer este ejemplo:

nombre = input("¿Cómo te llamas? ")
edad = int(input("¿Qué edad tienes? "))
print("Me llamo %s y tengo %i años." % (nombre,edad))

Imprimirá un resultado como:

¿Cómo te llamas? Alvaro
¿Qué edad tienes? 26
Me llamo Álvaro y tengo 26 años.

Control de excepciones

¿Qué ocurre si queremos ejecutar un código y depende de una serie de datos que no se han inicializado? No pasa nada, tenemos un mecanismo que controla las excepciones.

try-except

try:
  # Bloque de código que se probará antes de ejecutar
except:
  # Bloque de código que se ejecutará si ocurre algún error.

Por ejemplo:

try:
    print(a)
except:
    print("Hubo un problema.")

Esto nos imprimirá Hubo un problema. pero no nos dice en ningún momento dónde está el error. Evidentemente, el error es que la variable a no contiene ningún tipo de valor. Pero, ¿Qué ocurre si quiero personalizar estos errores y mostrar información según convenga?

No pasa nada, podemos añadir excepciones por error, por ejemplo, si la variable a no existía, podemos crear una excepción que permita decir que no existe porque no está creada de la siguiente forma.

  1. Primero identificamos como se llama el error en Python:
print(A)
Traceback (most recent call last):

  File "/tmp/untitled0.py", line 9, in <module>
    print(a)

NameError: name 'a' is not defined

El nombre del error que tenemos que utilizar es NameError.

  1. Definimos el try-except de la siguiente manera:
try:
  print(a)
except NameError:
  print("La variable no se ha asignao, por favor, revisa el programa.")
except:
  print("Hubo un problema, contacte con el desarrollador de la aplicación.")

¿Se pueden definir múltiples errores y que impiman un mensaje? Sí que se puede, esto además nos permite ahorrar mucho código.

try:
  pass
except(NameError, TypeError, ValueError):
  pass
except:
  pass

También podemos usar un alias e imprimir solo el mensaje de error:

try:
  print(a)
except(NameError, TypeError, ValueError) as EstoEsUnError:
  print(EstoEsUnError)
except:
  pass

¿Para qué nos sirve esto? Para tener un mayor control en la validación e impresión de nuestro código. EstoEsUnError imprimirá: NameError: name 'a' is not defined, con este str podemos iniciar una validación on if-elif-else.

with

with es un método que permite realizar acciones que posteriormente necesitan limpiarse para que no queden restos en memoria, un ejemplo muy común y extendido es cuando se trabaja con archivos.

with open('nombreArchivo.ext', modo') as fichero:
  # Bloque de código

Cuando abrimos el archivo de esta forma, aunque hayan problemas con el archivo, este termina cerrándose y dejando de existir en la memoria. Sin embargo, si trabajamos con el archivo de la siguiente forma, el archivo quedará en la memoria de forma casi indefinida en el tiempo generando datos basura:

fichero = open('nombreArchivo.ext','modo')

Además, de que hay que cerrarlo debidamente:

fichero.close()

En el siguiente punto trabajaremos más con los archivos, no nos alarmemos.

Puedes consultar más información sobre este apartado en este hilo de la documentación.

Trabajando con archivos en Python

¿Qué podemos hacer en Python con los archivos? ¿Podemos trabajar con ellos? La cuestión es que sí, podemos abrir, leer, escribir o crear y eliminar archivos, las operaciones básicas que nos deja hacer un SO si estuvieramos en una shell como bash o zsh.

Abriendo un archivo

La sintaxis que se utiliza es:

fichero = open("ruta del archivo", modo)

La sintaxis correcta,que debe usarse y que utilizaremos en estos ejemplos es:

with open("ruta del archivo", modo) as nombreFichero:
  # Bloque de código
Modo Descripción
r Lectura, es el valor por defecto, abre el archivo para que se pueda leer y da un error si el archivo no existe.
a Abre un archivo para agregarle información al final, si no existe el archivo lo crea.
w Sobreescribe cualquier contendio que haya en el archivo que esté abierto y/o crea el archivo si no existe.
x Crea el archivo, si devuelve error quiere decir que ya existe.

Leer archivo

Creamos este archivo:

$ cd /home/$USER/
$ cat << EOF >> hola.txt
> Hola Mundo
> EOF

Si hacemos un cat hola.txt nos mostrará Hola Mundo. Bien, abrimos el archivo con Python

>>> with open("hola.txt","r") as fichero:
>>>   fichero.read()
Hola Mundo

Este método también permite decirle que nos imprima los n caracteres del principio del texto con .read(4).

Devuelve una línea

Si tenemos un archivo con más líneas, podemos imprimirlas con .readline() en vez de .read(). Sin embargo, si queremos imprimir mas líneas, tenemos que llamar varias veces al método.

>>> fichero.readline()
>>> fichero.readline()

Leer el archivo completo

Con ayuda de un bucle for lo hacemos:

>>> with open("ejemplo.txt", "r") as fichero:
>>> for linea in fichero:
>>>   print(linea)

Creando un archivo nuevo

Si el archivo existe, dará error.

>>> with open("ejemplo.txt", "x") as fichero:

Cuando terminemos de escribir en un archivo, lo cerramos para que no quede en memoria.

fichero.close()

Añadir información al archivo

En esta línea añadimos el siguiente texto.

>>> with open("hola.txt","a") as fichero:
>>> fichero.write("Esta es una línea de ejemplo")

Cuando terminemos de escribir en un archivo, lo cerramos para que no quede en memoria.

fichero.close()

Sobreescribir en el archivo

Sobreescribimos el archivo si lo abrimos con el modo w:

>>> with open("hola.txt","w") as fichero:
>>> fichero.write("Te he sobreescrito el contenido con esta línea")

Cuando terminemos de escribir en un archivo, lo cerramos para que no quede en memoria.

fichero.close()

Eliminar un archivo

Hay que importar un módulo llamado os:

import os

os.remove("hola.txt")

Curso de Python - Introducción y preparación del entorno

Historia

Python es un lenguaje de programación interpretado, de alto nivel y de interés general. Este lenguaje tiene una filosofía de programación que se centra en la legibilidad del código, para ello implementa una serie de declaraciones y estándares que ha de respetarse para que el código funcione como es la indentación. Python es un lenguaje orientado a objetos que facilitan a los desarrolladores escribir un código limpio, eficaz, lógico y pequeño que sirve tanto para pequeños, como medianos y grandes proyectos.

La primera versión de Python se publicó en el año 1991 por Guido van Rossum un programador holandés graduado en matemáticas y ciencias computacionales en la universidad de Amsterdam en 1982. Posteriormente se publicó la versión 2.0 el día 16 de octubre del año 2000 y la versión 3.0 que se liberó el 3 de diciembre de 2008. Actualmente las versiones 1.0 y 2.0 ya no están soportadas, esta última dejó de tener soporte el día 31 de diciembre de 2019.

Notas importantes

Antes de seguir con este artículo es muy importante que sepamos que la versión con la que trabajaremos en las siguientes entragas es Python 3. Python 2.7 no tiene más soporte desde el 31 de diciembre del 2019.

A parte de la versión, conviene saber que tenemos que leer mucho la documentación oficial (inglés) si no queremos perder horas y horas con las clásicas pruebas de Error/Acierto (pruebo cosas al azar y lo que salga salió).

También tenemos muchos recursos en línea como Real Python (inglés) que contienen muchos recursos bastante bien explicados; Full Stack Python (inglés) tiene tambień muchos recursos, ayudas...

Instalación

Python por norma general está instalado por defecto en el 99% de distribuciones de Linux por lo que no te hará falta tener que instalar nada. Sin embargo, hay distribuciones que a pesar de que Python 2. se encuentre obsoleto, la mayor parte de librerías y programas que contienen en su sistema operativo hacen uso de la versión 2.7. que fue la última en mantenerse. Por lo que, quizás necesites realizar unos pasos adicionales para tener Python 3.* instalado en tu Linux.

Algunas distribuciones pueden ser: * CentOS 6/7, hay que hacer uso de Software Collections puedes hacer uso de esta guía que elaboré. * Debian Stable * RHEL 6/7 * Gentoo

En FreeBSD y OpenBSD hay que instalarlo con su respectiva paquería, puedes utilizar estas guía oficial de Python para hacerlo.

En Windows tenemos dos formas de hacerlo, una es utilizando el subsistema de Linux para Windows haciendo uso de un sistema Linux ejecutándose en un Windows, o bien, la forma oficial que es utilizar el instalador oficial elaborado por la comunidad de Python. Para descarganos el instalador hay que dirigirse a la página de descargas pulsando aquí.

Una vez descargado, ejecutamos el instalador y seguimos los pasos como tenemos en las imágenes:

Desmarcamos la opción de instalar para todos y marcamos la opción de añadir a la variable PATH, después "Install Now":

Vemos como se está instalando:

En este paso, deshabilitamos el path lenght limit:

Y desde PowerShell podemos abrir el ejecutable directamente, veremos la versión que estamos utilizando.

IDEs

Hay una gran selección de IDEs bastante buenos que podemos utilizar para aprender, hay tanto de pago, gratuitos, libres y no libres... hay un gran abanico para decidir.

IDLE

Un IDE que viene integrado con Python (incluyendo en la instalación de Windows en la que hemos hablado más arriba). Permite ejecutar código directamente, depurarlo y tiene un pequeño editor dónde realizar nuestras modificaciones.

Spyder

Es un IDE gratuito y libre que contiene multitud de funciones desde depuración, ejecución y redacción de un archivo en Python todo en una misma ventana. Pues ver más info aquí.

Jupyter Notebook

Es un servidor web que mediante estilos, javascripts y html5 genera un IDE web con el que poder interactuar, al igual que todos permite depurar, crear archivos, navegar por los directorios del sistema operativo...etc Más info

Navegando por los directorios del SO desde su interfaz web

Ejecutando comandos de Python en un archivo nuevo

Abriendo una sesión de SO desde Web

Jupyter labs

Basado en Jupyter Notebook y Arquitecture contiene más características, como especificar qué versión de Python lanzar, ejecutar una consola directamente de Python o de SO... Más info.

Anaconda

Anaconda es un software que contiene todo lo necesario para desplegar un entorno de desarrollo de Python tanto en Linux como en Windows. No solo contiene el intérprete, si no que también tienen varios editores que puedes seleccionar desde su panel "Anaconda Navitator". Más info.

PyCharm

PyCharm es un software elaborado por la empresa JetBrains, es bastante conocido porque tiene un potente interfaz de desarrollo que permite hacer carga de espacios de nombres, módulos, referencias... y es muy potente. Aunque es de pago, si eres estudiante y tienes la ISIC puedes obtener una licencia anual gratuita para todos los productos de la empresa como DataGrid, PhpStorm, CLion... y poder hacer tus desarrollos (siempre personales y no comerciales) con él. No obstante, si no eres estudiante, y no quieres comprar la licencia, puedes hacer uso de la versión comunitaria.Más info.

Editores

Si no te gusta utilizar un IDE, siempre puedes hacer uso de editores como vim, Sublime Text, VSCode con sus correspondientes plugins. Algunas de estas personalizaciones las hablaremos en otros posts.

Siguiente entrega

En el próxmo artículo, hablaremos de los principales conceptos básicos como son los identificadores, palabras reservadas, variables, funciones, tipos de datos, módulos... cada apartado que veamos tendrá un ejemplo de uso por lo que será más fácil aprender la lógica de los mismos.

¡Os espero en la siguiente entrega, y espero que os haya gustado este post!

Curso de Python - Tipos de operadores e introducción con módulos

En esta entrega vamos a ver cómo trabajar con los diferentes operadores que tenemos disponibles en Python, ¡que no son pocos! y además lo justo para trabajar con módulos.

Tipos de operadores

En Python tenemos varios tipos de oepradores para hacer comparaciones, cálculos, valorar expresiones lógicas.... * Aritmético * Asignación * Comparación * Lógico * Bit a bit (bitwise) * Identidad * Membresía

Aritméticos

Se utiliza para realizar operaciones matemáticas y se utilizando dos operandos para llevarlas a cabo.

Operador Descripción
+ Se utiliza para realizar operaciones de adicción (suma)
- Operaciones de sustracción (resta)
* Multiplicación
/ División (cociente)
% Módulo (resto de una división)

Adicción

>>> 2 + 3
5

Sustracción

>>> 1 - 2
-1

Multiplicación

>>> 2 * 1
2

División (cociente)

>>> 4 / 2
0

Módulo, (resto de una división)

>>> 4 % 2
2.0

Operadores de asignación

Estos operadores permiten asignar valores, por norma general a variables, pero no olvidemos que podemos involucrar otros tipos de identificadores como pueden ser listas, tuplas...

Operador Descripción
= Asigna un valor
+= Suma un valor al valor actual y guardar el nuevo valor
-= Resta un valor al valor actual y guardar el nuevo valor
*= Multiplica un valor al valor actual y guardar el nuevo valor

Igual (=)

>>> variable = 10
>>> print(variable)
10

Más e igual (+=)

>>> variable = 1
>>> variable += 2
>>> print(variable)
3

Menos e igual (-=)

>>> variable = 1
>>> variable -= 2
>>> print(variable)
-1

Multiplicar e igual (*=)

>>> variable = 2
>>> variable *= 2
>>> print(variable)
4

Operadores de comparación

Estos operadores te permiten realizar una comparación entre dos valores, son muy utilizados en los condicionales o en validaciones como pueden ser bucles...etc. Además, los valores comparados devuelven un True o un False (booleano) si la comparación se cumple o no.

Operador Descripción
< Menor que
> Mayor que
<= Menor o igual que
>= Mayor o igual que
!= Distinto de

Menor que

>>> 1 < 2
True

Mayor que

>>> 1 > 2
False

Menor igual que

>>> 1 <= 2
True

Mayor igual que

>>> 1 >= 2
False

Distinto de

>>> 1 != 2
True

Operadores lógicos

Estos permiten realizar operaciones lógicas y devuelven True o False según el resultado.

Operador Descripción
and Deben cumplirse las condiciones sí o sí
or Debe cumplir al menos una de las condiciones evaluadas
not Devuelve False si el resultado es verdadero

and

>>> 1<2 and 2<3
True
>>> x = 100 < 20 and 2>3
>>>print(x)
False

or

>>> 1<2 and 100>100
True

not

>>> not(2 != 100 and 90<200)
False

Operadores bitwise

Operadores Descripción
& Convierte el primer y segundo nº decimal en nº binarios, compara ambos nº. Cuando se encuentra 1 bit en el primer nº, y en el segundo también, se establece 1. Cuando se encuentra 1 bit en el primer nº y un 0 en el segundo también se establece un 0.
| Convierte el primer y segundo nº decimal en nº binarios, compara ambos nº. Cuando se encuentra 1 bit en el primer nº, y en el segundo también, se establece 1. Cuando se encuentra 1 bit en el primer nº y un 0 en el segundo también se establece un 1.
>> Convierte el primer y segundo nº decimal en nº binarios, compara ambos nº. Cuando se encuentra 1 bit en el segundo nº, y se desplaza el bit en el segundo también, se establece 1. Cuando se encuentra 1 bit en el primer nº y un 0 en el segundo también se establece un 1.
<< Lo anterior pero desde la izquierda
~ Devuelve el complemento del nº menos el nº que obtenemos cambiado cada 1 por un 0 y un 0 por un 1. Es lo mismo que -nº-1

Ampersan (&)

>>> 7 & 2
2

¿Por qué nos devuelve 2? Si nosotros pasamos a binario ambos números:

7 = 0 0 0 0 0 1 1 1
2 = 0 0 0 0 0 0 1 0

Cuando coincida el 1 del primer valor, con el 1 del segundo quedará como 1. Si el primer valor hay un 0 y en el segundo hay 1, se quedará el 0 por encima del 1 quedando tal que así:

0 0 0 0 0 1 1 1
0 0 0 0 0 0 1 0
---------------------
0 0 0 0 0 0 1 0

Si conviertes este número binario a decimal te dará 2.

Tubería o pipe

>>> 190 | 9
191

¿Por qué nos devuelve 191? Si nosotros pasamos a binario ambos números:

190 = 1 0 1 1 1 1 1 0
9 = 0 0 0 0 1 0 0 1

Cuando coincida el 1 del primer valor, con el 1 del segundo quedará como 1. Si el primer valor hay un 0 y en el segundo hay 1, se quedará el 1 por encima del 0 quedando tal que así:

1 0 1 1 1 1 1 0
0 0 0 1 0 0 0 1
---------------------
1 0 1 1 1 1 1 1

Si conviertes este número binario a decimal te dará 191. PENDIENTE DE REVISAR

Desplazamiento hacia la derecha o (shift-to-right)

>>> 10 >> 2
2

¿Por qué nos devuelve 2?

Si pasas el nº 10 a binario:

10 = 0 0 0 0 1 0 1 0

Desplazas 2 posiciones añadiendo dos ceros hacia la derecha, y te quedaría:

0 0 0 0 0 0 1 0

Si conviertes este número binario a decimal te dará 2.

Desplazamiento hacia la izquierda o (shift-to-left)

Este procedimiento es el mismo que el anterior, pero hacia el otro lado.

>>> 10 << 2
40

¿Por qué devuelve 2?

Si pasas a binario el nº 10:

10 = 0 0 0 0 1 0 1 0

Desplazas 2 posiciones añadiendo dos ceros hacia la izquierda, y te quedaría:

0 0 1 0 1 0 0 0

Si conviertes este número binario a decimal te dará 40.

Inversión bitwise

>>> ~100
-101

¿Por qué devuelve -101?

Devuelve el complemento del nº menos el nº que obtenemos cambiado cada 1 por un 0 y un 0 por un 1. Es lo mismo que -nº-1

Operador de identidad

Prueba si dos operandos comparten una identidad. Aquí hay dos tipos de operadores is e is not.

is

Por ejemplo, comparamos si un valor almacenado en x es igual a su valor.

>>> x = 10
>>> x is 10
True

is not

Aquí utilizamos el ejemplo anterior pero a la inversa.

>>> x = 10
>>> x is not 10
False

Operadores de membresía

Estos operadores permiten verificar si hay un str en otro str, lista, diccionario, tupla... (estos 3 últimos los verás más adelante). Se utiliza in para buscar y devolver True si encuentra dicho str, o, not in para verificar que no está.

in

>>> "echemos" in "echemos un bitstazo"
True

not in

>>> ".es" not in "echemos un bitstazo"
True

Módulos

¿Qué son los módulos? Los módulos son fragmentos de código que contienen librerías... que elaboran otros usuarios o que ya vienen integradas con Python. Hay muchos módulos que vienen ya pre-instalados en el sistema como pueden ser os que permite interactuar con el sistema operativo; subprocess que permite ejecutar comandos de shell; json con el que podremos trabajar con archivos o salidas JSON...

¿Cómo cargar este código?

El código de los módulos puede cargarse utilizando la palabra import, como en el siguiente ejemplo:

>>> import json

También podemos cargar parte del código de los módulos como por ejemplo random es un módulo que contiene métodos y propiedades. Podemos cargar solo uno de los métodos que tienes como es .random() y asignarle un alias para trabajar con él como en le siguiente ejemplo:

>>> from random import random as GenerarAleatorio
>>> GenerarAleatorio()
0.9037083824066462

¿Qué pasa si no me sé las propiedades o métodos de un módulo?

No pasa nada, puedes revisar siempre la documentación de Python pulsando aquí.

Espero que os haya gustado esta entrega, para la próxima, trabajaremos con listas, tuplas, sets y diccionarios.

Curso de Python - Tuplas, listas, diccionarios y sets

A continuación vamos a ver algunos de los tipos de datos clave en Python junto con alguno de sus métodos, de los cuáles nos serán útiles para empezar.

Tuplas tuple

Conjunto de valores que no cambian dentro del flujo de la ejecución del programa. Pueden contener como valor todo tipo de dato incluyendo otra tupla.

Más info en la doc oficial de Python

>>> nacionalidad = ( 'Español', 'Turco', 'Italiano')
>>> paises = ( 'España', nacionalidad, 'Turquía', 'Italia' )
>>> print(paises)
('España', ('Español', 'Turco', 'Italiano'), 'Turquía', 'Italia')

Añadir elementos a la tupla:

>>> nacionalidad = ( 'Español', 'Turco', 'Italiano')
>>> paises = ( 'España', nacionalidad, 'Turquía', 'Italia' )
>>> paises += ('Francia', 'Munich')
>>> print(paises)
('España', ('Español', 'Turco', 'Italiano'), 'Turquía', 'Italia', 'Francia', 'Munich')

Duplicar el str o caracter que obtengamos por un nº de veces:

>>> nacionalidad = ( 'Español', 'Turco', 'Italiano')
>>> nacionalidad * 2
('Español', 'Turco', 'Italiano', 'Español', 'Turco', 'Italiano')

Muestra el str que esté ubicada en una posición:

>>> nacionalidad = ( 'Español', 'Turco', 'Italiano')
>>> print(nacionalidad[2])
'Italiano'

Mostrar un conjunto de valores específicos de la tupla haciendo uso de su posición:

>>> paises = ( 'España', nacionalidad, 'Turquía', 'Italia','Francia', 'Munich')
>>> print(paises[3:5])
('Italia', 'Francia')

Listas

Grupo de valores representados dentro de unos [], se pueden cambiar de forma simple y sencilla. Más info en la doc oficial de Python

Declarando una lista:

>>> animales = [ 'gato', 'perro', 'búho' ]

Añadir valores en la última posición de la lista, (similar a la tupla):

>>> animales = [ 'gato', 'perro', 'búho' ]
>>> animales += [ 'lagartija', 'pez' ]
>>> print(animales)
['gato', 'perro', 'búho', 'lagartija', 'pez']

También podemos utilizar .extend():

>>> animales = [ 'gato', 'perro', 'búho' ]
>>> animales.extend([ 'lagartija', 'pez' ])
>>> print(animales)
['gato', 'perro', 'búho', 'lagartija', 'pez']

Por otro lado podemos añadir un valor a partir de una posición específica dentro de la lista .insert()

>>> animales = [ 'gato', 'perro', 'búho' ]
>>> animales.insert(0, 'lagartija')
>>> print(animales)
['lagartija', 'gato', 'perro', 'búho']

Eliminar un valor de la lista:

>>> animales = [ 'gato', 'perro', 'búho' ]
>>> animales.remove('gato')
>>> print(animales)
[ 'perro', 'búho' ]

Multiplicar el nº veces los valores de la lista:

>>> animales = [ 'gato', 'perro', 'búho' ]
>>> animales * 2
['gato', 'perro', 'búho', 'gato', 'perro', 'búho']

Mostrar valores específicos utilizando la posición de dichos valores en la lista:

>>> animales = [ 'gato', 'perro', 'búho' ]
>>> print(animales[0:2])
['gato', 'perro']

Diccionarios

Son un conjunto de valores que se almacenan en modo clave:valor, separados los valores por comas, y todas las claves y sus valores van encerrados en {} como JSON. Las claves no pueden contener tuplas, diccionarios, sets... solo str o int. Más info en la doc oficial de Python

>>> ciudades = { 'Andalucía': 'Sevilla', 'País Vasco': 'Bilbao', 'Baleares':'Palma' }

Accediendo a un valor del diccionario:

>>> ciudades = { 'Andalucía': 'Sevilla', 'País Vasco': 'Bilbao', 'Baleares':'Palma' }
>>> print(ciudades['Andalucía'])
Sevilla

Obtener el nº de posiciones de una lista:

>>> ciudades = { 'Andalucía': 'Sevilla', 'País Vasco': 'Bilbao', 'Baleares':'Palma' }
>>> len(ciudades)
3

Obtener las claves de un diccionario:

>>> ciudades = { 'Andalucía': 'Sevilla', 'País Vasco': 'Bilbao', 'Baleares':'Palma' }
>>> ciudades.keys()
dict_keys(['Andalucía', 'País Vasco', 'Baleares'])

Obtener los valores de un diccionario:

>>> ciudades = { 'Andalucía': 'Sevilla', 'País Vasco': 'Bilbao', 'Baleares':'Palma' }
>>> ciudades.values()
dict_values(['Sevilla', 'Bilbao', 'Palma'])

Obtener un valor de una clave del diccionario:

>>> print(ciudades.get('Baleares'))

Eliminar un valor de una clave del diccionario:

del ciudades['Baleares']

# Esto de volverá None.
print(ciudades.get('Baleares'))

Cambiar el valor de una clave del diccionario:

ciudades['Baleares'] = 'Menorca'

# Esto de volverá Menorca.
print(ciudades.get('Baleares'))

Sets

Son un conjunto sin orden de valores encerrados en {} que se ordenan cuando se imprimen: Más info en la doc oficial de Python

>>> marcas_coche = { 'Opel', 'Citröen', 'Tesla' }
>>> print(marcas_coche)
{'Citröen', 'Opel', 'Tesla'}

Comparar 2 sets y mostrar los valores no duplicados de ambas listas

>>> marcas_coche = { 'Opel', 'Citröen', 'Tesla' }
>>> marcas_moto = { 'Suzuki', 'Citröen', 'Tesla', 'Yamaha' }
>>> marcas_coche | marcas_moto
{'Citröen', 'Opel', 'Suzuki', 'Tesla', 'Yamaha'}

Comparar 2 sets y mostrar los valores duplicados de ambas listas como sin repetirlos.

>>> marcas_coche = { 'Opel', 'Citröen', 'Tesla' }
>>> marcas_moto = { 'Suzuki', 'Citröen', 'Tesla', 'Yamaha' }
>>> marcas_coche & marcas_moto
{'Citröen', 'Tesla'}

Comparar 2 sets y mostrar diferencias:

>>> marcas_coche = { 'Opel', 'Citröen', 'Tesla' }
>>> marcas_moto = { 'Suzuki', 'Citröen', 'Tesla', 'Yamaha' }
>>> marcas_coche - marcas_moto
{'Opel'}
>>> marcas_moto - marcas_coche
{'Suzuki', 'Yamaha'}

Añadir un conjunto de valores a un set y que queden en primer lugar.

marcas_coche.update([["2","3","4"])
print(marcas_coche)
{2, 3, 4, 'Opel', 'Citröen', 'Tesla'}

Añadir un valor al set

marcas_coche.add("Valor")
print(marcas_coche)
{2, 3, 4, 'Opel', 'Citröen', 'Tesla', 'Valor'}

Eliminar un valor del set

marcas_coche.remove("Opel")
print(marcas_coche)
{2, 3, 4, 'Citröen', 'Tesla', 'Valor'}

Imprimir dos sets en uno:

setA = {1,2,3,4,5}
setB = {6,7,8,9,10}
print(setA|setB)
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

¡Ya sabemos trabajar mínimamente con estos tipos de datos en Python! En los próximos cursos veremos como trabajar con el control de flujo, es decir, añadiendo condicionales para encausar las decisiones imperativas de nuestro script o programa, y también como iterar diferentes tipos de valores mediante el uso de los bucles.

Detecta cambios en tus archivos con AIDE

Aide es un sistema avanzado de detección de intrusión que nos permite visualizar cambios en los archivos. Si una persona accede de forma ilegal a nuestro servidor y modifica un archivo que no tiene que tocar, este sistema de intrusión te lo detecta mediante el hash del archivo.

También permite revisar nuevos archivos creados, eliminados o modificados. Al realizar un escaneo a los archivos puede devolver diversos códigos de salida como errores de escritura, argumentos inválidos, funciones incompletas...etc El software no ocupa más que 200kb de espacio, y tiene un gran potencial, algo similar a mlocate.

Instalación en Fedora

sudo dnf install aide

Ejemplos de uso

En el siguiente apartado veremos los usos que podemos darle: * Comprobar versión * Generar la base de datos * Instalar la DB en el directorio estipulado en /etc/aide * Comprobando la integridad sin cambios * Comprobando la integridad con una modificación

Comprobar versión:

Podemos ver todas las opciones con las que se compiló y de dónde obtiene la configuración.

sudo aide --version
Aide 0.16

Compiled with the following options:

WITH_MMAP
WITH_PCRE
WITH_POSIX_ACL
WITH_SELINUX
WITH_XATTR
WITH_E2FSATTRS
WITH_LSTAT64
WITH_READDIR64
WITH_ZLIB
WITH_CURL
WITH_GCRYPT
WITH_AUDIT
CONFIG_FILE = "/etc/aide.conf"

Generar la base de datos

Depende del volumen de información que tengamos puede que tarde más o menos, hay que tener un poco de cuidado si estamos trabajando con elementos críticos en el sistema y tengamos un sistema no muy potente.

sudo aide --init
AIDE initialized database at /var/lib/aide/aide.db.new.gz

Number of entries:  11284

---------------------------------------------------
The attributes of the (uncompressed) database(s):
---------------------------------------------------

/var/lib/aide/aide.db.new.gz
  MD5      : dwWdkE+qrFulxJf6iEWJTQ==
  SHA1     : aK4Ao0mbmSmtCueAhyJnoJ4mdwI=
  RMD160   : TDxnNq5kYr1fmXXi8lAgCdsnfeA=
  TIGER    : nqgmBwvdbU4BrDrBS0pFdn9MIYPwd2q5
  SHA256   : QT3fev2WCQ+rDzvPMFU8ZgRgEXAd1pzd
             WLf95un9zeg=
  SHA512   : wCjWTKbQuKfNN/Y2Jytuq71waZrm24sr
             aQMShVvuYDS2DBRiT0G0WP146SuAkFV6
             lGqBitUYo+AgqvEPLuNXAQ==


End timestamp: 2019-01-16 18:56:44 +0000 (run time: 0m 7s)

Moviendo la base de datos

Este proceso es esencial a menos que modifiquemos la línea de dónde leerá la base de datos en el fichero de configuración o bien se lo indiquemos mediante parámetro.

sudo mv mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz

Comprobar la integridad de los archivos

Permite visualizar si hay o no cambios en la integridad de los archivos.

sudo aide --check
AIDE found NO differences between database and filesystem. Looks okay!!

Number of entries:  11284

---------------------------------------------------
The attributes of the (uncompressed) database(s):
---------------------------------------------------

/var/lib/aide/aide.db.gz
  MD5      : rK2m9AmpajAl1ft5hBUMNQ==
  SHA1     : 0gB7cMLYLFHjRs52/7EBp3+NeS4=
  RMD160   : AcDvvoKGwNGBJe4xN+GpQVjFF2k=
  TIGER    : VOjFoPFMk6Q6zuRZcPNkaHPPYOT5yG7F
  SHA256   : xCptxDZF+uw36xUP1F0pRgc+iQtAtCbO
             aJqaD2EzhHs=
  SHA512   : SEoScwAVxVPvCfC1ZSVLR+iTP2H/ZV1d
             hi+FZm1MzVQhrsL5yqTOPxLuitdzYnn6
             aZpV9FSangKVytif0MM2vQ==


End timestamp: 2019-01-16 19:02:40 +0000 (run time: 0m 5s)

Como puedes ver, no se observan cambios.

Prueba modificando uno de ellos

Si toqueteamos el fichero de /etc/krb5.conf que pertenece a una cosa rara llamada Kerberos, te devolverá una salida como esta indicando que ha habido un cambio en el archivo de configuración.

AIDE found differences between database and filesystem!!

Summary:
  Total number of entries:  11284
  Added entries:    0
  Removed entries:    0
  Changed entries:    1

---------------------------------------------------
Changed entries:
---------------------------------------------------

f   ...   i  . . : /etc/krb5.conf

---------------------------------------------------
Detailed information about changes:
---------------------------------------------------

File: /etc/krb5.conf
  Inode    : 12452018                         | 12717139


---------------------------------------------------
The attributes of the (uncompressed) database(s):
---------------------------------------------------

/var/lib/aide/aide.db.gz
  MD5      : rK2m9AmpajAl1ft5hBUMNQ==
  SHA1     : 0gB7cMLYLFHjRs52/7EBp3+NeS4=
  RMD160   : AcDvvoKGwNGBJe4xN+GpQVjFF2k=
  TIGER    : VOjFoPFMk6Q6zuRZcPNkaHPPYOT5yG7F
  SHA256   : xCptxDZF+uw36xUP1F0pRgc+iQtAtCbO
             aJqaD2EzhHs=
  SHA512   : SEoScwAVxVPvCfC1ZSVLR+iTP2H/ZV1d
             hi+FZm1MzVQhrsL5yqTOPxLuitdzYnn6
             aZpV9FSangKVytif0MM2vQ==


End timestamp: 2019-01-16 19:05:15 +0000 (run time: 0m 2s)

Como vemos, no es un software muy "moderno", pero cumple su función.

¡Espero que le saquéis utilidad!

Distrochooser, descarga fácilmente distros de Linux desde CLI

Distrochooser es un pequeño script que he elaborado para descargar cualquier distribución popular pulsando un par de teclas evitando ir a los mirrors o páginas oficiales para descargarlos. Se puede obtener desde GitHub

git clone https://github.com/sincorchetes/distrochooser
chmod +x distrochooser/run.sh
./distrochooser/run.sh

Este script crea un directorio iso en la ruta de trabajo actual dónde descargar la imagen, para su descarga hace uso del comando wget utilizando el modificador -c que permite renaudar la descarga en caso de fallo, por lo que deberemos tenerlo instalado en nuestro sistema.

En la próxima versión soportará verificación de firma, más distribuciones. Espero que os guste.

Un saludo.

Entornos de escritorio y gestores de ventanas

Hoy en día es indispensable hacer uso de un servidor gráfico que nos permita visualizar páginas Web, escuchar música y ver películas o series entre otras cosas. Eso de estar con terminales y reproducción de contenido multimedia en formato ASCII como que no nos va mucho. Queremos visualizar el contenido cuánto más nítido y con calidad, se vive mejor.

Sin embargo, hasta hace relativamente poco, solo disponíamos de terminales de texto y programas elaborados en una interfaz MS-DOS como suelen comentar las personas más veteranas (ya que por mi edad, no pude vivirlo ni sentirlo). La historia fue que hasta que Xerox PARC (el centro de investigación del futuro para Xerox Corp.), no sacó su primer Xerox Alto en 1973, no se utilizaba ningún tipo de interfaz gráfica y mucho menos, existía el concepto del cursor del ratón. Posteriormente, hubo una guerra entre Apple y Microsoft que se disputaron a ver quién copiaba mejor la interfaz gráfica y el ratón inventado de Xerox.

Entornos de escritorio

Se denominan entornos de escritorio a un conjunto de software que contiene interfaz gráfica y que permita realizar múltiples tareas de una forma sencilla, fácil y productiva debido a su interacción amigable de cara al usuario.

Por ejemplo, ver los eventos de nuestro calendario sin tener que hacer uso de comandos o terminales de texto; visualizar el correo electrónico solo haciendo dos clics en el programa...

Haremos un breve repaso de los entornos de escritorio que existen en Linux y en BSD que podemos instalar, y en qué se diferencian principalmente.

CDE

Common Desktop Environment, fue un entorno gráfico para UNIX que fue desarrollado por empresas de gran hincampié tecnológico como HP, IBM, Novell y Sun (comprada por Oracle) viendo su primera versión en junio de 1993 de la mano de HP, IBM, SunSoft y USL(Unix System Laboratories). Estos elaboraron un proyecto en común repartiéndose las tareas para llevar a cabo diferentes objetivos hasta llegar a su primera versión del escritorio. Posteriormente, se fueron involucrando más empresas en su desarrollo. No obstante, estuvo unos años en el mercado hasta que GNOME y KDE les arrebató su posicionamiento.

GNOME

GNOME (GNU Network Object Model Environment) traducido al español como Entorno de Modelo de Objeto de Red GNU, un nombre no tan amigable como sus siglas fue desarrollado por Miguel de Icaza y Federico Mena el 15 de agosto de 1997 como alternativa a KDE (ahora Plasma) para sistemas operativos UNIX-like tipo BSD, Linux, o UNIX como esSolaris (antes SunOS).

Una de las características de GNOME es que hace uso de GTK+. Es un conjunto de bibliotecas multiplataforma para desarrollar interfaces gráficas para el usuario. Básicamente, define entre otras cosas el diseño de la ventana, los botones, introduce eventos, disparadores... como Qt en Plasma o Motif en CDE pero cada cual tiene sus diferencias.

Por otro lado, pretende ser un entorno de escritorio de fácil uso con poca personalización (sobre todo en la última versión 3.x) con el que se pueda trabajar desde el minuto 0.

Instalación

Desde los repositorios oficiales de cada distribución o sistema, hay distribuciones que requieren de pasos adicionales e incluso, configuraciones que no permitan la instalación de GNOME con el gestor de servicios systemd como es el caso de Gentoo, o también contemplan la instalación más básica de GNOME que sucede tanto en Gentoo como en Archlinux. Por ende, os recomendamos la documentación.: * Fedora: sudo dnf install @gnome-desktop * CentOS: sudo yum install @gnome-desktop * Archlinux: sudo pacman -S gnome gnome-extra * Gentoo (systemd): USE="-qt4 -qt5 -kde X gtk gnome systemd" sudo emerge --ask gnome-base/gnome" * Ubuntu: sudo apt-get install ubuntu-gnome-desktop * Debian: Se utiliza una herramienta llamada taskel(8) con interfaz ncurses que permite la instalación de un entorno. sudo apt-get install tasksel && sudo tasksel * openSUSE Leap 42.3: sudo zypper -n in patterns-openSUSE-gnome * FreeBSD: sudo pkg install gnome3

KDE (aka Plasma)

KDE fue el primer entorno de escritorio para sistemas UNIX-like que nació en octubre de 1996 de la mano de un programador alemán llamado Matthias Ettrich que buscaba básicamente una interfaz gráfica que unificáse todos los sistemas UNIX imitando el entorno de escritorio CDE.

Plasma se caracteriza a parte de hacer uso de la suite Qt de bibliotecas gráficas, de hacer un entorno muy completo y muy personalizado, en el se puede configurar todo lo que un usuario jamás pudo haber imaginado que podía llegar a configurar en un entorno, y sobre todo, haciendo uso de sus propias herramientas para hacerlo sin utilizar software a terceros tipo GNOME TweakTool, o teniendo conocimientos de JavaScript o CSS para modificar estilos y comportamientos como hacen las últimas versiones de GNOME.

Hoy en día, el software de Plasma es tan portable, que se puede hasta ejecutar en Windows y en dispositivos móviles.

Instalación

Desde los repositorios oficiales de cada distribución o sistema, hay distribuciones que requieren de pasos adicionales e incluso, configuraciones que no permitan la instalación de GNOME con el gestor de servicios systemd como es el caso de Gentoo, o también contemplan la instalación más básica de Plasma (KDE) que sucede tanto en Gentoo como en Archlinux. Por ende, os recomendamos la documentación: * Fedora: sudo dnf install @kde-desktop * CentOS: sudo yum install @kde-desktop * Archlinux: sudo pacman -S plasma * Gentoo (systemd): sudo emerge --ask kde-plasma/plasma-meta" * Ubuntu: sudo apt-get install kde-plasma-desktop * Debian: Se utiliza una herramienta llamada taskel(8) con interfaz ncurses que permite la instalación de un entorno. sudo apt-get install tasksel && sudo tasksel * openSUSE Leap 42.3: sudo zypper -n in patterns-openSUSE-kde * FreeBSD: sudo pkg install gnome3

MATE

MATE es un fork de GNOME que salió el 19 de agosto de 2011 como muestra del descontento de la nueva versión de GNOME 3 debido a que reducía muchísimo la personalización del entorno de escritorio, consumía mucho más, y tenía otro tipo de funcionalidades y características no muy transigentes. Este proyecto fue desarrollado por un desarrollador argenito de Archlinux llamado Germán Perugorría conocido en la comunidad del software libre como Perberos para continuar el desarrollo de este entorno de escritorio. Que por cierto, es el que nosotros utilizamos. El nombre proviene de la hierba Mate muy común en Argentina para tomar.

Este entorno liberó su última versión el 7 de febrero de 2018, con la satisfacción de haberse portado plenamente a la nueva suite de bibliotecas de GTK+ 3 y añadiendo más funcionalidades que no tenía sobre todo en Caja que en GNOME 3 se llama Nautilus.

Instalación

  • Fedora: sudo dnf install @mate-desktop
  • CentOS: sudo yum install epel-release && sudo yum install @mate-desktop
  • Archlinux: sudo pacman -S mate-desktop
  • Gentoo (systemd): sudo emerge --ask mate-base/mate"
  • Ubuntu: sudo apt-get install mate-desktop
  • Debian: Se utiliza una herramienta llamada taskel(8) con interfaz ncurses que permite la instalación de un entorno. sudo apt-get install tasksel && sudo tasksel
  • openSUSE Leap 42.3: sudo zypper -n in patterns-openSUSE-kde
  • FreeBSD: sudo pkg install gnome3

XFCE

XFCE se caracteriza por ser un entorno de escritorio muy liviano y ligero ya que ese eran sus dos objetivos cuando se desarrolló. La primera versión se liberó en 1996 de la mano de Olivier Fourdan. Este utiliza las bibliotecas de GTK+ para el desarrollo de sus programas gráficos.

Actualmente está comenzando a portar su software a GTK+ 3 llegando un poco tarde, ya que actualmente ya se está desarrollando GTK+ 4.

Instalación

Desde los repositorios oficiales de cada distribución o sistema, hay distribuciones que requieren de pasos adicionales e incluso, configuraciones que no permitan la instalación de XFCE con el gestor de servicios systemd como es el caso de Gentoo, o también contemplan la instalación más básica de XFCE que sucede tanto en Gentoo como en Archlinux. Por ende, os recomendamos la documentación: * Fedora: sudo dnf install @xfce-desktop * CentOS (require tener activado epel): sudo yum install epel-release && sudo yum install @xfce-desktop * Archlinux: sudo pacman -S plasma * Gentoo (systemd): sudo emerge --ask " * Ubuntu: sudo apt-get install xfce-desktop * Debian: Se utiliza una herramienta llamada taskel(8) con interfaz ncurses que permite la instalación de un entorno. sudo apt-get install tasksel && sudo tasksel * openSUSE: Hay que habilitarlo desde Factory * FreeBSD: sudo pkg install xfce

LXDE

Este fue un entorno de escritorio también como el anterior, cuyo objetivo era proveer a un PC de una suite de herramientas gráficas que permitieran trabajar consumiendo lo más mínimo de un ordenador. Su primera versión fue liberada en 2006 por Hong Jen Yee. Actualmente tiene su desarrollo parado, ya que se sustituyó por LXQt.

LXQt

Es la continuación del proyecto LXDE como entorno de escritorio. El anterior hacia uso de librerías GTK+, LXQT hace uso de Qt ya que al parecer al creador de LXDE no le terminó de convencer GTK+. Su objetivo y finalidad son el mismo que en LXDE.

Instalación

Desde los repositorios oficiales de cada distribución o sistema, hay distribuciones que requieren de pasos adicionales e incluso, configuraciones que no permitan la instalación de LXQt con el gestor de servicios systemd como es el caso de Gentoo, o también contemplan la instalación más básica de LXQt que sucede tanto en Gentoo como en Archlinux. Por ende, os recomendamos la documentación: * Fedora: sudo dnf install @lxqt-desktop * CentOS (require tener activado epel): sudo yum install epel-release && sudo yum install lxqt-* * Archlinux: sudo pacman -S lxqt * Gentoo (Requiere pasos adicionales): sudo emerge --ask lxqt-meta * Ubuntu: No disponible * Debian: Se utiliza una herramienta llamada taskel(8) con interfaz ncurses que permite la instalación de un entorno. sudo apt-get install tasksel && sudo tasksel * openSUSE Leap 42.3: sudo zypper in -y pattern lxqt * FreeBSD (Requiere pasos adicionales): sudo pkg install lxqt

Razor-Qt

Fue un entorno de escritorio desarrollado con las bibiliotecas Qt en 2010, no obstante, el equipo de Razor-Qt empezó a colaborar con el creador de LXDE originando el entorno anteriori dando origen a la primera versión en julio del 2014.

Sugar Desktop

Sugar es un entorno de escritorio que nació con el objetivo de crear una interfaz muy intuitiva para aquell@s niñ@s que no podían acceder a la tecnología puntera de países del primer mundo. Creada por Sugar Labs en mayo del 2016, como entorno para el proyecto OLPC (One Laptop Per Child) un proyecto en el que se le permite a los niños mediante un portátil de bajo costo enseñarles a incorporarse a la tecnología sin necesidad de tener grandes recursos pudo crecer y seguir manteníendose como una alternativa educativa también para l@s más peques de la casa.

Instalación

Desde los repositorios oficiales de cada distribución o sistema, hay distribuciones que requieren de pasos adicionales e incluso, configuraciones que no permitan la instalación de Sugar con el gestor de servicios systemd como es el caso de Gentoo, o también contemplan la instalación más básica de Sugar que sucede tanto en Gentoo como en Archlinux. Por ende, os recomendamos la documentación: * Fedora: sudo dnf install @sugar-desktop * CentOS: No disponible, al menos de manera oficial. * Archlinux: sudo pacman -S sugar * Gentoo: No disponible de manera oficial. * Ubuntu: sudo apt-get install sucrose * Debian: sudo apt-get install sucrose * openSUSE Leap 42.3: zypper ar http://download.opensuse.org/repositories/X11:/Sugar/openSUSE_13.1/ X11:Sugar && zypper refresh && zypper in sugar sugar-activities * FreeBSD: No disponible de forma oficial ni extra oficial.

Gestores de ventanas

Los gestores de ventanas es un conjunto mínimo de software que nos permite establecer una sesión gráfica con la que poder interactuar con elementos gráficos. Su consumo es muy pequeño al igual que sus prestaciones, pero, se puede llegar a ser muy productivo con ellos si se saben configurar y utilizar.

TWM

Tab Window Manager, es el gestor de ventanas más común en todo Linux. Fue desarrollado por Tom LaStrange desde 1987, el nombre original estaba basado en la siglas de su nombre Tom's Window Manager, pero el X Consortium lo adoptó y lo renombró en 1989. En TWM se pueden apilar las ventanas, las cuáles contienen título, e iconos para interactuar. Este gestor de ventana suele utilizarse con programas como un reloj analógico xclock(1), y también un emulador de terminal llamado xterm(1) entre otros.

Instalación

  • Fedora: sudo dnf install xorg-x11-twm
  • CentOS: No disponible, al menos de manera oficial.
  • Archlinux: sudo pacman -S xorg-twm
  • Gentoo: No disponible de manera oficial.
  • Ubuntu: sudo apt-get install twm
  • Debian: sudo apt-get install twm
  • openSUSE Leap 42.3:sudo zypper in twm
  • FreeBSD: pkg install twm

i3wm

i3wm o también conocido como i3, es un gestor de ventanas que no se superposiciona, simplemente se adapta una ventana con la otra sin superponerse. i3 nos permite gestionar ventanas en modo stack es decir, apiladas, o bien en modo de pestañas tab entre más características. En suma, soporta modo multi-pantalla, está reescrita desde 0 estando todo su código licenciado bajo términos BSD. Hay que destacar que tiene soporte UTF-8 y es muy fácil de configurar. Su primera versión fue escrita en C por Michael Stapelberg el 15 de marzo del 2009.

Instalación

  • Fedora: sudo dnf install i3
  • CentOS: sudo yum install epel-release && sudo yum install i3
  • Archlinux: sudo pacman -S i3-wm
  • Gentoo: sudo emerge --ask x11-wm/i3
  • Ubuntu: sudo apt-get install i3
  • Debian: sudo apt-get install i3
  • openSUSE Leap 42.3:sudo zypper in twm
  • FreeBSD: sudo pkg install x11-wm/i3

Fluxbox

Fluxbox es un gestor de ventanas creado por Henrik Kinnunen el 12 de septiembre de 2001. Es un wm muy sencillo y fácil de usar, bastante ligero en cuanto consumo y rendimiento. Está basado en un gestor de ventanas llamado Blackbox ya desmantenido. La última versión liberada es la 1.3.7 publicada el 8 de febrero de 2015.

Instalación

  • Fedora: sudo dnf install fluxbox
  • CentOS: sudo yum install epel-release && sudo yum install fluxbox
  • Archlinux: sudo pacman -S fluxbox
  • Gentoo: sudo emerge --ask x11-wm/fluxbox
  • Ubuntu: sudo apt-get install fluxbox
  • Debian: sudo apt-get install fluxbox
  • openSUSE Leap 42.3:sudo zypper in fluxbox
  • FreeBSD: sudo pkg install x11-wm/fluxbox

Openbox

Openbox es otro gestor de ventanas creado por Dana Jansens y Mikael Magnusson el 18 de septiembre de 2002. Este también derivó de sus inicios de Blackbox, sin embargo, ha sido reescrito totalmente desde la versión 3.0. Este gestor sacrifica entre otras cosas algunas funciones típicas como la barra de menú, lista de apps en ejecución o bordes redondeados en las ventanas. No obstante, posee herramientas de configuración del entorno bastane útiles para cambiar el fondo de pantalla, tema del gestor...etc, no obstante, el gestor de ventanas lleva sin desarrollarse desde el 1 de julio del 2015.

Instalación

  • Fedora: sudo dnf install openbox
  • CentOS: sudo yum install epel-release && sudo yum install openbox
  • Archlinux: sudo pacman -S openbox
  • Gentoo: sudo emerge --ask x11-wm/fluxbox
  • Ubuntu: sudo apt-get install openbox
  • Debian: sudo apt-get install openbox
  • openSUSE Leap 42.3:sudo zypper in openbox
  • FreeBSD: sudo pkg install x11-wm/openbox

Enlightenment

Es un gestor de ventanas con una gran cantidad de applets, módulos y aplicaciones que lo intentan convertir en un entorno de escritorio completo, la primera versión liberada fue en 1997 por Rasterman (Carsten Haitzler), mientras que la última versión liberada fue el 15 de marzo de 2018. Enlightenment lleva un desarrollo lento y denso que hace que el entorno sea un poco menos novedoso y no todos sus módulos y applets están bien recibidos según que distribuciones. Suelen faltar muchos de ellos, y algunas veces las compilaciones de los mismos no suelen llevar a resultados favorables. No obstante es una buena alternativa para aquellas personas que busquen un estado intermedio entre gestor y entorno, y resulta muy liviano y con una imagen un tanto futurista.

Instalación

  • Fedora: sudo dnf install enlightenment
  • CentOS: No disponible de forma oficial.
  • Archlinux: sudo pacman -S enlightenment
  • Gentoo: sudo emerge --ask enlightenment:0
  • Ubuntu: sudo apt-get install e17
  • Debian: sudo apt-get install e17
  • openSUSE Leap 42.3:sudo zypper in enlightenment
  • FreeBSD: sudo pkg install x11-wm/enlightenment

Awesome

Otro gestor de ventanas elaborado en C y en Lua, también es parecido a i3 en el que no es necesario hacer uso de ningún tipo de ratón y permite acoplar fácilmente las ventanas entre sí. La primera versión fue liberada el 18 de septiembre del 2007 por Julien Danjou, siendo un fork de dwm. En algunas distribuciones se encuentra disponible, pero desde el 25 de septiembre del 2016 se encuentra desmantenido.

wmii

Windows Manager Improved 2, es un gestor de ventanas que soporta el manejo de ventanas con ratón o teclado elaborado por Anselm R. Garbe y Kris Maglione y viendo la luz por primera vez el 1 de junio de 2005, tiene una filosofía minimalista de no ir más allá de 10,000 líneas de código. La última versión estable fue liberada el 1 de julio del 2017.

dwm

Es otro gestor de ventanas desarrollado por Anselm R. Garbe y liberándolo el 14 de julio de 2006. Es un wm muy minimalista pareciéndose a wmii, sin embargo, es mucho más simple que este último y está escrito en puro C para tener un rendimiento mucho más elevado además de añadir seguridad al código, pero este muy conocido gestor de ventanas se quedó en desarrollo al igual que wmii, el 1 de julio de 2017.

Fuentes