8 de agosto de 2018

Repasando Python. Mi solución a "New Cities - SENDGRID"

No estoy muy convencido. Habría que repasar la lista de las conexiones filtradas, por si pueden generar agrupaciones. Pero resuelve la cuestión.


= = = = = = = = = =

import re

# -------------------
# pasar lista a cadena
# -------------------
def pasarLista(listado):
    cadena=[]   
    for subcadena in listado:
        for item in subcadena:
            cadena.append(item)
    return "".join(cadena) # envía la cadena.
   
def filtro(listado,quitar):
    cadena=[]
    for subcadena in listado:
        if (not subcadena[0] in quitar and not subcadena[1] in quitar):
            cadena.append(subcadena)
    return cadena

# ----------------
# función problema
# ----------------
def subnetworks(net, crushes):
  
    suma = 0
   
    # 1/ Construir una cadena con las conexiones.
    # hay una conexión a la fuente.
    cadena=pasarLista(net)
    cadena2=filtro(net,crushes)
   
    # 2/ Quitar los que estén en crushes - poner guiones.
    cadenaCrushes="".join(sorted([i for i in crushes]))
    cadenaNodos=re.sub("["+cadenaCrushes+"]",r"-",cadena)
    # cadenaNodos=re.sub("["+cadenaCrushes+"]",r"-","BABBACCDHHFDCACBACSE")
   
    # 3/ Obteniendo lista de grupos en cadenaNodos
    # unicos --> grupos de un solo nodo
    # conjunto --> cadena agrupando nodos
    conjunto=[j for i in cadena2 for j in i]
    unicos=[i for i in cadenaNodos.split("-") if len(i)==1]
   
    #4/ A partir de la cadena conjunto
    # si único está en la lista conjunto, quitar de único
    for j in conjunto:
        if j in unicos:
            unicos.remove(j)
           
    # print(cadena,cadenaCrushes,cadenaNodos,unicos,conjunto)
    # print(cadena2)
    # print(len(cadena2)+len(unicos))
   
    # La solución es la suma de los nodos filtrados +
    # los nodos que se han quedado sueltos en único
    # a menos de que tenga que hacer algo más en CONJUNTO
   
    return len(cadena2)+len(unicos)

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert subnetworks([
            ['A', 'B'],
            ['B', 'C'],
            ['C', 'D']
        ], ['B']) == 2, "First 2"
    assert subnetworks([
            ['A', 'B'],
            ['A', 'C'],
            ['A', 'D'],
            ['D', 'F'],
        ], ['A']) == 3 , "Second 3"
    assert subnetworks([
            ['A', 'B'],
            ['B', 'C'],
            ['C', 'D']
        ], ['C', 'D']) == 1, "Third 1"
    assert subnetworks([
            ['A', 'B'],
            ['A', 'C'],
            ['A', 'D'],
            ['D', 'F'],
            ['B', 'C'],
        ], ['A']) == 2, "Second-BIS 2"
    print('Done! Check button is waiting for you!')

5 de agosto de 2018

Prestashop. Guía de trabajo.

Prestashop

Primera parte

