miércoles, 26 de agosto de 2015

Bucles eficientes con Itertools



Itertools es un módulo de la librería estándar de Python que incorpora funciones que devuelven objetos iterables, es decir, estructuras de datos basadas en elementos que pueden recorrerse secuencialmente y que pueden utilizarse en procesos repetitivos (bucles).

Estas funciones fueron diseñadas para una ejecución rápida, haciendo un uso eficiente de la memoria, con la idea de resolver algoritmos basados en bucles más complicados que aquellos que habitualmente se suelen implementar en un programa para recorrer los elementos de una lista, diccionario, etc.

Funciones que devuelven iterables infinitos


Agrupa un conjunto de funciones Itertools que devuelven un iterable que no se interrumpirá si no se fuerza un final, por ejemplo, cuando se cumpla una determinada condición.

count()


Devuelve un objeto iterable en la que el primer elemento tendrá el valor inicial (start) y los sucesivos se irán incrementando/decrementado con el valor del paso (step), de manera ininterrumpida.

itertools.count(start=0 [, step=1])

En el siguiente ejemplo cuando se alcanza un valor determinado se fuerza la interrupción del bucle:

from itertools import *
for valor in count(5, 3):
    print(valor, end = ' ')
    if valor == 20: break

Salida: 5 8 11 14 17 20

cycle()


Devuelve un objeto iterable con los elementos (de principio a fin) del iterable de entrada, que se reproducirán una y otra vez mientras no se fuerce un final.

itertools.cycle(iterable)

A continuación, varios ejemplos:

cycle() con una cadena:

contador = 0 
for elemento in cycle("Python"):
    print(elemento, end = ' ')
    contador += 1
    if contador == 12: break

Salida: P y t h o n P y t h o n

cycle() con una lista:

contador = 0
for elemento in cycle([10, 12, 14]):
    print(elemento, end = ' ')
    contador += 1
    if contador == 5: break

Salida: 10 12 14 10 12

cycle() con un diccionario:

contador = 0
for elemento in cycle({'x':1, 'y':2, 'z': 3}):
    print(elemento, end = ' ')
    contador += 1
    if contador == 9: break

Salida: x z y x z y x z y

repeat()


Devuelve el objeto completo, una y otra vez, de manera indefinida a menos que se especifique el número de veces (times) que hay que ejecutar el bucle.

itertools.repeat(object[, times])

A continuación, varios ejemplos:

repeat() con un entero:

for elemento in repeat(3, 5):
    print(elemento, end = ' ')

Salida: 3 3 3 3 3

repeat() con map():

print(list(map(pow, range(5), repeat(3))))

Salida: [0, 1, 8, 27, 64]

Funciones que devuelven iterables que finalizan


Agrupa funciones del módulo Itertools que devuelven iterables que finalizan con la secuencia de entrada más corta.


accumulate()


La función devuelve un iterable con sumas acumuladas o totales acumulados derivados de los resultados obtenidos al aplicar alguna función (func).

Los elementos del objeto iterable se evalúan tomando el primer elemento con el segundo; después el resultado obtenido de ambos elementos con el tercero y así sucesivamente.

itertools.accumulate(iterable[, func])

La función que se incluya debe tener dos argumentos (uno por cada elemento que se evalúa) y los elementos de la entrada iterable, necesariamente, serán del tipo que acepte dicha función. Además, si la entrada iterable está vacía la salida también lo estará.

A continuación, varios ejemplos:

accumulate() con la función implícita que acumula sumas:

for acumulado in accumulate([1, 2, 3, 4, 5]):
    print(acumulado, end = ' ')

Salida: 1 3 6 10 15

accumulate() con función max para 'acumular' el valor máximo:

for valor_maximo in accumulate([1, 3, 2, 5, 4], max):
    print(valor_maximo, end = ' ')

Salida: 1 3 3 5 5

acumulate() con función lambda:

for diferencia in accumulate([10, 30, 50], lambda a, b: b-a):
    print(diferencia, end = ' ')

Salida: 10 20 30

chain()


