Mostrando entradas con la etiqueta Python. Mostrar todas las entradas
Mostrando entradas con la etiqueta Python. Mostrar todas las entradas

11 de octubre de 2020

04. 2 Configurando Geany para ejecutar python3 (SOLO LINUX)

 

Una vez que hemos ejecutado nuestro Hola Mundo!, vamos a configurar geany para ejecutar una versión más moderna de python (http://introtopython.org/programming_environment_linux.html)

Con casi toda seguridad python3 está instalado en el ordenador, pero hay que activarlo en Geany si no lo está.

Configurando Geany para usar Python 3

NOTA: este paso quizás no sea necesario en una instalación de Windows.

Con el programa en Geany anterior de Hola Mundo! cargado en Geany (importante), navega al menú CONSTRUIR >> ESTABLECER COMANDOS DE CONSTRUCCIÓN.

Busca "Comandos de Python" y en la línea "Compilar" sustituye python por python3. Debe aparecer la instrucción python3 -m py_compile "%f"

Y donde aparezca "Comandos de ejecución" en la línea "Execute", también. Debe quedar como python3 "%f"

Prueba otra vez el programa Hola Mundo, pulsando F5.
Nota: como este año hemos cambiado de python a python3, en lo que sigue algunas instrucciones pueden cambiar. ¡¡Atentos/as a los cambios y novedades!!
python3

04. 1 Usando Python3 en mi ordenador con Windows 10

 

Para instalar Python3 en mi ordenador con Windows 10, puedo ver este vídeo. Pero ten en cuenta lo siguiente:


 

  1. No tengas miedo e instala la última versión estable. (Python 3.8.6 - Sept. 24, 2020)
  2. Página de descarga: https://www.python.org/downloads/windows/
  3. Bájate siempre un fichero ejecutable (debe poner executable). Si tu ordenador es de 64 bits debe poner x86-64 y si es de 32 bits sólo x86.
  4. Deberás darle al botón derecho sobre el icono de instalación y ejecutar como administrador.
  5. Para poder utilizar PYTHON3, todavía tienes que instalar GEANY

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!')

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


29 de julio de 2018

Repasando python. Expresiones regulares. Resumen 1


Usando el módulo re (patrones-patterns estilo Perl). Módulo regex para estilo Emacs

Las expresiones regulares son un tipo especial de programación dentro de Python, cuyo objetivo es trabajar con cadenas de caracteres, para buscar coincidencias y así saber si las cadenas que tenemos, por ejemplo, son direcciones, emails, código de algún lenguaje, o multitud de otras aplicaciones. 

Patrones simples - Simple Patterns

Coincidencia de caracteres - matching characters

Por ejemplo, la cadena hola y la cadena hola coinciden exactamente. Si tenemos activado un modo de case-insensitive además coincidirán las cadenas Hola y HoLa exactamente.

En expresiones regulares usamos también caracteres especiales o metacaracteres. Estos metacaracteres representan no un carácter, sino un conjunto de ellos, o una repetición, etc. por lo que no coinciden en principio. Los metacaracteres son:

. ^ $ * + ? { } [ ] \ | ( )

[ ] - Brackets o corchetes.

Indican una clase de caracteres, bien uno a uno, o bien entre un rango (separados por un guión). Por ejemplo, para indicar las letras a, b y c, podemos escribir [abc] o [a-c]. Todas las letras minúsculas pueden ser [a-z].

Si escribo [abc$] aunque $ sea un metacaracter, dentro de una clase, se refiere al caracter $.

Clase complementaria. Uso el metacaracter ^. Por ejemplo, la clase de todos los caracteres excepto el 5 sería [^5].

\ - Backslash o barra diagonal invertida

Este metacaracter es muy importante. Indica que lo que le sigue es una clase especial. Por ejemplo:
  • \d --> Coincide cualquier dígito. Equivale a la clase [0-9].
  • \D --> Coincide cualquier carácter que no sea un dígito. Equivale a [^0-9].
  • \s --> Coincide cualquier carácter "espacio en blanco". Equivale a [ \t\n\r\f\v].
  • \S --> Coincide cualquier carácter que no sea "espacio en blanco". Equivale [^ \t\n\r\f\v].
  • \w --> Coincide cualquier carácter alfanumérico. Equivale a [a-zA-Z0-9_].
  • \W --> Coincide cualquier carácter no alfanumérico. Equivale a [^a-zA-Z0-9_].
También indica que si le sigue un metacaracter estamos analizando ese carácter. Por ejemplo , la referencia a \$ indica el carácter $.

Estas clases pueden formar, a su vez, otras clases. Por ejemplo, [\s,.] indica la clase de los caracteres espacios en blanco más el punto más la coma.

. Punto

El metacaracter punto '.' coincide con cualquier cosa excepto un carácter de nueva línea. El modo alternativo es re.DOTALL (incluso coincidiendo con una nueva línea). A menudo el punto se usa para indicar "cualquier carácter".

= = = = = = = =

Repitiendo cosas - repeating things

No sólo utilizamos las expresiones regulares para las coincidencias, sino para repetir cosas. Por ejemplo, la expresión ca*t (usando el metacaracter asterisco) coincidirá con ct (0 a carácter), cat (1 a), caaat (3 a caracteres). Se lee "a" cualquier número de veces...

Otro carácter de repetición es + (más). Funciona igual que * pero diferencia que no acepta 0 repeticiones del asterisco. Por eso ca+t no tiene coincidencia con ct. Se lee "a" por lo menos una vez...

El carácter ? indica la aparición una 0 cero veces. Por ejemplo home-?brew muestra coincidencia con homebrew y home-brew.


Y la última forma es {a,b}, repeticiones desde a veces hasta b veces. Por ejemplo a\{1,3}b indicará coincidencia con a\b , a\\b y a\\\b. 

* se puede expresar como {0,} , + como {1,} y ? como {0,1}, pero no se suele hacer.



= = = = = = = =

Usando expresiones regulares - Using Regular Expressions


Compilando expresiones regulares
Usamos el módulo re para compilar una expresión regular. 

>>> import re
>>> p = re.compile('ab*')
>>> p  
<_sre.SRE_Pattern object at 0x...>

También acepta banderas como

>>> p = re.compile('ab*', re.IGNORECASE)

Python trata las expresiones regulares como cadenas, y las pasa al módulo programado en C re.


The Backslash Plague - La plaga de la barra diagonal
Ya sabemos que se usa la barra invertida para denotar caracteres especiales o clases. Imaginemos que queremos compilar los caracteres \seccionEn este caso, tenemos que indicar el carácter literal de barra invertida con otra barra antecediéndola \\seccion al introducirlo en re.compile() . Pero si queremos expresarlo como una cadena lietral de Python, necesitamos "\\\\seccion"

Por eso a veces usamos en Python la notación raw: r"ab" (antecediendo con r).

Regular StringRaw string
"ab*"r"ab*"
"\\\\section"r"\\section"
"\\w+\\s+\\1"r"\w+\s+\1"

Representando coincidencias - Performing Matches
Tenemos los siguientes métodos o atributos, entre otros:


Method/AttributePropósito
match()Determina si la expresión regular coincide al principio de la cadena.
search()Busca por toda la cadena, buscando la localización donde coincida
findall()Encuentra todas las subcadenas que coincidan con la expresión y las devuelve en forma de lista.
finditer()
Encuentra todas las subcadenas que coincidan con la expresión y las devuelve en forma de iterador (iterator).

El resultado de un programa con match es o bien None (sin coincidencias) o un objeto tipo match con información sobre la misma.


import re
p = re.compile('[a-z]+')
m = p.match("tiempo")
print m

En este caso el resultado es <_sre.SRE_Match object at 0x7f2749ff3648>

Para extraer información de este objeto, necesitamos varios métodos o atributos. Entre los más importantes tenemos:

Métodos / atributosPropósito
group()Devuelve la cadena que coincide con la expresión regular.
start()Devuelve la posición inicial de la coincidencia.
end()Devuelve la posición final de la coincidencia.
span()Devuelve una tupla con la posición inicial y final.


import re
p = re.compile('[a-z]+')
m = p.match("tiempo")
print m.group()
print m.start(),m.end()
print m.span()

Dando como resultado

tiempo
0 6
(0, 6)

Otro ejemplo


# *-* coding: utf-8 *-*

import re
p = re.compile('[a-z]+')
# en este caso, dará None, ya que busca DESDE el principio de la cadena.
print p.match(":::message")
# En este caso, mostrará el objeto match, y se puede obtener información su aparición.
m=p.search(":::message")
print m
print m.group()
print m.span()

Dando como resultado

None
<_sre.SRE_Match object at 0x7fa9a11f3648>
message
(3, 10)


findall() devuelve una lista de cadenas coincidentes


>>> p = re.compile('\d+')

>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords fQa-leaping')

['12', '11', '10']

findall() tiene que crear la lista completa antes de devolverla como el resultado. El método finditer() devuelve objetos tipo match que pueden ser iterados.


>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator 
<callable-iterator object at 0x...>
>>> for match in iterator:
...     print match.span()
...
(0, 2)
(22, 24)
(29, 31)


28 de julio de 2018

Repasando python. Solución a "Stressful subject - SENDGRID"

def is_stressful(subj):
    """
        recoognise stressful subject       
    """
    stwords=["help","asap","urgent"]
    condition = False
    if subj==subj.upper(): condition = True
    if subj[-3:]=="!!!": condition = True
    # filtrando caracteres y quedándose con letras
    newSubj1=[]
    for a in subj.upper():
        if a.isalpha():
            newSubj1.append(a)
    # Quitando caracteres repetidos
    newSubj2=[]
    for indice, a in enumerate(newSubj1):
        if not (indice>1 and a==newSubj1[indice-1]):
            newSubj2.append(a)
    newSubj2Str="".join(newSubj2)
    print(newSubj2Str)
    # Reconociendo palabras de stwords
    for a in stwords:
        if newSubj2Str.find(a.upper())>=0:
            condition = True
    return condition

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert is_stressful("Aquí hay una HHHELLLLPPPP!!!")==True,"Mine"
    assert is_stressful("Hi") == False, "First"
    assert is_stressful("I neeed HELP") == True, "Second"
    print('Done! Go Check it!')

3 de marzo de 2017

Cálculo de expresiones algebraicas en Python - infix a postfix

Programa "definitivo" que convierte expresiones algebraicas infix a postfix (rpn) y algoritmo de cálculo. 

Ayuda:
  1. Aunque reconoce las funciones de un número sin paréntesis se recomienda usarlos. Por ejemplo, sin(3) en vez de sin3
  2.  Los operadores que pueden usarse son  "+","-","*","/","^" y los paréntesis "(" y ")".
  3. El programa pide al inicio si necesitamos un cálculo con grados (escribe g) o radianes (escribe r).
  4. Listado de funciones: "sin","cos","tan","sec","cosec","cotan","ln","log","exp","sqr","abs","asin","acos","atan"
  5. Reconoce dos constantes: pi y e
  6. Reconoce la notación con exponente: 5*10e4 = 50000. O bien, 5e4. Pero no funcionará con negativos como 5e-4. En ese caso escribir 5*10e-4
  7. En general es preferible usar paréntesis.
  8. El resultado se presenta con 4 decimales.
  9. Puede contener aún algún error.
= = = 

# *-* coding: utf-8 *-*

import math

# http://csis.pace.edu/~wolf/CS122/infix-postfix.htm
# http://hawkee.com/snippet/16636/

# Funciones

def esFloat(a):
    try:
        float(a)
        return True
    except:
        return False

# Fin de funciones

radOGrados=raw_input("Pulsa r para radianes y g para grados: ")
radOGrados=radOGrados[0:1].lower()

if radOGrados=="g":
    factor=4*math.atan(1)/180
else:
    factor=1

seguir = True
while seguir:
  
    cadena=raw_input("Introduce una cadena con operadores válida: ")

    operadores=["+","-","*","/","^","(",")","sin","cos","tan","sec","cosec","cotan","ln","log","exp","sqr","abs","asin","acos","atan"]
    prefoper=[0,0,1,1,2,-1,-1,3,3,3,3,3,3,3,3,3,3,3,3,3,3]
    stack=[]
    postfix=""

    # =====================================
    # Pretratamiento para números negativos
    # =====================================
    cadena2=""
    previo=""
    bandera = 0
    for i in cadena:
        if ((i=="-" or i=="+") and (previo in operadores or previo=="" or not esFloat(previo))
            and previo!="e" and previo!=")" and bandera==0):
            cadena2+="(0"+i
            bandera=1
        elif i in operadores and bandera==1:
            cadena2+=")"+i
            bandera=0
        else:
            cadena2+=i
        previo = i

    if bandera==1:
        cadena2+=")"
    # ======================
    # Fin del pretratamiento
    # ======================

    print "Esta es la cadena a convertir: %s" % (cadena2)

    bandera = 0

    for count, i in enumerate(cadena2):
     
        if bandera>0:
            bandera-=1 # va restando 1
            continue # permite leer funciones
        
        if i in operadores and prefoper[operadores.index(i)]>=0: # todos menos los paréntesis
            indice = operadores.index(i) # indice del elemento nuevo
            # la 5ª regla, la potencia, se consigue con un while en la siguiente en vez de un if.
            while len(stack)>=1 and prefoper[indice]<=prefoper[operadores.index(stack[len(stack)-1])]:
                # y la preferencia del operador es mayor que la del último del stack
                # el igual incluye la asociación, 4ª regla
                postfix+=" "+stack[len(stack)-1] # añade el último al postfix
                stack.pop() # quita de la pila el último
            stack.append(i) # añade el operador al stack
            postfix+=" "
        elif (cadena2[count:count+3]=="sin"
            or cadena2[count:count+3]=="cos"
            or cadena2[count:count+3]=="tan"
            or cadena2[count:count+3]=="sec"
            or cadena2[count:count+3]=="log"
            or cadena2[count:count+3]=="exp"
            or cadena2[count:count+3]=="abs"
            or cadena2[count:count+3]=="sqr"): # si son estas funciones, las añade
            stack.append(cadena2[count:count+3])
            bandera=len(cadena2[count:count+3])-1
        elif (cadena2[count:count+4]=="asin"
            or cadena2[count:count+4]=="atan"
            or cadena2[count:count+4]=="acos"): # si son estas más largas, las añade también
            stack.append(cadena2[count:count+4])
            bandera=len(cadena2[count:count+4])-1
        elif (cadena2[count:count+5]=="cosec"
            or cadena2[count:count+5]=="cotan"): # si son estas más largas, las añade también
            stack.append(cadena2[count:count+5])
            bandera=len(cadena2[count:count+5])-1
        elif cadena2[count:count+2]=="ln": # si es el ln
            stack.append(cadena2[count:count+2])
            bandera=len(cadena2[count:count+2])-1
        elif cadena2[count:count+2].lower()=="pi": # si es el número pi
            postfix+=str(4*math.atan(1))
            bandera=len(cadena2[count:count+2])-1
        elif (cadena2[count:count+4].lower()=="10e-"
            or cadena2[count:count+4].lower()=="10e+"): # si es exponente positivo o negativo
            postfix+="1"+cadena[count+2:count+4]
            bandera=len(cadena2[count:count+4])-1
        elif cadena2[count:count+3].lower()=="10e": # si es exponente positivo sin el signo más
            postfix+="1e"
            bandera=len(cadena2[count:count+3])-1
        elif i=="e" and (cadena2[count+1:count+2] in operadores[0:7] or cadena2[count+1:count+2]==""): # número e
            postfix+=str(math.exp(1))
        elif i=="(":
            stack.append("(") # añade el paréntesis al stack
        elif i==")":
            while len(stack)>1 and stack[len(stack)-1]!="(":
                postfix+=" "+stack[len(stack)-1] # añade al postfix
                stack.pop() # lo quita de la pila
            if stack[len(stack)-1]=="(":
                stack.pop() # lo quita de la pila
        else: # si no está en operadores o no son paréntesis
            postfix+=i
        # imprime la lectura de la cadena, la posición, la cadena postfix y el stack
        # print cadena2.index(i), i, stack, postfix

    # termina añadiendo el stack a la cadena postfix
    while len(stack)>=1:
        if prefoper[operadores.index(stack[len(stack)-1])]>=0:
            postfix+=" "+stack[len(stack)-1]
        stack.pop()

    print "La cadena postfix: %s" % (postfix)

    # ============================
    # Cálculo de la cadena postfix
    # ============================

    pila=[]
    pila=postfix.split(" ")
    calculo=[]
    # print pila

    while len(pila)>0:
     
        item = pila.pop(0)
        # print item
     
        if item=="+" and len(calculo)>1:
            calculo.append(calculo.pop()+calculo.pop())
        elif item=="-" and len(calculo)>1:
            tmp = calculo.pop()
            calculo.append(calculo.pop()-tmp)
        elif item=="*" and len(calculo)>1:
            calculo.append(calculo.pop()*calculo.pop())
        elif item=="/" and len(calculo)>1:
            tmp = calculo.pop()
            calculo.append(calculo.pop()/tmp)
        elif item=="^" and len(calculo)>1:
            tmp = calculo.pop()
            calculo.append(pow(calculo.pop(),tmp))
        elif item=="sin":
            calculo.append(math.sin(factor*calculo.pop()))
        elif item=="cos":
            calculo.append(math.cos(factor*calculo.pop()))
        elif item=="tan":
            calculo.append(math.tan(factor*calculo.pop()))
        elif item=="sec":
            calculo.append(1/math.cos(factor*calculo.pop()))
        elif item=="cosec":
            calculo.append(1/math.sin(factor*calculo.pop()))
        elif item=="cotan":
            calculo.append(1/math.tan(factor*calculo.pop()))
        elif item=="ln":
            calculo.append(math.log(calculo.pop()))
        elif item=="log":
            calculo.append(math.log10(calculo.pop()))
        elif item=="exp":
            calculo.append(math.exp(calculo.pop()))
        elif item=="asin":
            calculo.append(math.asin(calculo.pop())/factor)
        elif item=="acos":
            calculo.append(math.acos(calculo.pop())/factor)
        elif item=="atan":
            calculo.append(math.atan(calculo.pop())/factor)
        elif item=="abs":
            calculo.append(abs(calculo.pop()))
        elif item=="sqr":
            calculo.append(pow(calculo.pop(),0.5))
        elif esFloat(item):
            calculo.append(float(item))
        else:
            pass
        
        print calculo
     
    print "El resultado es: %.4f" % (calculo[0])
   
    seguir2 = raw_input("¿Quieres hacer otro cálculo? [s,n] ")
    if seguir2.lower()=="n":
        seguir = False