Presentación de la tienda on line. Items calificables de la primera parte de la página.
  1. Inclusión de, al menos, tres categorías o subcategorías de productos. Las categorías no tienen por qué tener una imagen asociada.
  2. Cada categoría debe tener, al menos, un par de etiquetas. Puedes añadir además una meta descripción, un meta title y una url amigable.
  3. Usar Localización > Localización para importar paquetes de idiomas, monedas, etc. Descarga los de "United Kingdom" y "USA". Si navegas por Localización > Idiomas, podrás ver cómo se han añadido los idiomas inglés británico e inglés estadounidense. (¿Versión 1.6 sólo Inglés?)
  4. Edita una de las categorías o subcategorías que tienes, elige idioma inglés británico, y escribe las entradas de esa categoría en inglés. Guarda. Ve a la tienda, entra en esa categoría y cambia al idioma inglés para comprobar que aparece correctamente. Recuerda en que tienes que traducir al inglés cada campo que escribiste antes en español: etiquetas, url amigable, etc.
  5. Elegir 6 productos, que hay que distribuir entre las 3 categorías. Uno de esos seis productos deberá tener la información en inglés británico. Al menos uno de los productos tiene que tener una imagen (opcional: más de una imagen para un mismo producto, con una de portada). Importante: elige una referencia clara (clave primaria) para cada producto, como por ejemplo PR01,PR02,etc.
  6. Establecer los precios y el IVA de cada producto.
  7. Añade un nuevo impuesto y aplícaselo a un producto de los seis anteriores. Navega hasta Localización > Impuestos y añade el NUEVO IVA 23%. Al fondo de la página, elige basada en "Dirección de facturación". Navega después a Localización > Reglas de Impuestos y crea una nueva regla de impuestos, con el nuevo IVA, que se llame NOS SUBEN EL IVA, aplicado sólo a España. Importante: después de crear la regla, pulsar en "Añadir nueva regla de impuestos" (ver imagen).

  1. A otro producto cualquiera, establecer precios específicos desde la pestaña PRECIO. Elige que para los clientes (customers) españoles pagando en euros, se aplique una reducción de un 5% si compran tres o más unidades de ese producto.
  2. Sería conveniente que entrando en Catálogo > Atributos de Productos seleccionemos los que necesitamos. Por ejemplo, yo no necesito el número de zapatos, ya que no los voy a vender. Personalizar la lista de estos atributos.
  3. Gestiona manualmente la cantidad de productos que existen usando combinaciones, si un mismo producto tiene distinto tamaño o color pero sigue siendo el mismo y costando lo mismo, y cantidades, para definir manualmente cuántos productos quedan en la tienda.
  4. Utiliza la pestaña SEO para añadir información meta de cada producto. En español e inglés, de igual forma que lo hiciste para las categorías.
  5. Las características pueden gestionarse en el menú Catálogo > Características. Pon valores a algunas. Estas son características GENERALES de los productos. Navega después hasta PRODUCTOS > CARACTERÍSTICAS (o FUNCIONALIDADES en otra versión) y elige alguna para esa gama de productos.
  6. Añadiremos combinaciones (antiguas declinaciones) de los productos (pág 27 del manual). Navega hasta el menú Catálogo > Atributos y valores (Atributos de productos). Elige un atributo nuevo, distinto a color, capacidad, ICU que vienen por defecto y añádelo. Pulsando en la lista de atributos en Acciones el botón "+" aparece la lista de valores. Añade algunos. Posteriormente, en PRODUCTOS > COMBINACIONES  añade una nueva combinación de este producto usando el atributo que antes hayas elegido.
  7. NOTA: las características son siempre las mismas para todos los productos. Los atributos y valores pueden depender de los modelos que haya para cada producto.
  8. Opcional: personalización de productos. Añadir adjuntos (por ejemplo, guías de instalación, etc).
  9. Añadir, al menos, un "pack" de productos.

Segunda parte

  1. Entra en Catálogo > Monitoreo. Podrás observar si tienes categorías vacías, productos desactivados, productos con o sin atributos y fuera de stock
  2. OBSOLETO EN LA VERSIÓN 1.6 (no hacer).En Catálogo > Mapas de Imágenes (escenas) puedes incluir imágenes interactivas. Una imagen grupal en la que, pulsando con el botón en diferentes zonas, accedes a diferentes productos. Lee la siguiente guía:  mapas de imágenes. Pulsa en "guardar y continuar" para ir guardando los cambios. Añade, al menos, una imagen mapeada o una escena.
  3. Puedes gestionar las etiquetas usadas en la web desde Catálogo > Etiquetas. Asegúrate que, al menos, existen 4 etiquetas en la web, tanto en español como en inglés (CUATRO POR CADA UNA).
  4. En Clientes > Grupos añade un nuevo grupo de clientes. Por ejemplo, un grupo llamado "de la provincia de Cádiz" a los que se aplicará un 5% de descuento.
  5. Añade manualmente clientes a la base de datos, en Clientes > Clientes. El nombre, apellidos, dirección de correo electrónico y una contraseña son obligatorios. Añade, al menos, 2 clientes a la base de datos.  Uno debe pertenecer al grupo anterior (punto 4).
  6. En Clientes > Direcciones, al menos para uno de los clientes anteriores, introduce datos de teléfono, dirección, código postal, etc. Empieza escribiendo el correo electrónico, y podrás recuperar el nombre, apellidos, etc. La información ya añadida en clientes.
  7. Truco: para ver toda la información referida a un cliente, el correo electrónico desaparece y se convierte en un botón. Al hacer click, aparecerá no sólo su información, sino lo que haya comprado, su carrito, sus direcciones, sus grupos, etc.
    • Bien por el procedimiento del punto 7 o bien entrando en Clientes > Carrito se puede saber qué ha pedido el cliente.
  8. Introduce al menos un fabricante nuevo usando el acceso Catálogo > Fabricantes. Recuerda activarlo. Los campos de información son ya conocidos, con etiquetas, descripción, logo... Los fabricantes coinciden con las distintas marcas que vendemos en la tienda. En la lista de los productos, en Catálogo > Productos (Elige un producto de la lista) > Asociaciones puede cambiarse el nombre del fabricante de dicho producto.
  9. Introduce al menos dos nuevos proveedores usando el acceso Catálogo > Proveedores. Añade información de dónde se encuentran esos proveedores y actívalos. Navega hasta Catálogo > Productos (Elige un producto de la lista) > Proveedores y, por cada producto, elige los proveedores que te suministran ese producto. Puedes además incluir la referencia que tu proveedor tiene de ese producto.