La función devuelve un iterable construido con todos los elementos de los objetos iterables de entrada.

itertools.chain(*iterables)


for elemento in chain([1, 2], [3, 4, 5]):
    print(elemento ** 2, end = ' ')

Salida: 1 4 9 16 25

chain.from_iterable()


Es similar a chain() pero solo admite un argumento iterable.

classmethod chain.from_iterable(iterable)


for elemento in chain.from_iterable(["un","dos","tres"]):
    print(elemento, end = ' ')

Salida: u n d o s t r e s

compress()


La función devuelve un iterable a partir de los elementos de data que tienen cada uno un valor asociado en selectors que actúan como un filtro. Todos los elementos con el valor True o 1 serán los utilizados para construir el iterable. El proceso finaliza cuando se agoten los elementos en data o en selectors.

itertools.compress(data, selectors)


for elemento in compress("iterable", [1,0,1,0,1,0,0,1]):
    print(elemento, end = ' ')

Salida: i e a e

dropwhile()


Construye un iterable a partir del iterable de entrada sin devolver ningún elemento hasta que la condición del predicado sea falsa. Después de ese momento se devuelven todos los elementos que resten.

dropwhile(predicate, iterable)


for elemento in dropwhile(lambda valor: valor == 'x', 
                          ['x','x','y','z','x','x']):
    print(elemento, end = ' ')    

Salida: y z x x

filterfalse()


Esta función devuelve un iterable con los elementos del iterable de entrada que no cumplan la condición expresada en el predicado. Si el predicado es None devuelve los elementos con valor False o 0.

itertools.filterfalse(predicate, iterable)


for elemento in filterfalse(lambda valor: valor == 'x', 
                            ['x','x','y','z','x','x']):
    print(elemento, end = ' ')

Salida: y z

groupby()


Devuelve un iterable con los elementos del iterable de entrada agrupados por el dato utilizado como clave (key).

itertools.groupby(iterable, key=None)

A continuación, varios ejemplos:

groupby() con lista ordenada:

En el siguiente ejemplo la lista de tuplas contiene ciudades de diferentes países ordenados alfabéticamente:


ciudades = [("Bolivia", "Sucre"), ("Bolivia", "La Paz"), 
            ("Chile", "Valdivia"), ("Chile", "Arica"), 
            ("España", "Cádiz"), ("Perú", "Cusco"), 
            ("Perú", "Lima")]

for clave, grupo in groupby(ciudades, lambda x: x[0]):
    print(clave, list(grupo))

Salida:
Bolivia [('Bolivia', 'Sucre'), ('Bolivia', 'La Paz')]
Chile [('Chile', 'Valdivia'), ('Chile', 'Arica')]
España [('España', 'Cádiz')]
Perú [('Perú', 'Cusco'), ('Perú', 'Lima')]


groupby() y itemgetter() con lista desordenada:

En el siguiente ejemplo la lista de tuplas contiene ciudades de diferentes países que no están ordenados alfabéticamente.

Para que se pueda agrupar la información de cada país es necesario ordenar la lista. Para ello se utiliza la función sorted() con la función itemgetter() del módulo operator. Esta función permite establecer el criterio de ordenación. En el ejemplo, como cada tupla contiene dos elementos (país, ciudad) itemgetter(0) establece que el criterio de orden será el primer elemento de la tupla, es decir, el país.


from operator import itemgetter
ciudades = [("Perú", "Cusco"), ("Chile", "Valdivia"), 
            ("Bolivia", "Sucre"), ("Bolivia", "La Paz"), 
            ("España", "Cádiz"), ("Chile", "Arica"), 
            ("Perú", "Lima")]

ciudades = sorted(ciudades, key=itemgetter(0))
for clave, grupo in groupby(ciudades, itemgetter(0)):
    print(clave, list(grupo))

Salida:
Bolivia [('Bolivia', 'Sucre'), ('Bolivia', 'La Paz')]
Chile [('Chile', 'Valdivia'), ('Chile', 'Arica')]
España [('España', 'Cádiz')]
Perú [('Perú', 'Cusco'), ('Perú', 'Lima')]


