lunes, 15 de noviembre de 2021

¿Como enviar emails a través del terminal en una Raspberry Pi?



imagen de entrada mostrando un correo



Enviando correos desde la terminal de bash de Rapsbian.

A veces crear un programa de Python para enviar correo electrónico, sobre todo la primera vez que lo haces, puede resultar un poco engorroso.  Por eso, resulta más útil tener configurado un servidor smtp que nos permita enviar correos directamente desde el terminal. Una vez que lo tenemos configurado, además de enviar emails desde la línea de comandos,  podremos incluso usar posteriormente Python para enviar mensajes.

En la Rapsberry Pi funciona bien el programa msmtp.

1.- Instalación.


pi@raspberrypi:~ $ sudo apt-get install msmtp

2.- Configuración.


Lo primero que tenemos que hacer es crear nuestro archivo de configuración del programa. En este ejemplo voy a usar una cuenta para trastear que tengo de gmail, pero puedes usar la de otro proveedor y te dará la configuración para tu cuenta. Tecleamos el siguiente comando:

pi@raspberrypi:~ $ msmtp --configure tuCuentaEmail@gmail.com
# - copy this to your configuration file /home/pi/.msmtprc
# - encrypt your password:
#   gpg -e -o ~/.msmtp-password.gpg
account tuCuentaEmail@gmail.com
host smtp.gmail.com
port 587
tls on
tls_starttls on
auth on
user tuCuentaEmail
passwordeval gpg --no-tty -q -d ~/.msmtp-password.gpg
from tuCuentaEmail@gmail.com

Como siempre al usar una cuenta de Gmail hay que tener en cuenta:

- Si usas una cuenta con un doble factor de autentificación, la cosa se complica ya que tienes que usar una contraseña única para la aplicación y usarla en el archivo de configuración de msmtp.  Se puede hacer, pero es más lioso, con lo que te recomiendo, mientras lo permita google, crear una cuenta sencilla sin doble factor de autentificación para trastear. Además en este último caso (si no usas un doble factor de autentificación) necesitas permitir el acceso de aplicaciones inseguras en tu cuenta. Lo puedes hacer en este enlace.

Volviendo a la configuración, el comando previo nos da lo que tenemos que escribir en el archivo de configuración. ¡Pero no nos crea el archivo! así que tenemos que hacerlo nosotros.

En nuestro directorio home creamos el fichero ~/.msmtprc

pi@raspberrypi:~ $ nano .msmtprc
! NOTA. No debemos utilizar 'sudo' ya que es el usuario, pi, en mi caso el que envía el correo.

Como yo solo voy a usar esta cuenta de gmail, en la cuarta línea del archivo de configuración, cambio 'account tuCuentaEmail@gmailc.om' por 'account default' ya que no voy a usar más correos y esta va a ser la cuenta por defecto.

.msmtp   

# - copy this to your configuration file /home/pi/.msmtprc
# - encrypt your password:
#   gpg -e -o ~/.msmtp-password.gpg
account default
host smtp.gmail.com
port 587
tls on
tls_starttls on
auth on
user tuCuentaEmail
passwordeval gpg --no-tty -q -d ~/.msmtp-password.gpg
from tuCuentaEmail@gmail.com
Si quieres ver como sería un archivo de configuración con múltiples cuentas de correo puedes acceder a este enlace de x86blog donde encontrarás más información.

En este punto casi estamos listos para enviar nuestro primer correo pero antes de eso debemos ejecutar:

pi@raspberrypi:~ $ chmod 600 .msmtprc

Esto lo hacemos por dos buenos motivos. El primero es para una mayor seguridad, ya que el archivo puede contener la contraseña en forma de texto plano y este comando hace que solo el usuario pi pueda leer y escribir este archivo. Y la segunda es porque si no lo hacemos msmtp nos dará un error “must have no more than user read/write permissions”