Gestión de los módulos de pago.
  1. En este punto hemos de decidir cómo nos podrán pagar los clientes. Buscamos la opción del menú Módulos > Pago . Aparecerán muchos servicios a través de los cuales poder ingresar dinero. Activados, por defecto, tenemos Trasferencia bancaria y Cheque. Vamos a desactivar la opción de cheque y a configurar las opciones de Transferencia bancaria (si es necesario, actualizar previamente). Incluir nombre de la empresa propietaria de la cuenta, nº de cuenta, sucursal, dirección de la sucursal (inventaros los datos). En un caso real, este punto es fundamental. Hay que barajar la posibilidad de aumentar el número de formas en las que un cliente pueda pagarnos. Una muy usada es PayPal.
  2. De las otras opciones vamos a instalar el módulo de Pago Contra Reembolso (o cashondelivery).
  3. En la misma página de "Pago" podemos gestionar las restricciones (al fondo de la página web). Entre ellas, vamos a activar los módulos de transferencia y pago contra reembolso para todos los países, quitárselas a los visitantes e invitados (visitors-guests), y por moneda, dejarla la transferencia sólo en euros. No os olvidéis de pulsar en "Guardar restricciones" para cada caso.
Transportistas.
  1. Es interesante restringir las zonas geográficas, países y estados a los que se venderá. Verlo en Localización >> Zona (deja activado sólo Europa y Norteamérica). Usa esta opción en combinación con las zonas geográficas de los transportistas (punto siguiente).
  2. Definir dos nuevos transportistas (Menú Transportes >> Transportistas). Puedes, por ejemplo, acceder a correos y definir la entrega por paquete azul (nacional) o por paquete internacional económico (internacional) (*). Otro de ellos, puedes buscarlo en las múltiples empresas de paquetería, como NACEX, SEUR, etc.
    • (*) NOTA: las condiciones de los envíos de paquetes con correos pueden cambiar. En todo caso, consultar la web.
    • SEUR tiene un módulo de instalación gratuita en PRESTASHOP 1.6, aunque hay que registrarse en la compañía para usarlo.
    • Al menos, de uno de los transportistas hay que rellenar la ficha: velocidad, plazo máximo, rangos de precios según zonas geográficas, tamaños máximos de paquetes, etc.
  3. Llegados a este punto, llamar al profesor/a para que pueda realizar una compra en mi tienda.
Anexo: configuración del tema en http://profesoryale.com/plantillas-prestashop/