islice()


La función devuelve un iterable con una selección de elementos del iterable de entrada. Permite retornar un número de elementos partiendo desde el inicio del iterable o un rango específico.

itertools.islice(iterable, stop)

A continuación, algunos ejemplos:

Devuelve elementos partiendo desde el comienzo:


for elemento in islice("KLMNOPQRST", 5):
    print(elemento, end = ' ')

Salida: K L M N O

itertools.islice(iterable, start, stop [, step])

Devuelve los elementos que hay desde una posición inicial a una final:

for elemento in islice("KLMNOPQRST", 5, 7):
    print(elemento, end = ' ') 

Salida: P Q

Devuelve los elementos que hay desde una posición inicial a una final, separados entre sí por un valor fijo de elementos:


for elemento in islice("1234567890", 0, 8, 2):
    print(elemento, end = ' ') 

Salida: 1 3 5 7

starmap()


Construye un objeto iterable aplicando una función que utiliza como argumentos los elementos del iterable de entrada, devolviendo una secuencia con los resultados obtenidos.

itertools.starmap(function, iterable)

En el ejemplo se recorre una lista de tuplas y se construye el iterable a devolver con el valor más alto de los elementos que hay en cada una de ellas:


for elemento in starmap(max, 
                        [(10,2),(2,32),(63,54),(4,45)]):
    print(elemento, end = ' ') 

Salida: 10 32 63 45

takewhile()


Construye un iterable a partir del iterable de entrada devolviendo elementos mientras la condición del predicado es verdadera. En el momento que cambie a falsa no devolverá más elementos aunque exista alguno que cumpla la condición.

itertools.takewhile(predicate, iterable)

En el ejemplo se recorre una lista de cadenas comprobando si su longitud es igual a 1:


for elemento in takewhile(lambda x: len(x) == 1, 
                          ['a','b','ab','bc','c']):
    print(elemento, end = ' ')

Salida: a b

tee()


Devuelve varios iterables independientes (por defecto, 2) sobre la base de una sola entrada original.

itertools.tee(iterable, n=2)


a, b = tee([1,2,3,4])
for elemento1, elemento2 in zip(a, b):
    print("a:", elemento1)
    print("b:", elemento2)

Salida:
a: 1
b: 1
a: 2
b: 2
a: 3
b: 3


zip_longest()


Devuelve un iterable que agrega elementos de cada uno de los iterables de entrada. Si los iterables de entrada tienen distinta longitud se completará utilizando el valor de fillvalue, hasta que se alcance el final del iterable que tenga una longitud mayor.

itertools.zip_longest(*iterables, fillvalue=None)


for elemento in zip_longest(['x','y','z'], ['0','1'], 
                            fillvalue='#'):
    print(elemento, end = ' ')

Salida: ('x', '0') ('y', '1') ('z', '#')

Generadores para combinatoria


Agrupa una serie de funciones que generan iterables como resultado de operaciones de combinatoria en las que se utilizan otros iterables de entrada.

product()


Realiza el producto cartesiano con los elementos de los iterables de entrada devolviendo un iterable basado en tuplas con el resultado.

itertools.product(*iterables, repeat=1)


for elemento in product("XYZ", "mn"):
    print(elemento, end = ' ')

Salida: ('X', 'm') ('X', 'n') ('Y', 'm') ('Y', 'n') ('Z', 'm') ('Z', 'n')

for elemento in product("X", repeat = 4):
    print(elemento, end = ' ')

Salida: ('X', 'X', 'X', 'X')

for elemento in product("XYZ", repeat = 2):
    print(elemento, end = ' ')

Salida: ('X', 'X') ('X', 'Y') ('X', 'Z') ('Y', 'X') ('Y', 'Y') ('Y', 'Z') ('Z', 'X') ('Z', 'Y') ('Z', 'Z')

permutations()


Devuelve un objeto iterable basado en tuplas con permutaciones de longitud r a partir de los elementos del objeto de entrada.

itertools.permutations(iterable, r=None)


for elemento in permutations("123", 2):
    print(elemento, end = ' ')