Ahora probamos si todo esta bien enviando el primer mensaje. Para ello vamos a comentar la línea "#passwordeval gpg --no-tty -q -d ~/.msmtp-password.gpg". Al no tener esta línea, el programa, al ejecutarse, nos va a preguntar por nuestra password, la cual tendremos que escribir. Esto no es muy seguro, pero para ver si hasta ahora vamos bien nos vale. En el próximo paso descomentaremos esta línea y usaremos nuestra contraseña automáticamente pero codificada, para que en caso de que alguien logre acceder a el archivo de configuración no la pueda ver. 

Después de comentar la línea, escribimos:

pi@raspberrypi:~ $ echo "This is an email test." | msmtp -d correoDestino@outlook.com
Si todo ha ido como se espera, el programa te mostrará muchas líneas y te preguntará la contraseña de tu cuenta de correo. Una vez que se la pongas acabará de enviar el email.

Solo te queda ir a la cuenta donde lo hayas enviado y comprobar que está allí. Puede ocurrirte, como me paso a mi, que aparezca en la carpeta de spam, pero no pasa nada, le dices que no es spam y ya aparecerá desde entonces en la bandeja de entrada.

3.- Aumentar la seguridad.

Es sistema que hemos utilizado para enviar el correo no es muy práctico. Si tenemos un script automático no podemos estar delante para teclear la contraseña. Y mucho menos (aunque si pones una línea como 'password tuContraseña' en la configuración funcionaría)  guardar tu contraseña de correo en el archivo de configuración que al fin y al cabo es un archivo de texto plano. Todo esto lo hacemos directamente desde la rapsberry pi o desde una conexión vnc. (Por que a mi por lo menos si me conecto mediante ssh para la configuración me funciona a medias. Una vez configurado ya puedo mandar los correos de una forma normal.)

Para incrementar la seguridad podemos hacer dos cosas.

A) Usar gnome-keyring

Si ya tenemos Raspbian instalado en modo gráfico podemos utilizar esta opción para guardar nuestra contraseña de correo codificada y no tener que teclearla cada vez que mandemos un correo.

Es importante no tener el autologin activado al iniciar Rapsbian ya que si lo tenemos en automático la primera vez que enviemos un correo nos dirá que el "llavero estandar", donde se guarda la contraseña está bloqueado y tendremos que usar la contraseña del llavero para desbloquearla. Aunque solo la primera vez que lo utilicemos. Aunque se podría solucionar dejando la clave del llavero en blanco (y entonces no nos pediría desbloquearla) por seguridad no es recomendable. 

Empezamos instalando las librerías necesarias:

pi@raspberrypi:~ $ sudo apt-get install libsecret-tools
pi@raspberrypi:~ $ sudo apt-get install gnome-keyring

Pasamos a codificar la contraseña de gmail y guardarla en el llavero de contraseñas.

Tecleamos:

pi@raspberrypu:~ $ secret-tool store --label=msmtp host smtp.gmail.com service smtp user tuCuentaEmail

Aquí nos pedirá la contraseña de gmail. La introducimos y después de darle al enter, nos saldrá esta ventana:

introducir clave para el gestor de contraseñas

Para que funcione todo bien, utilizamos de contraseña, la misma que tengamos para el usuario 'pi' o tu usuario en el comienzo de sesión. Así cuando nos registremos para acceder a la raspberry con el usuario pi o el que tú uses, automáticamente se desbloqueara el llavero de contraseñas y no tendremos que introducirla cada vez que mandemos un email.

Si queremos comprobar que hemos tecleado la contraseña correctamente usamos la instrucción:

pi@raspberrypi:~ $ secret-tool lookup host smtp.gmail.com
y nos devolverá el valor de la contraseña.

También lo podríamos gestionar visualmente en Raspbian pero tendríamos que instalar el paquete "seahorse"  que es una aplicación gráfica de Gnome para gestionar las contraseñas.

Ahora modificamos el archivo de configuración de msmtp para decirle de donde debe obtener la contraseña. Descomentamos la línea de la contraseña y ponemos la que aparece en negrita.