Tercera parte

  1. En este punto el profesor/a debería haber realizado una o dos compras en mi tienda (una como transferencia bancaria, otra como pago contrareembolso).
  2. Consultar la lista de pedidos (pág. 54) en Pedidos > Pedidos.
    1. Comprobar el estado de cada pedido. Pulsar en la lista en el icono 
    2. En el pedido por Contrareembolso  puede comprobarse su estado (Preparación en curso). Puede, dentro de sus características, verse la factura generada y el comprobante de envío.  Cambia el estado a "enviado", como que ya se ha realizado el envío.
    3. Asímismo, cambiar el estado de un envío por transferencia como "pago aceptado" o "enviado".
    4. En Facturas, puede verse un desglose de las facturas emitidas, según fechas o estados. En ("Bonos de entrega")"Comprobantes de Entrega" se puede ver una relación de lo que se ha enviado al cliente. Queda constancia en este último caso de la factura y del albarán de entrega.
    5. En ("Estado de los pedidos") "Estados" se definen todas aquellas situaciones en las que se puede encontrar un pedido.
    6. Vamos a activar las devoluciones. En Pedidos > Devoluciones de mercancía activar las devoluciones en 15 días. Avisar al profesor/a para que, bajo su cuenta, demande la devolución de un producto. Debe escribir una razón. Vosotros podréis poner el estado de la devolución en "Pendiente de confirmación" ," En espera de paquetes", "Paquetes recibidos", "Volver negó" y "Diligenciados".
    7. Opcional: establece una devolución como "diligenciada". Accede después a Pedidos >> Pedidos. En la parte superior, encontrarás un icono de "Devolución parcial" "Reembolso Parcial". Púlsalo. Podrás entonces...
      • "Reingresar productos al inventario" - Opción que deberás confirmar si el producto vuelve a estar disponible para la venta.
      • "Crear un bono" - Le creará un bono de descuento para usar en próximas compras, estos aparece en "Pedidos >> Vales"  "Reglas de Descuentos/Vales descuento"
      • "Reembolso parcial" (Botón) esto te creará la factura rectificativa del pedido por el importe indicado; una factura rectificativa es importante para cumplir con la fiscalidad española. Dicha factura puede obtenerse del listado de documentos como "Créditos a favor".
    8. Reglas de descuento >> Vales descuento (vales o reglas de cesta). Seguid las instrucciones que se dan en el vídeo (https://youtu.be/BW__4PY6SLc y crear una regla de compra o vale para regalar un descuento a un cliente concreto.  Esta opción sirve, o para bien realizar una devolución descontando lo pagado en la próxima compra, o bien para aplicar un vale de descuento promocional a un grupo de clientes a los que, por ejemplo, se les regale una cierta cantidad de dinero en sus compras, o uno de los productos de la tienda, o la rebaja del precio de envío. Es una opción muy configurable.
    9. Ofrecer una regla de descuento del 5% a una categoría de producto determinada en Reglas de descuentos >> Reglas del catálogo.
  3. Opcional: Conviene repasar los vídeos de Luis Cambras sobre gestión de pedidos:
  4. Mensajes al cliente. Si al cliente, desde pedidos, le queremos enviar un mensaje podemos hacerlo. Escogeremos un mensaje predefinido (que podemos configurar desde Pedidos > Mensajes de Pedidos) o bien escribiremos uno nosotros. Escribid uno.

Cuarta Parte


  1. En Localización >> Monedas, activaremossólo el euro como moneda transaccional. Actualizar las tasas de cambio.
  2. En Estadísticas, tenemos una gran cantidad de opciones que analizan distintos aspectos de nuestra tienda. Analiza, por ejemplo, las ventas y pedidos en el período de un año. Enseña los resultados/as a tu profesor/a. Valora el hecho de estudiar opciones como "Procedencia de los visitantes" que indica qué página web han visto tus clientes ANTES de acceder a tu página.
  3. Los módulos son funcionalidades extra que pueden instalarse o desinstalarse de la tienda. Accede a la lista en Módulos >> módulos. Desde aquí pueden activarse, desactivarse, instalarse, desinstalarse... Por ejemplo, filtra los módulos por Módulos no instalados, y por ejemplo, instala el módulo "Shopgate M-Commerce". Usando la pestaña Módulos >> Catálogo de módulos y temas accederás a una serie de módulos y temas instalables, pero no gratuitos. También desde el enlace "Addons" (enlace en la parte inferior derecha de cualquier página del back-office).
  4. Instalación de un tema (instala al menos otro tema):
  5. Para configurar el aspecto de la tienda (plantilla por defecto) os dejo esta web: http://profesoryale.com/plantillas-prestashop/
  6. Leer bien el apartado 5. Algunas de las cosas que aparecen en la página se modifican en el Configurador de temas y otras tienen sus módulos específicos, como las diapositivas de la página web (que hay que buscar en Módulos  >> Módulos ).
  7. El menú superior, por ejemplo, puede cambiarse desde Módulos  >> Módulos buscando "Menú horizontal superior".
  8. En Módulos >> posiciones de los módulos pueden cambiarse la posición de los módulos en el front-office (tienda visualizada). Cada zona de la página se llama "hook". Los módulos pueden cambiarse de hook, aunque no siempre es posible, porque el programa no lo permite. Más información en esta web.
  9. Editar PREFERENCIAS:
    • Asegurarse de que en General Configuración habilitamos el redondeo Clásico.
    • En Pedidos poner el proceso de pedido en una sola página.
    • Habilita el modo B2B en las preferencias de cliente.
    • En SEO y URL tener en cuenta que hay que escribir el dominio donde está mi página. En modo local es localhost, pero si la lanzo por internet hay que cambiar el nombre del dominio. también se puede cambiar el nombre y la información de ciertas páginas: si cambio la página "index" personalizo la información que aparece en el título de la ventana del navegador y su pestaña.
    • En CMS se gestiona el contenido estático, o sea, de aquellas páginas que muestran informaciones fijas, como las condiciones de compra, etc.
    • Se pueden gestionar en preferencias >> imágenes los tamaños y los tipos de imágenes.
  10. Otros:
    • En Clientes >> Títulos Tratamientos se puede cambiar el tratamiento formal de una persona: señor, señora, doctor, etc.
    • En Parámetros Avanzados >> Correo electrónico tener en cuenta que , si no funciona el envío de mensajes con la función mail de PHP, es posible que tengamos que configurar una dirección de correo por SMTP. Consultar proveedor de  correo electrónico. A mí me gusta el servicio de gmail.
    • En Parámetros Avanzados > Copia de seguridad DB Respaldar BD puedes guardar una copia de los datos de la base de datos. Importante tener copias de tu página por si necesitases restaurarla. Hazlo con cierta frecuencia.
    • En Parámetros Avanzados > Protocolos del sistema Registros/Logs pueden verse los errores que se han cometido en el mismo. Normalmente los errores no son críticos. Aquí aparece un listado de ellos. También u nregistro de lo que hemos ido haciendo en la página.
    • Si está instalado el módulo de actualización 1-click , desde Parámetros Avanzados >> 1-click upgrade podemos actualizar las versiones. Recomiendo antes hacer copia de la base de datos y del sitio web.
    • En Administración >> Preferencias pueden editarse algunos parámetros, como el tamaño máximo de archivos que pueden subirse al sitio.
    • En Administración >> Acceso rápido se pueden editar diversos accesos a ciertas páginas.
    • En Administración >> Empleados pueden añadirse cuentas de administración del sistema, para que más personas puedan administrar el sitio web. Añadir al menos otra cuenta de administrador.
    • En Administración >> perfiles y permisos pueden editarse ciertos roles con los permisos correspondientes para administrar el sitio, más o menos importantes.
    • En Preferencias >> productos, apartado "inventario de productos", activar las opciones: "¿Activar el manejo automático del inventario?" y "¿Habilitar las opciones avanzadas del inventario?". Aparecerá una nueva opción del menú de administración llamada Existencias. Desde aquí puede gestionarse el stock de productos.

2 de agosto de 2018

Repasando Python. Mi solución a "node disconnected users - SENDGRID"

def disconnected_users(net, users, source, crushes):
 
    suma=0 #total de mail-pigeons
    conectado={} #Diccionario de conectados o no...
 
    # print (net) 
    # 1/ marca la fuente como 1, el nodo roto como -1 y los demás como ceros.
    for i in users:
        conectado[i]=(i in source)-(i in crushes)
     
    # 2/ Busca en las listas si está al lado de uno conectado. Si lo está, le pone un uno.
    # hay una conexión a la fuente.
    for h in range(0,2): # dos pasadas al menos lo aseguran. Parecen estar las listas ordenadas, pero ¿y si no lo están?
        for j in net:
            for k in range(0,2): #límite superior 1
                # Si el otro es 1 y este es cero, lo cambia a uno. Si era 1 lo deja, y si era -1 tb lo deja.
                conectado[j[(k!=1)]]=(conectado[j[(k!=1)]]==1)-(conectado[j[(k!=1)]]==-1)+(conectado[j[k]]==1 and conectado[j[(k!=1)]]==0)
                # print(j,conectado[j[(k!=1)]],conectado) 
 
   # 3/ Sumo los valores de los que NO están en el diccionario
    for clave, valor in users.items():
        suma+=(conectado[clave]!=1)*valor
             
    # print("Suma: "+str(suma))
 
    return suma

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert disconnected_users([
        ['A', 'B'],
        ['B', 'C'],
        ['C', 'D']
    ], {
        'A': 10,
        'B': 20,
        'C': 30,
        'D': 40
    },
        'A', ['B']) == 90, "First"

    assert disconnected_users([
        ['A', 'B'],
        ['B', 'D'],
        ['A', 'C'],
        ['C', 'D']
    ], {
        'A': 10,
        'B': 0,
        'C': 0,
        'D': 40
    },
        'A', ['B']) == 0, "Second"

    assert disconnected_users([
        ['A', 'B'],
        ['A', 'C'],
        ['A', 'D'],
        ['A', 'E'],
        ['A', 'F']
    ], {
        'A': 10,
        'B': 10,
        'C': 10,
        'D': 10,
        'E': 10,
        'F': 10
    },
        'C', ['A']) == 50, "Third"

    print('Done. Try to check now. There are a lot of other tests')

Repasando python. Expresiones regulares. Resumen 2

https://docs.python.org/2/howto/regex.html
Generador de código: http://hilite.me/

Funciones a nivel de módulo - Module-Levels functions

No es necesario crear patrones. El módulo re también posee las funciones de alto nivel match(), search(), findall(), sub() y otras. Toman los mismos argumentos que los métodos de los patrones, con la expresión regular (RE) como primer argumento y la cadena a nalizar como segundo, devolviendo un objeto match o None.

>>> print re.match(r'From\s+', 'Fromage amk')
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')  
<_sre.SRE_Match object at 0x...>

= = = = = = = = =

Banderas - compilation flags.

Las banderas indican modificaciones de cómo vamos a trabajar con las expresiones regulares. Entre ellas tenemos:

DOTALL, S --> Coincide cualquier carácter, incluso caracteres de líneas nuevas.

I , IGNORECASE --> Representa la coincidencia tipo case-insensitive, es decir, dará coincidencia de letras independientemente si son o no mayúsculas o minúsculas.

L, LOCALE --> hace que \w, \W, \b, and \B dependan del modo local actual. "Locales" son una caracterśitica de la biblioteca de C que intenta ayudar a escribir programas que tienen  en cuenta las diferencias entre varias lenguas. Por ejemplo, \w representa la clase [A-Za-z] pero no tiene en cuenta la "Ñ" o la "ñ". Si el sistema está bien configurado, y el Locale en español está  seleccionado, al activar la bandera LOCALE, el sistema considerará que la Ñ entra dentro de la clase [A-Z]. Activar esta bandera ralentiza algo el programa. 

M, MULTILINE --> ^ produce una coincidencia al principio de la cadena y $ al final de la misma e 

inmediatamente antes de una nueva línea (si existe)al final de la cadena. Cuando esta bandera está 

activa, ^ coincide al principio de la cadena y al principio de cada línea dentro de la cadena. De forma

idéntica, $ coincide al final de la cadena y al final de cada línea (inmediatamente antes del carácter línea nueva).

U,UNICODE --> Hacen a las clases \w, \W, \b, \B, \d, \D, \s y \S depender de la base de datos UNICODE.

X, VERBOSE --> Te permite escribir RE más fáciles de leer. Si lo activo, se ignoran los espacios en 

blanco, excepto cuando forman parte de la misma clase o están antecedidos por \. También permite  escribir comentarios con #

charref = re.compile(r"""
 &[#]                # Start of a numeric entity reference
 (
     0[0-7]+         # Octal form
   | [0-9]+          # Decimal form
   | x[0-9a-fA-F]+   # Hexadecimal form
 )
 ;                   # Trailing semicolon
""", re.VERBOSE)

charref = re.compile("&#(0[0-7]+"
                     "|[0-9]+"
                     "|x[0-9a-fA-F]+);")

Con y sin la bandera re.VERBOSE

= = = = = = = = = 

Más metacaracteres

| --> Operador OR.
^ --> Al principio. A menos que la bandera MULTILINE esté activa, se aplica al principio de la cadena.
Por ejemplo, buscar la palabra From al principio de la cadena:

>>> print re.search('^From', 'From Here to Eternity')
<_sre.SRE_Match object at 0x...>
>>> print re.search('^From', 'Reciting From Memory')
None

$ --> Al final. O justo antes de un carácter de nueva línea.

>>> print re.search('}$', '{block}')
<_sre.SRE_Match object at 0x...>
>>> print re.search('}$', '{block} ')
None
>>> print re.search('}$', '{block}\n')
<_sre.SRE_Match object at 0x...>

\A --> al principio de la cadena como ^ , pero si estoy en el modo multilínea, \A sólo lo hará al principio de la cadena y no al principio de cada línea.
\Z --> Sólo al final de la cadena.
\b --> "Contorno de palabra" Sólo se aplica al final o al principio de una palabra, si está está entre espacios en blanco o signos de puntuación.

Ejemplo:


>>> p = re.compile(r'\bclass\b')
>>> print p.search('no class at all')
<_sre.SRE_Match object at 0x...>
>>> print p.search('the declassified algorithm')
None
>>> print p.search('one subclass is')
None

\b hay que usarlo como literal. Si no puede haber confusión con el carácter BACKSPACE.

\B--> Opuesto a \b

= = = = = = = = = = = 

Agrupando - Grouping

A veces necesitamos más información que sólo si una expresión regular coincide o no. A veces necesitamos que una expresión regular detecte si hay varios grupos de caracteres en una cadena que cumplan determinadas condiciones. Por ejemplo, en la cabecera de un mensaje con el estándar RFC-822, debemos determinar si existen varios grupos separados con nombre:valor.

From: author@example.com
User-Agent: Thunderbird 1.5.0.9 (X11/20061227)
MIME-Version: 1.0
To: editor@example.com

Construimos grupos con los metacaracteres ( y ) , los paréntesis. Y a estos grupos se les puede aplicar los metacaracteres *, + , ? y {m,n}

Por ejemplo, coincidencia del grupo ab repetido...

>>> p = re.compile('(ab)*')
>>> print p.match('ababababab').span()
(0, 10)

Los grupos están indexados. El índice se puede pasar como argumento a group(), start(), end(), and span() y el índice cero, siempre que se cree un grupo, siempre existe. Por lo tanto, el grupo 0 siempre existe y coincide con el resultado de la expresión regular RE. Los métodos de los objetos match siempre tienen cero por índice por defecto.


>>> p = re.compile('(a)b')
>>> m = p.match('ab')
>>> m.group()
'ab'
>>> m.group(0)
'ab'

Los subgrupos se numeran de izquierda a derecha, desde el uno hacia arriba. Para saber el múmero de un subgrupo simplemente cuenta el número de paréntesis de apertura de izquierda a derecha:


>>> p = re.compile('(a(b)c)d')
>>> m = p.match('abcd')
>>> m.group(0)
'abcd'
>>> m.group(1)
'abc'
>>> m.group(2)
'b'

Para que devuelva una lista con todos los grupos: m.groups()
Para que devuelva una lista con grupos específicos: m.group(2,1,3)



Detectar palabras repetidas



>>> p = re.compile(r'\b(\w+)\s+\1\b')
>>> p.search('Paris in the the spring').group()
'the the'

Usamos texto raw para escribir solamente un \ .  La expresión significa:

  • (\w+) grupo de más de 1 carácter alfanumérico. Es el grupo 1
  • \s+ ...seguido de 1 o más espacios en blanco
  • \1 ...seguido del grupo número 1 (las llaman referencias traseras o backreferences)
  • Entre \b y \b, o sea, palabra completa

= = = = = = = = =

Grupos con nombre y no capturados - Non-capturing and Named Groups 

Construir expresiones regulares puede consistir en crear muchos grupos. Bien para destacar subcadenas, o bien para estructurar la expresión regular. A veces es difícil seguir la numeración de los subgrupos. Hay dos características que nos ayudan en este seguimiento; ambas usan una sintaxis común para la extensión de expresiones regulares.

Si en la sintaxis uso (?...) , ? no tiene nada que repetir. Se aprovecha esta circunstancia para indicar (en Perl o Python) que si tenemos (?=foo) 

Si quiero un grupo no-capturado, uso la sintaxis ?: como en el ejemplo...


>>> m = re.match("([abc])+", "abc")
>>> m.groups()
('c',)
>>> m = re.match("(?:[abc])+", "abc")
>>> m.groups()
()

Si quiero un grupo con nombre, necesito la sintaxis (?P<name>...). Por ejemplo:


>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'
>>> m.group(1)
'Lots'

He creado el grupo llamado "palabra", "word".

Y las referencias anteriores que se hacían con números \1  ahora se pueden referenciar con la sintaxis (?P=name)


La expresión regular para encontrar palabras dobles, \b(\w+)\s+\1\b se puede escribir como \b(?P<word>\w+)\s+(?P=word)\b:



>>> p = re.compile(r'\b(?P<word>\w+)\s+(?P=word)\b')
>>> p.search('Paris in the the spring').group()
'the the'

= = = = = = = = = 

Aseveraciones hacia adelante - lookahead assertions

Forma positiva (?=...) y forma negativa (?!...). Tiene éxito si la expresión regular que contiene coincide (positiva) ( o no coincide - negativa) en la posición actual, y falla en caso contrario. 

Por ejemplo, la expresión .*[.].*$ coincidirá con cualquier nombre de fichero punto extensión. "Punto" representa cualquier carácter que se repetirá 0 o más veces más un punto (clase punto [.]) más, visto desde el final de la expresión, repetirá 0 o más veces un carácter .*

Intentemos ahora varios intentos de encontrar ficheros con extensión que no sea bat. Por ejemplo:

.*[.][^b].*$  intento para que el primer carácter no sea "b". Pero claro, también excluye otros ficheros, como prueba.bar.

.*[.]([^b]..|.[^a].|..[^t])$ en esta expresión se intenta que no acepte una extensión cuyo primer carácter es b, cuyo segundo es a y cuyo tercero es t. De acuerdo, no aceptará un fichero bat. Pero tampoco uno del tipo prueba.cf con tan sólo dos caracteres. No sirve.

.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$ Este patrón incluyendo ? toma los segundos caracteres y tercero como opcionales. Vale, lo consigue. Pero es poco legible. Y lo que es peor, si quiero excluir los ficheros con extensión bat y exe, se complicaría aún más.

Pero usando una aseveración negativa: .*[.](?!bat$)[^.]*$  que significa: si no coincide el patrón bat, sigue evaluando. Y llegará a que desde el final encontrará cualesquiera caracteres que no sean puntos.

Si coincide el patrón bat, da una coincidencia negativa y falla. y ahora para incluir la extensión exe ya es fácil: .*[.](?!bat$|exe$)[^.]*$

= = = = = =

Cortando cadenas

.split(string[, maxsplit=0]) --> corta por las coincidencias del patrón. Si maxsplit no es cero, corta las veces que explicita. 


>>> p = re.compile(r'\W+')
>>> p.split('This is a test, short and sweet, of split().')
['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
>>> p.split('This is a test, short and sweet, of split().', 3)
['This', 'is', 'a', 'test, short and sweet, of split().']

Si quiero además que aparezca el delimitador, pongo el patrón entre paréntesis.


>>> p = re.compile(r'\W+')
>>> p2 = re.compile(r'(\W+)') ### Delimitador entre paréntesis.
>>> p.split('This... is a test.')
['This', 'is', 'a', 'test', '']
>>> p2.split('This... is a test.')
['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']

Y como función a nivel de módulo:


>>> re.split('[\W]+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('([\W]+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('[\W]+', 'Words, words, words.', 1)
['Words', 'words, words.']

= = = = = = = = =

Búsqueda y reemplazo

.sub(reemplazo, cadena[, cuenta=0]) --> retorna una cadena que se obtiene sustituyendo el reemplazo en la cadena, según coincida o no con el patrón. Lo hace las veces que se especifique en "cuenta".

>>> p = re.compile('(blue|white|red)')
>>> p.sub('colour', 'blue socks and red shoes')
'colour socks and colour shoes'
>>> p.sub('colour', 'blue socks and red shoes', count=1)
'colour socks and red shoes'

.subn hace lo mismo, pero retorna una tupla con la cadena nueva y las veces que ha realizado el reemplazo.

>>> p = re.compile('(blue|white|red)')
>>> p.subn('colour', 'blue socks and red shoes')
('colour socks and colour shoes', 2)
>>> p.subn('colour', 'no colours at all')
('no colours at all', 0)

Si el reemplazo es una cadena, cualquier carácter \ en ella se procesa. \n se convierte en una nueva línea, \r en retorno de carro y así sucesivamente. Las referencias traseras, como \1, se reemplazan con la correspondiente subcadena correspondiente al grupo de la expresión regular.

En este ejemplo se hace coincidir la palabra section por una cadena cerrada entre llaves y cambiamos section a subsection.


>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First} section{second}')
'subsection{First} subsection{second}'

También hay una sintaxis para referirse a un grupo con nombre en los reemplazos. Todas son equivalentes. Cuidado con la sintaxis \g<1>0 porque puede interpretarse como la '10'


>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<1>}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<name>}','section{First}')
'subsection{First}'

El reemplazo también puede ser una función, el resultado de ella. Se le pasa a la función el objeto match correspondiente. El siguiente ejemplo encuentra números decimales y los sustituye por hexadecimales:


>>> def hexrepl(match):
...     "Return the hex string for a decimal number"
...     value = int(match.group())
...     return hex(value)
...
>>> p = re.compile(r'\d+')
>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
'Call 0xffd2 for printing, 0xc000 for user code.'

Si utilizamos funciones a nivel de módulo re.sub() , el primer parámetro es el patrón.

= = = = = = = = = = =

Algunos problemas

  • Algunas veces el empleo del módulo re está desaconsejado porque es más lento que métodos de cadenas ad hoc. Por ejemplo, si simplemente quiero sustituir una palabra por otra mejor usar el método replace(). O translate() para borrar un carácter o modificarlo por otro.
  • Recordar que match() dará las coincidencias si empieza desde cero, si no hay que usar search().
  • Greedy or not greedy (ávaro o no ávaro): el uso del carácter * es avaricioso. De tal forma que encuentra hasta la última instancia del mismo. Esto a veces da resultados no deseados. Para evitarlo, se usa caracteres no avariciosos como ?
  • # AVARICIOSO
    >>> s = '<html><head><title>Title</title>'
    >>> len(s)
    32
    >>> print re.match('<.*>', s).span()
    (0, 32)
    >>> print re.match('<.*>', s).group()
    <html><head><title>Title</title>
    
    # NO AVARICIOSO
    >>> print re.match('<.*?>', s).group()
    <html>
    
  • Es mejor usar , por mor de la claridad, la bandera re.VERBOSE