Salida: ('1', '2') ('1', '3') ('2', '1') ('2', '3') ('3', '1') ('3', '2')

combinations()


Devuelve un objeto iterable basado en tuplas con las combinaciones sin repetición posibles, de una longitud r, a partir de los elementos del objeto de entrada.

itertools.combinations(iterable, r)


for elemento in combinations("123", 2):
    print(elemento, end = ' ')

Salida: ('1', '2') ('1', '3') ('2', '3')

combinations_with_replacement()


Devuelve un objeto iterable basado en tuplas con las combinaciones con repetición posibles, de una longitud r, a partir de los elementos del objeto de entrada.

itertools.combinations_with_replacement(iterable, r)


for elemento in combinations_with_replacement("123", 2):
    print(elemento, end = ' ')

Salida: ('1', '1') ('1', '2') ('1', '3') ('2', '2') ('2', '3') ('3', '3')


Ir al índice del tutorial de Python

sábado, 1 de agosto de 2015

Buscar, extraer y depurar con Pysaurio


Pysaurio es un módulo para Python 3.x que desarrollamos para el caso práctico del artículo "Empaquetado y distribución de un proyecto Python" publicado en este blog.

Pysaurio permite buscar, extraer y depurar información de un conjunto de archivos del mismo tipo; y crear con toda la información seleccionada un archivo, por ejemplo, del tipo CSV.

La información en los archivos a explorar puede estar dispuesta en varias filas:

PC01.txt:
User=ms123
Name=Mayra Sanz
OS=GNU/Linux
IP=10.226.140.1


Y también puede estar organizada en varias columnas:

PC01.txt:
User: ms123        Name: Mayra Sanz
OS: GNU/Linux   IP: 10.226.140.1


En resumen, Pysaurio, a partir de los datos de los siguientes archivos:

PC01.txt:
User=ms123
Name=Mayra Sanz
OS=GNU/Linux
IP=10.226.140.1


PC02.txt:
User=lt001
Name=Luis Toribio
OS=GNU/Linux
IP=10.226.140.2


PC03.txt:
User=co205
Name=Clara Osto
OS=Win
IP=10.226.140.3


...puede construir un único archivo CSV con el siguiente contenido:

User,Name,OS,IP
MS123,Mayra Sanz,GNU/Linux,10.226.140.1
LT001,Luis Toribio,GNU/Linux,10.226.140.2
CO205,Clara Osto,Win,10.226.140.3


En el ejemplo el archivo generado está organizado en columnas con un encabezado y se podría abrir con programas que trabajan con hojas de cálculo como Calc, GNumeric y Excel. Este proceso de consolidación de la información puede facilitar el análisis de datos que se almacenan en muchos archivos independientes.

Para instalar Pysaurio con pip:

$ pip3 install pysaurio

Plantillas .rap


Para procesar archivos con Pysaurio es necesario crear una plantilla .rap que tiene una estructura similar a un archivo INI. Dicha plantilla contendrá una descripción del proceso de búsqueda, extracción y depuración que tendrá que ejecutarse cuando la plantilla se abra. Pysaurio incorpora métodos para crear y abrir las plantillas.

Para el ejemplo que hemos comentado la plantilla .rap tendría el siguiente contenido:

[General]
description = Obtener lista de usuarios
extension = txt
prefix = PC
output_folder = txt
input_folder = txt
output_file = users.csv
delimiter = ,
quotechar = "
include_header = 1
include_file = 0
search_multiple = 0


[Fields]
user = User=
name = Name=
os = OS=
ip = IP=


[Rules]
rule1 = ('user', 'UPPER')

Secciones de una plantilla .rap


Un archivo .rap se divide en las siguientes secciones:


[General]