.msmtprc

account default
host smtp.gmail.com
port 587
tls on
tls_starttls on
auth on
user tuCuentaEmail
passwordeval secret-tool lookup host smtp.gmail.com
from tuCuentaEmail@gmail.com

Procedemos a reiniciar la rapsberry pi y nos aseguramos de no logearnos automáticamente, sino tecleando la contraseña.  (si tienes el logueo automático puedes cambiarlo con sudo raspi-config en opciones de inicio)

Cuando arranque de nuevo, intentamos enviar un email cualquiera y nos saldrá que aun, la contraseña del llavero está bloqueada y se necesita autentificación. Si queremos que se desbloquee al inicio de la sesión, dejamos la opción marcada "Desbloquear este depósito de claves automáticamente siempre que hay iniciado sesión" e introducimos de nuevo la contraseña que pusimos. (la que pusimos antes y recuerda que tuvo que ser la misma que usa tu usuario para iniciar sesión)

desbloquear inicio de sesion


Y ya está. A partir de ahora no habrá que teclearla más y podremos enviar nuestros correos de forma automática.

B) Guardar la contraseña encriptada.


Para encriptar la clave del correo vamos a usar gpg que ya viene por defecto en Rapsbian.

(existe un paquete más moderno gpg2, pero voy a usar este aunque sea más antiguo porque ya viene por defecto).

Generaremos nuestras claves público-privadas:

pi@raspberrypi:~ $ gpg --gen-key
El programa solo nos pedirá un nombre de usuario y una dirección de correo electrónico que será nuestra id para operar con gpg.

Nombre y apellidos: tuCuentaEmail
Dirección de correo electrónico: tuCuentaEmail@gmail.com
Ha seleccionado este ID de usuario:
    "tuCuentaEmail <tuCuentaEmail@gmail.com>"
A continuación nos pedirá una contraseña para proteger la clave. (Haz el favor de guardarla bien que sino luego no podremos descifrar los archivos.) Una vez que han pasado unos segundos ya nos saldrá un mensaje en el que el programa nos comunica nuestra clave pública y que todo ha terminado correctamente.

Nos vamos en el sistema operativo de la raspberry pi al directorio /dev/shm. Este directorio tiene la particularidad de que reside en memoria, por lo que todo lo que hagamos en el se borrará al reiniciar el sistema.  Seguimos con estas instrucciones:

# Vamos al directorio /dev/shm
pi@raspberrypi:~ $ cd /dev/shm
pi@raspberrypi:/dev/shm $

# Creamos un directorio temporal y le asignamos permisos solo al usuario.
pi@raspberrypi:/dev/shm $ mkdir temporal
pi@raspberrypi:/dev/shm $ chmod 700 temporal
pi@raspberrypi:/dev/shm $ cd temporal

# Codificamos nuestra contraseña de gmail. Dentro de temporal tecleamos:
> gpg --encrypt --output=.msmtp-password.gpg --recipient=tuCuentaEmail@gmail.com - <<END
> LacontraseñademicorreoGmail
> END

EXPLICACIÓN DEL COMANDO ANTERIOR.

1) Primero tecleamos el comando y pulsamos Intro. Asegúrate de no olvidar el guión cerca del final, lo que haría que el comando gpg leyera la clave privada de codificación de la "entrada standar" de linux, con lo que dejamos la puerta abierta a que otro la pudiera leer. 

2) Luego en esa línea ponemos la contraseña de la cuenta de gmail y pulsamos Intro.

3) tecleamos END y pulsamo intro.

Y ya está, si ahora miramos el directorio veremos nuestro archivo con la contraseña codificada:

pi@raspberrypi:/dev/shm/temporal $ ls -a
.  ..  .msmtp-password.gpg

Para proteger este archivo, aunque está encriptado le damos solo permisos al usuario para trabajar con el:

pi@raspberrypi:~ $ chmod 600 .msmtp-password.gpg 
Ahora lo movemos al directorio donde se encuentra el archivo de configuración de msmtp.

pi@raspberrypi:/dev/shm $ mv .msmtp-password.gpg ~/

Importante. Tenemos que entrar en el archivo de configuración de msmtp y descomentar la línea que comentamos al principio para que nos quede así:

msmtprc

# - copy this to your configuration file /home/pi/.msmtprc
# - encrypt your password:
#   gpg -e -o ~/.msmtp-password.gpg
account default
host smtp.gmail.com
port 587
tls on
tls_starttls on
auth on
user tuCuentaEmail
passwordeval gpg --no-tty -q -d ~/.msmtp-password.gpg
from tuCuentaEmail@gmail.com

Sin embargo por alguna extraña razón (algun bug) si intentamos mandar ahora un correo este no funciona porque

pi@raspberrypi:~ $ echo "This is an email test." | msmtp -d correoDestino@outlook.com
ignoring system configuration file /etc/msmtprc: No existe el fichero o el directorio
loaded user configuration file /home/pi/.msmtprc
falling back to default account
gpg: descifrado fallido: No secret key
msmtp: cannot read output of 'gpg --no-tty -q -d ~/.msmtp-password.gpg'
¡dice que no puede leer la contraseña y sin ni siquiera preguntarnos por la clave de descifrado!. Es curioso porque la instrucción gpg --no-tty -q -d ~/.msmtp-password.gpg si funciona correctamente al ejecutarlo en el terminal pero no dentro del programa al usarlo con passwordeval. Para solucionarlo hay que hacer una pequeña modificación y solo funciona si estas trabajando con Raspbian en modo gráfico.

Instalamos el paquete pinentry-gtk-2


pi@raspberrypi:~ $ sudo apt-get install pinentry-gtk2

y luego tenemos que crear en el directorio .gnupg el archivo gpg-agent.conf (sino existe ya) y poner esta línea:

~/.gnupg/gpg-agent.conf

pinentry-program /usr/bin/pinentry-gtk-2


Y por fin si mandamos un correo con el comando que vimos al principio del post ya funciona y nos pide la contraseña para desencriptar. Lo bueno además es que esta contraseña queda codificada en cache y podemos seguir mandando correos sin tener que volverla a teclear mientras dure la sesión.




Ya para finalizar y si quieres enviar los correos de una forma más cómoda puedes utilizar el programa mail que se encuentra dentro del paquete mailutils. Se se integra bien con mmstp y permite trabajar de forma más cómoda para incluir objeto del mensaje, enviar archivos adjuntos etc.


viernes, 22 de octubre de 2021

Guía rápida para crear un servidor web casero con LAMP y WordPress en Rapsberry Pi

Imagen de worpress para la entrada


Como dice la wikipedia " Wordpress es un sistema de gestión de contenidos enfocado a la creación de cualquier tipo de página web". Y no solo eso, desde la raspberry pi, una vez creado el contenido podremos servir esa página al mundo exterior sin necesidad de contratar un alojamiento en ningún sitio.

Vamos con ello.

1) instalar el servidor web apache

Primeramente y como siempre hacemos procedemos a actualizar el sistema. 

pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get full-upgrade

Instalamos el servidor, en este caso apache.

pi@raspberrypi:~ $ sudo apt-get install apache2 -y

Vamos a necesitar conocer la IP de la rapsberry pi en nuestra red local. Si no tienes una dirección estática o no la sabes puedes conocerla con el siguiente comando:

pi@raspberrypi:~ $ hostname -I
Luego abre un navegador web y entra en http://localhost o si es desde otro equipo de la red local con la ip local de la rapsberry que obtuviste con el comando anterior (por ejemplo http://192.168.1.58) y saldrá la página de bienvenida de apache si todo ha ido bien.

página inicial del servidor apache


2) Instalar php necesario para que funcione wordpress.

> sudo apt-get install php -y

entramos en:

> cd /var/www/html/

borramos index.html 

> sudo rm index.html
y para para comprobar si funciona con un editor de texto, nano por ejemplo (sudo nano index.php), crearemos un archivo llamado index.php con el siguiente contenido.
<?php echo "hello world "; ?>
<?php echo date('Y-m-d H:i:s'); ?>
<?php phpinfo(); ?>

Guardamos el archivo y salimos del editor.

Para comprobar que funciona entramos en un navegador y tecleamos htpp://localhost, o si accedemos desde otro ordenador de nuestra red, tecleamos la dirección IP de la Raspberry.

Deberíamos ver esta pantalla.

pantalla de bienvenida de php


sino vemos el mensaje podemos probar a reiniciar el servidor web con:

> sudo service apache2 restart


3) Instalar MySQL

Instalamos MariaDb junto con los paquetes PHP_MySQL.

> sudo apt-get install mariadb-server php-mysql -y

volvemos a reiniciar el servidor:

> sudo service apache2 restart


4) Instalar wordpress

Volvemos al directorio /var/www/html/ donde tenemos index.php y una vez dentro borramos todo el contenido.

> sudo rm index.php

Descargamos la versión mas reciente de wordpress con:

sudo wget http://wordpress.org/latest.tar.gz

extraemos su contenido con:

 sudo tar xzf latest.tar.gz

Ahora hay que pasar todo el contenido de la carpeta wordpress que se habrá creado en el directorio, a la actual en la que estamos situados (/var/www/html/) con :