Esta sección es obligatoria y puede contener los siguientes atributos:
   
  • description: descripción breve de la plantilla.
  • extension: extensión de los archivos a rastrear (.txt, .log, ...).
  • prefix: cadena de comienzo de los nombres de los archivos.
  • input_folder: directorio donde se encuentran los archivos a rastrear.
  • output_folder: directorio donde se guardará el archivo de salida.
  • output_file: nombre del archivo de salida (.csv, .html, .txt, ...).
  • delimiter: carácter utilizado como separador de campos (, ; . | ...).
  • quotechar: carácter utilizado para indicar el inicio y fin de un campo cuando contenga el carácter expresado como separador de campos (' " ...).
  • include_header: establece que la salida tenga o no encabezado ('0' o '1').
  • include_file: establece que la salida incluya o no un campo con el nombre del archivo de donde se extrajeron los datos ('0' o '1').
  • search_multiple: establece que en una línea puede aparecer o no más de un campo ('0' or '1').

[Fields]

Esta sección es obligatoria y contendrá una línea por cada campo del fichero de salida.

Cada campo se expresará como una asignación: a la izquierda del signo '=' se escribirá el nombre del campo y a la derecha la cadena que tendrá que buscarse en los ficheros para localizar la información: 

nombre_campo = cadena_de_búsqueda

Ejemplo:

user = user=

Pyrsaurio inicialmente leerá todo lo que haya a la derecha de la cadena buscada, hasta alcanzar el final de línea. Y con posterioridad tendremos la posibilidad de depurar la información capturada con reglas: obteniendo una subcadena de una longitud determinada, buscando o sustituyendo caracteres, borrando información desde/hasta un lugar determinado, etc.

Si en la sección [General] el atributo search_multiple tiene el valor '1' el sistema podrá encontrar más de un campo en una misma línea. Esto implica que después de la captura será necesario aplicar alguna regla para adaptar la información encontrada.

Si el atributo search_multiple tiene el valor '0' cuando se encuentre un campo en una línea ya no buscará el resto de campos y así Pysaurio funcionará más rápido.


[Rules]

Esta sección es opcional y si está presente contendrá las reglas que se aplicarán a los datos encontrados, pudiendo haber más de una regla por campo y pudiendo no aparecer campos a los que no sean necesarios aplicarle ninguna.

Como se ha comentado con anterioridad el uso de reglas será imprescindible cuando se busquen datos de varios campos en una misma línea. En una línea multicampo si un primer campo buscado se encuentra al comienzo de la línea, como dentro de la información capturada estará el resto de la línea, si hubiera más campos, estarían incluidos en ella. Con posterioridad, se aplicarían las reglas necesarias para extraer la información requerida, eliminar la innecesaria, etc., adaptando la información a cada necesidad.

Cada regla se expresará como una asignación: a la izquierda del signo '=' se escribirá el identificador de la regla (rule1, rule2, etc) y a la derecha una tupla que contendrá el nombre del campo al que se aplica, la función que se aplica y los argumentos que sean necesarios:

rule1 = ('user', 'UPPER')

Funciones para reglas (rules)


  • SUBSTR: Extrae del dato una subcadena desde una posición y con una longitud determinada. rule1 = (nombre_campo, 'SUBSTR', posición_inicial, longitud)
  • REPLACE: Busca una cadena en el dato y la sustituye por otra. rule1 = (nombre_campo, 'REPLACE', cadena_búsqueda, cadena_sustitución)
  • UPPER: Convierte a mayúsculas el dato. rule1 = (nombre_campo, 'UPPER')
  • LOWER: Convierte a minúsculas el dato. rule1 = (nombre_campo, 'LOWER')
  • REVERSE: Invierte el orden de los caracteres que aparecen en el dato. rule1 = (nombre_campo, 'REVERSE')
  • REMOVE: Borra el dato. rule1 = (nombre_campo, 'REMOVE')
  • FIELDISDATA: Cambia el dato encontrado por el nombre del campo. rule1 = (nombre_campo, 'FIELDISDATA')
  • REMOVEFROM: Borra del dato toda la información desde la cadena indicada. rule1 = (nombre_campo, 'REMOVEFROM', cadena)
  • REMOVETO: Borra del dato toda la información hasta la cadena indicada. rule1 = (nombre_campo, 'REMOVETO', cadena)  

Métodos de la clase Raptor


- __init__(self): Inicializa un objeto plantilla .rap
   
     
    >>> from pysaurio import Raptor              
    >>> rap1 = Raptor()  


- Save(self, name): Guarda una plantilla .rap
     
    name -- nombre del archivo .rap
    
    >>> rap1.Save('template.rap')

- Open(self, name): Abre una plantilla .rap
    
    name -- nombre del archivo .rap
    
    >>> rap1.Open('template.rap')

- ApplyRules(self, rec): Aplica reglas a los datos de los campos
    
    rec -- Diccionario Python con nombres de campos y valores
    
    Retorna diccionario Python con nombres de campos y valores actualizados
    
    >>> rap1.ApplyRules(Dict)

- BuildHeader(self): Construye encabezado
    
    Retorna lista con nombre de campos
    
    >>> rap1.BuildHeader()

- BuildRow(self, row): Construye el registro de un archivo de entrada
    
    row -- nombre del archivo
    
    Retorna diccionario Python con nombres de campos y valores.
    
    >>> rap1.BuildRow('filename') 

- ShowError(self): Muestra errores

    Retorna una cadena con los errores producidos al abrir o guardar una plantilla
    
    >>> rap1.ShowError()


Generar archivo .CSV (campos en filas)


En el siguiente ejemplo se muestra el modo de crear y después abrir una plantilla .rap para generar el archivo CSV que comentamos al principio de este artículo. La información está ordenada en filas y los archivos a procesar se encontrarían en el directorio 'txt' al mismo nivel que el script create_csv.py:

create_csv.py
from pysaurio import Raptor
import csv
  
def main():
 
 # Crear y guardar una plantilla .rap
     
 rap1 = Raptor()  
 rap1.description = 'Obtener lista de usuarios'
 rap1.extension = 'txt'
 rap1.prefix = 'PC'
 rap1.input_folder = 'txt'
 rap1.output_folder = 'txt'
 rap1.output_file = 'users.csv'
 rap1.delimiter = ','
 rap1.quotechar = '"'
 rap1.include_header = '1'
 rap1.include_file = '0'
 rap1.search_multiple = '0'
 rap1.fields['user'] = 'User='
 rap1.fields['name'] = 'Name='
 rap1.fields['os'] = 'OS='
 rap1.fields['ip'] = 'IP='
 rap1.rules.append(('user', 'UPPER')) 
 rap1.Save("users.rap")
 del rap1

 # Abrir la plantilla .rap y generar el archivo .csv
 
 rap2 = Raptor()
 rap2.Open('users.rap')
 if rap2.number_errors == 0:   
  file_csv = open(rap2.output_file, 'w', newline='')
  csv_output = csv.writer(file_csv, 
     delimiter=rap2.delimiter,
     quotechar=rap2.quotechar, 
     quoting=csv.QUOTE_MINIMAL)
  if rap2.include_header == '1':
   fields_list = rap2.BuildHeader()
   print(fields_list)
   csv_output.writerow(fields_list)
       
  for row in rap2.list_files:
   new_record = rap2.ApplyRules(rap2.BuildRow(row))   
   new_record = list(new_record.values())
   print(new_record)
   csv_output.writerow(new_record)   
  file_csv.close()   
 else:
  print(rap2.ShowError())
 del rap2
 return 0

if __name__ == '__main__':
 main()

Además de los atributos de la sección [General] en un programa se podrían consultar también los siguientes:
   
  • fields: diccionario Python que contendrá nombre de campos y cadenas de búsqueda, es decir, lo declarado en la sección [Fields] de un archivo .rap.
  • record: cuando se procesa una lista de archivos, contendrá para un archivo determinado, la lista de campos encontrados con sus respectivos valores.
  • rules: lista de reglas declarada en la sección [Rules] de un archivo .rap.
  • list_files: lista de archivos a procesar.
  • errors: lista con mensajes de errores al abrir o guardar una plantilla .rap
  • number_errors: número de errores producidos al abrir o guardar una plantilla .rap.

Generar archivo .CSV (campos en columnas)


En el siguiente ejemplo se muestra el modo de crear un archivo CSV a partir de ficheros con los campos distribuidos en columnas. Los archivos a procesar serían los siguientes:

PC01.log:
User=ms123       Name=Mayra Sanz
OS=GNU/Linux     IP=10.226.140.1


PC02.log:

User=lt001       Name=Luis Toribio
OS=GNU/Linux     IP=10.226.140.2


PC03.log:
User=co205       Name=Clara Osto
OS=Win           IP=10.226.140.3



create_csv_from_columns.py
from pysaurio import Raptor
import csv
  
def main():
 
 # Crear y guardar una nueva plantilla .rap
     
 rap1 = Raptor()  
 rap1.description = 'Obtener lista de usuarios con datos en columnas'
 rap1.extension = 'log'
 rap1.prefix = 'PC'
 rap1.input_folder = 'txt'
 rap1.output_folder = 'txt'
 rap1.output_file = 'users_from_columns.csv'
 rap1.delimiter = ','
 rap1.quotechar = '"'
 rap1.include_header = '1'
 rap1.include_file = '0'
 rap1.search_multiple = '1'
 rap1.fields['user'] = 'User='
 rap1.fields['name'] = 'Name='
 rap1.fields['os'] = 'OS='
 rap1.fields['ip'] = 'IP='
 rap1.rules.append(('user', 'UPPER'))
 rap1.rules.append(('user', 'SUBSTR', 1, 6))
 rap1.rules.append(('os', 'REMOVEFROM', ' '))
 rap1.Save("users_columns.rap")
 del rap1

 # Abrir la plantilla .rap y generar el archivo .csv
 
 rap2 = Raptor()
 rap2.Open('users_columns.rap')
 if rap2.number_errors == 0:   
  file_csv = open(rap2.output_file, 'w', newline='')
  csv_output = csv.writer(file_csv, 
     delimiter=rap2.delimiter,
     quotechar=rap2.quotechar, 
     quoting=csv.QUOTE_MINIMAL)
  if rap2.include_header == '1':
   fields_list = rap2.BuildHeader()
   print(fields_list)
   csv_output.writerow(fields_list)
       
  for row in rap2.list_files:
   new_record = rap2.ApplyRules(rap2.BuildRow(row))   
   new_record = list(new_record.values())
   print(new_record)
   csv_output.writerow(new_record)   
  file_csv.close()   
 else:
  print(rap2.ShowError())
 del rap2
 return 0

if __name__ == '__main__':
 main()

Generar varios archivos .csv utilizando varias plantillas .rap


Para finalizar, un ejemplo en el que se procesan las plantillas .rap de una lista. Posiblemente, lo más interesante de este código está en ver cómo se crean dinámicamente objetos en Python.

create_csv_run_list_templates.py
from pysaurio import Raptor
import csv
  
def main():
 
 # Declarar lista con plantillas .rap
     
 rap_templates = ['users.rap',
   'users_columns.rap']

 # Declarar objetos dinámicamente
      
 objects = [Raptor() for index in range(len(rap_templates))]

 # Recorrer todos los objetos
 
 for number_object in range(len(objects)):
  current_rap = rap_templates[number_object]
  current_object = objects[number_object]
  print('Template:', current_rap)

  # Abrir plantilla .rap y generar archivo .csv
   
  current_object.Open(current_rap)  
  if current_object.number_errors == 0:   
   file_csv = open(current_object.output_file, 'w', newline='')
   csv_output = csv.writer(file_csv, 
      delimiter=current_object.delimiter,
      quotechar=current_object.quotechar, 
      quoting=csv.QUOTE_MINIMAL)
   if current_object.include_header == '1':
    fields_list = current_object.BuildHeader()
    print(fields_list)
    csv_output.writerow(fields_list)
        
   for row in current_object.list_files:
    new_record=current_object.ApplyRules(current_object.BuildRow(row))   
    new_record=list(new_record.values())
    print(new_record)
    csv_output.writerow(new_record)   
   file_csv.close()   
  else:
   print(current_object.ShowError())
  del current_object
 return 0

if __name__ == '__main__':
 main()


Ir al índice del tutorial de Python