> sudo mv wordpress/* .

ordenamos un poco las cosas eliminando el archivo descargado y el directorio wordpress

> sudo rm -rf wordpress latest.tar.gz

Por ultimo damos permisos al usuario Apache para gestionar estos archivos.

> sudo chown -R www-data: .

Lo que sigue hasta el paso 5 es opcional:

Para gestionar la base de datos de forma más visual podemos instalar un gestor de mysql como phpmyadmin.

> sudo apt-get install phpmyadmin

Durante la instalación nos preguntará en que servidor queremos instalarlo

seleccion de apache en la instalación de phpmyadmin

, seleccionaremos apache2. Igualmente indicaremos SI cuando nos pregunte sobre configurar la base da datos para phpMyAdmin con dbconfig-common.



Después nos preguntarán una contraseña para phpMyAdmin. Podemos dejarla en blanco ya que el programa establecerá una aleatoria y nosotros no la necesitaremos el resto del tutorial. Si posteriormente queremos acceder a la base lo haremos con la contraseña del usuario 'root' que configuraremos luego. 

Finalmente, cuando haya terminado necesitamos editar una línea para indicarle a Apache que hemos instalado PHPMyAdmin. Para ello:

> sudo nano /etc/apache2/apache2.conf

y añadiremos el final de este archivo lo siguiente:

Include /etc/phpmyadmin/apache.conf

Una vez realizado, salvamos presionado CTRL + X y pulsamos Y para aceptar los cambios.

Reiniciamos Apache:

> sudo service apache2 restart

y comprobamos que se ha instalado correctamente accediendo a la siguiente url desde nuestro navegador en la Raspberry Pi http://localhost/phpmyadmin o bien desde otro equipo de la misma red con http://direción_IP_PI/phpmyadmin. Deberíamos ver algo similar a:

phpmyadmin, página de bienvenida en Raspberry pi

5) Configurar la base de datos de wordpress

Procedemos a configurar la base de datos antes de iniciar wordpress.

> sudo mysql_secure_installation 

Vamos contestando a las preguntas que nos hace el programa.

1 pregunta cofiguracion mysql_sevure_installation

Lo primero que nos dicen es que pongamos la contraseña root de la base de datos, pero como acabamos de instalarla aun no tiene ninguna contraseña así que le damos un intro para dejarla en blanco.

A continuación si que ya ponemos una contraseña para el usuario root de la base de datos , seleccionando "yes" en la pregunta de que si queremos contraseña y la confirmamos.


introducion de contraseña de root


A todas las demás preguntas que nos haga las dejamos en la opción por defecto dándole al intro.


5.a. Creamos la base de datos para wordpress con:

> sudo mysql -uroot -p

Lógicamente usamos la contraseña que elegimos antes.

Luego  vamos a crear una base de datos que se llamará wordpress, y le damos privilegios para poder acceder a ella al usuario 'root':

MariaDB [(none)]> create database wordpress;
MariaDB [(none)]> GRANT ALL PRIVILEGES ON wordpress.* TO 'root'@'localhost' IDENTIFIED BY 'PASSWORD';

Importante: sustituye la palabra PASSWORD por la contraseña que le hayas puesto al usuario 'root'.

MariaDB [(none)]> FLUSH PRIVILEGES;

Salimos del programa pulsando ctrl + d.


6.) Entramos en Wordpress y acabamos de rematar el proceso.

Ahora que lo tenemos todo preparado es hora de entrar en Wordpress. Para ello como hemos hecho hasta ahora, en un navegador acedemos al localhost o si como en mi caso lo estas haciendo desde otro equipo usamos la dirección Ip de la Rapsberry en nuestra red. Nos aparecerá la página de configuración inicial de Wordpress.

pagina de elección de idioma de wordpress

Escogemos el idioma de la aplicación que queremos y le damos a continuar. Nos aparecerá la pantalla de Bienvenida.

Pantalla de bienvenida de wordpress


Ya en la siguiente pantalla tenemos que poner los datos de configuración de la base de datos que usará wordpress y que ya hemos creado antes.


configuración de base de datos de wordpress


Cambia la contraseña por la que hayas asignado antes al usuario 'root'. Si todo ha ido bien wordpress se podrá conectar con éxito a la base de datos  y ya casi habremos terminado.

pantalla final de la instalación de wordpress


Ya solo nos queda elegir un nombre para nuestro sitio web, elegir un nombre de usuario y una contraseña (recuerda ambos que sino luego no podrás entrar) y poner nuestro correo electrónico.

información necesaria para acceder a wordpress


Cuando hayas terminado el programa de instalación nos dirá que ha finalizado y a continuación ya aparecerá la página para logearnos en wordpress y poder empezar a crear nuestro sitio web.

pantalla de login en wordpress

y con esto ya habremos terminado. 

Vista web de la página de administración de Wordpress


Y mientras tengas la rapsberry pi encendida tu sitio web estará disponible y en funcionamiento. 



domingo, 17 de octubre de 2021

Bucles en Python con for y while




Bucles en Python con For y While.


Bucles Determinados (for).

Un bucle for se utiliza para recorrer una secuencia de datos ya sea una lista, una tupla, un diccionario, un conjunto o una cadena de caracteres.

Con el bucle for podemos ejecutar un conjunto de instrucciones, una vez por cada elemento de una lista, una tupla, etc.

Una estructura típica de un bucle for sería:

for variable in elementos_a_recorrer(lista, tupla, rango etc)
    |        "
    |    Cuerpo del bucle
    |        "
    |        "

Por ejemplo podemos recorrer una palabra,  que es una cadena de caracteres, con este tipo de bucles.

> letras = "Hola"
> for letra in letras:
>    print(letra)
H
o
l
a

Otras veces se utiliza este tipo de bucle para ver los diferentes elementos que están dentro de un objeto de Python. Por ejemplo, el tipo range(n) sirve para que de forma interna se cree un objeto que contenga n elementos. Esto nos sirve para crear una especie de contador. 

> for i in range(5):
> 	print(i, end=" ")

0 1 2 3 4

Como vemos, range(5) crea un objeto que contiene 5 elementos, empezando desde el cero. También podemos indicarle que cree un objeto que empiece en un número y termine en otro. Si queremos crear un objeto con los números del 5 al 10, haríamos lo siguiente:

> for i in range(5, 11):
>	print(i, end=" ")

5 6 7 8 9 10

Lo que le hemos dicho al programa es que cree un objeto con 6 elementos (11-5) empezando desde el número 5. Incluso le podemos decir que se salte números introducciendo un tercer argumento llamado "salto", por ejemplo para que nos calcule todos los números impares del 1 al 10 podemos usar la siguiente instrucción:

> for i in range(1,11,2):
> 	print(i, end=" ")

1 3 5 7 9

Haciendo un inciso y ya que se puede recorrer sus objetos con un bucle for, vamos a ver el módulo de Python "itertoools". Este módulo como su nombre indica nos proporciona ciertas herramientas de iteración. Principalmente contiene las siguientes:

- count:    crea un objeto que nos permite contar desde un determinado valor hasta el infinito si no lo parasemos.

> from itertools import count
> for numero in count(1):
>	print(numero, end = " ")
>	if numero == 10:
>		break

Comenzamos importando el módulo count y recorriendo el objeto que genera desde el número 1.  Para que no siga hasta el infinito a través de un condicional le decimos que cuando llegue al número 10, rompa el bucle. (un poco más adelante veremos de nuevo la sentencia break un poco más explicada). Un segundo argumento indicaría el salto de un número al siguiente. La salida del programa sería:

1 2 3 4 5 6 7 8 9 10 

- cycle : realiza una iteración infinita a través de un iterable.

> from itertools import cycle
> iterable = "hola"
> for i in cycle(iterable):
>	print(i, end = " ")

Con este bucle se repetiría la palabra "hola" de forma infinita.

o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l
a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h
o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h ol
a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a...

- repeat(valor, num): repite un elemento de forma infinita o si le proporcionamos un segundo argumento un número determinado de veces.

Por ejemplo, si quisiéramos repetir el número 15, cuatro veces, haríamos lo siguiente:

> from itertools import repeat
> for i in repeat(25,4):
>	print(i)

25
25
25
25

- accumulate(objeto [, función]): este iterador usa dos argumentos, el objeto iterable y la función que se seguiría en cada iteración. Si no se pasa ninguna función, la suma se realiza de forma predeterminada. 

> # Importamos el modulo itertools para trabajar con él.
> from itertools import accumulate
 
> # Creamos una lista.
> lista = [1,2,3,4,5]

> # Creamos el iterador y lo asignamos a una variable
> resultado = accumulate(lista)

> # Imprimimos el resultado
> for i in resultado:
> 	print(i)

1
3
6
10
15

La explicación del funcionamiento es que cada número se suma al anterior y su resultado al siguiente y así sucesivamente.

1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
10 + 5 = 15

- takewhile(expresión, iterable). Este iterador va cogiendo un elemento del iterable hasta que la primera vez que la expresión nos devuelve False, en la que termina. Lo vemos mejor con un ejemplo. Vamos a coger números de una lista hasta el momento en que nos encontremos con un número impar:

from itertools import takewhile
def es_par(x):
	return(x%2==0)
	
lista = [2,6,8,7,10,12]

nueva_lista = takewhile(es_par, lista)

for i in nueva_lista:
	print(i)

2 6 8

- chain(*iterables). Esta función coge varios iterables y los une en uno solo. 

from itertools import chain
 
# una lista de números impares
odd = [1, 3, 5, 7, 9]
 
# una lista de números pares
even = [2, 4, 6, 8, 10]
 
# encadena los números pares con los impares.
numbers = list(chain(odd, even))
 
print(numbers)

[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]

product(*iterables, repeat=1) Nos realiza una combinación con repetición de n elementos. Por ejemplo si queremos saber todas las combinaciones posibles de dos listas de números: 

from itertools import product
 
# una lista de números 
una = [1, 2]
 
# otra lista de números 
otra = [2, 4, 6, 8, 10]
 
# combina los elementos de una con los de otras.
for i in product(una, otra):
	print(i, end=" ")

(1, 2) (1, 4) (1, 6) (1, 8) (1, 10) (2, 2) (2, 4) (2, 6) (2, 8) (2, 10)

- permutations(iterable, r=None) Retorna permutaciones de elementos sucesivos de longitud r en el iterable. 

from itertools import permutations
 
# una lista de números 
lista=[2,5,7]
 
# realiza todas las permutaciones de los numeros de la lista
for i in permutations(lista):
	print(i)

(2, 5, 7)
(2, 7, 5)
(5, 2, 7)
(5, 7, 2)
(7, 2, 5)
(7, 5, 2)

Existen muchas más funciones interesantes en itertools que puedes encontrar en la documentación de Python.


Otro cosa interesante cuando se usa un bucle es la función len(). Esta función nos devuelve el número de elementos de un objeto y nos puede venir bien para realizar una iteración en función del número de elementos que tiene un objeto.

cadena = "Daenerys"
print(f'la palabra {cadena} tiene {len(cadena)} letras')
for i in range(len(cadena)):
	print(i,'letra', cadena[i])

la palabra Daenerys tiene 8 letras
0 letra D
1 letra a
2 letra e
3 letra n
4 letra e
5 letra r
6 letra y
7 letra s


Bucles Indeterminados.


Con los bucles while, podemos ejecutar un conjunto de instrucciones mientras que la condición que se evalúa sea verdadera.

Por ejemplo:

Imprimamos la variable de control i siempre que esta sea menor de 6.

i = 1
while i < 6:
  print(i)
  i += 1
1
2
3
4
5

Con estos bucles hay que tener cierta precaución. En el ejemplo anterior si hubiésemos olvidado incrementar la variable en uno en cada giro del bucle, este se hubiese repetido de forma infinita ya que la condición siempre hubiese sido verdadera.


Instrucciones tanto para bucles for como para while.


La instrucción break

Solo se permite dentro del cuerpo del bucle. Cuando se ejecuta el bucle finaliza. Por lo general una sentencia break esta dentro de alguna clausula de una sentencia if en el cuerpo del bucle, para que break se ejecute con condiciones.

Ejemplo de un bucle que termina cuando la cuenta llega a 3.

i = 1
while i < 6:
  print(i)
  if i == 3:
    break
  i += 1

1
2
3

La instrucción continue.

Al igual que el anterior solo se admite dentro del cuerpo de un bucle. Cuando se ejecuta, la iteración actual del cuerpo del bucle finaliza, y la ejecución continua con la próxima iteración del bucle. 

Ejemplo de un bucle en el que se cuenta del 1 al 6 saltando el numero 3.

i = 0
while i < 6:
  i += 1
  if i == 3:
    continue
  print(i)

# Date cuenta como el número 3 no está en el resultado.

1
2
4
5
6


La instrucción else en los bucles.

Las sentencias while y for pueden opcionalmente incluir una clausula else posterior y al mismo nivel, porque se trata como un conjunto. La sentencia o el bloque que se situa debajo de else se ejecuta de manera natural siguiendo el flujo del programa (al final del iterador for , o cuando la condición del bucle while se convierte en falsa) pero esto no sucede cuando el bucle finaliza antes de tiempo (mediante break o una excepción por ejemplo).

email = input("Introduce un email: ")
for i in email:
	if i=="@":
		arroba = True; break
		# ; varias sentencias en una sola línea
else:
	arroba = False
print(arroba)


La sentencia pass.

El cuerpo de una sentencia compuesta en Python no puede esta vacía (como por ejemplo un bucle for, una funcion o una sentencia if), debe contener al menos una sentencia, una instrucción. Podemos usar una sentencia pass, que no realiza ninguna acción simplemente pasa, para solventar este problema.

for x in [012]:
  pass

Si lo ejecutas, veras que el bucle funciona ¡Pero no hace nada!.