2009-11-28 19 views
18

Estoy escribiendo una aplicación en Python que tendrá muchas funciones diferentes, así que lógicamente pensé que sería mejor dividir mi script en diferentes módulos. Actualmente mi script lee en un archivo de texto que contiene código que se ha convertido en tokens y deletreos. El script luego reconstruye el código en una cadena, con líneas en blanco donde los comentarios habrían estado en el código original.Hacer una secuencia de comandos de Python Orientado a objetos

Tengo un problema al hacer la secuencia de comandos orientada a objetos. Todo lo que intento no parece hacer que el programa se ejecute de la misma manera que si fuera solo un archivo de script. Idealmente, me gustaría tener dos archivos de script, uno que contenga una clase y una función que limpie y reconstruya el archivo. El segundo script simplemente llama a la función de la clase en el otro archivo en un archivo dado como argumento desde la línea de comando. Este es mi script actual:

import sys 

tokenList = open(sys.argv[1], 'r') 
cleanedInput = '' 
prevLine = 0 

for line in tokenList: 

    if line.startswith('LINE:'): 
     lineNo = int(line.split(':', 1)[1].strip()) 
     diff = lineNo - prevLine - 1 

     if diff == 0: 
      cleanedInput += '\n' 
     if diff == 1: 
      cleanedInput += '\n\n' 
     else: 
      cleanedInput += '\n' * diff 

     prevLine = lineNo 
     continue 

    cleanedLine = line.split(':', 1)[1].strip() 
    cleanedInput += cleanedLine + ' ' 

print cleanedInput 

Después de seguir consejos Alex Martelli a continuación, ahora tengo el siguiente código que me da el mismo resultado que mi código original.

def main(): 
    tokenList = open(sys.argv[1], 'r') 
    cleanedInput = [] 
    prevLine = 0 

    for line in tokenList: 

     if line.startswith('LINE:'): 
      lineNo = int(line.split(':', 1)[1].strip()) 
      diff = lineNo - prevLine - 1 

      if diff == 0: 
       cleanedInput.append('\n') 
      if diff == 1: 
       cleanedInput.append('\n\n') 
      else: 
       cleanedInput.append('\n' * diff) 

      prevLine = lineNo 
      continue 

     cleanedLine = line.split(':', 1)[1].strip() 
     cleanedInput.append(cleanedLine + ' ') 

    print cleanedInput 

if __name__ == '__main__': 
    main() 

Aunque me gustaría dividir mi código en varios módulos. Un "archivo limpio" en mi programa tendrá otras funciones en él, así que, naturalmente, un archivo limpio debería ser una clase en sí misma.

+0

Supongamos que tiene el objeto que desea. Cómo lo usarías? En otras palabras, ¿qué sintaxis quieres? –

+1

En esta situación, consideraría el archivo de entrada como el objeto. El proceso de limpieza es la función que me gustaría poder realizar en dicho objeto. Con esto en mente, me gustaría principalmente tomar el bucle for y hacer que la función de limpieza en mi clase de archivo de entrada. – greenie

Respuesta

45

para acelerar su código existente mensurable, añadir def main(): antes de la asignación al tokenList, guión todo después de que los 4 espacios, y al final poner el lenguaje habitual

if __name__ == '__main__': 
    main() 

(el protector no es realmente necesario, pero es un buen hábito para tener, sin embargo, ya que, por guiones con funciones reutilizables, los hace importarse de otros módulos).

Esto tiene poco que ver con "orientado a objetos": simplemente es más rápido, en Python, mantener todo su código en funciones, no como código de módulo de nivel superior.

Segundo aumento de velocidad, el cambio cleanedInput en una lista, es decir, su primera tarea debe ser = [], y donde quiera que ahora tiene +=, utilice .append lugar. Al final, ''.join(cleanedInput) para obtener la cadena final resultante. Esto hace que el código tome tiempo lineal como una función del tamaño de entrada (O(N) es la forma normal de expresar esto) mientras que actualmente toma un tiempo cuadrático (O(N squared)).

Entonces, corrección: las dos declaraciones justo después de continue nunca se ejecutan. ¿Los necesitas o no? Quítelos (y el continue) si no es necesario, elimine el continue si esas dos declaraciones son realmente necesarias. Y las pruebas que comienzan con if diff fallarán dramáticamente a menos que se haya ejecutado el if anterior, porque diff no estaría definido entonces. ¿Su código tal como se publicó quizás tenga errores de sangría, es decir, es la sangría de lo que ha publicado diferente de la de su código real?

Considerando estas importantes mejoras necesarias, y el hecho de que es difícil ver qué ventaja está persiguiendo al hacer este pequeño código OO (y/o modular), sugiero aclarar la situación de sangrado/corrección, aplicando las mejoras I ' he propuesto, y dejándolo así ;-).

Editar: como el OP ahora ha aplicado la mayoría de mis sugerencias, permítame seguir con una forma razonable de separar la mayoría de las funcionalidades de una clase en un módulo aparte. En un nuevo archivo, por ejemplo foobar.py, en el mismo directorio que el guión original (o en site-packages, o en otra parte sys.path), colocar este código:

def token_of(line): 
    return line.partition(':')[-1].strip() 

class FileParser(object): 
    def __init__(self, filename): 
    self.tokenList = open(filename, 'r') 

    def cleaned_input(self): 
    cleanedInput = [] 
    prevLine = 0 

    for line in self.tokenList: 
     if line.startswith('LINE:'): 
      lineNo = int(token_of(line)) 
      diff = lineNo - prevLine - 1 
      cleanedInput.append('\n' * (diff if diff>1 else diff+1)) 
      prevLine = lineNo 
     else: 
      cleanedLine = token_of(line) 
      cleanedInput.append(cleanedLine + ' ') 

    return cleanedInput 

Su script principal, entonces se convierte simplemente:

import sys 
import foobar 

def main(): 
    thefile = foobar.FileParser(sys.argv[1]) 
    print thefile.cleaned_input() 

if __name__ == '__main__': 
    main() 
+7

Expandir un poco sobre uno de los puntos de Alex: en Python, las cadenas son inmutables. 's + = 'abc'' hace una copia completamente nueva de' s' con 'abc' adjunto. Entonces, si está creando una cadena larga con concatenación, está copiando una cadena cada vez mayor una y otra vez.Agregar elementos a una lista (o usar el módulo 'StringIO') y luego hacer una sola 'unión' al final lo evita. –

+0

Gracias por esos comentarios Alex. Sí, hubo errores de sangría de cuando lo pegué. Lo arreglé ahora para reflejar lo que estoy viendo en mi IDE. La razón por la que me gustaría que este código se vuelva modular es porque el programa finalmente será un analizador sintáctico de descenso recursivo, por lo que me gustaría dividir las cosas por simplicidad. – greenie

+1

@greenie, gracias por arreglar la sangría, pero los problemas que señalé (código no en una función, string + =) permanecen, y hay otros como la repetición total de la compleja expresión de división y tira, que son ** ahora ** prioridad más alta, para mejorar la calidad de este código, que relegar algunas líneas a un método en lugar de una función. Primero lo primero! -) –

1

Cuando realizo esta refactorización en particular, generalmente comienzo con una transformación inicial dentro del primer archivo. Paso 1: mueve la funcionalidad a un método en una nueva clase. Paso 2: añadir la invocación mágica a continuación para obtener el archivo para funcionar como un guión nuevo:

class LineCleaner: 

    def cleanFile(filename): 
     cleanInput = "" 
     prevLine = 0 
     for line in open(filename,'r'):   
      <... as in original script ..> 

if __name__ == '__main__': 
    cleaner = LineCleaner() 
    cleaner.cleanFile(sys.argv[1]) 
+0

Ajusté su sangría. Sin embargo, 'clean()' y 'arg' permanecen indefinidos. –

+0

Lo sentimos, presione el botón de enviar antes de estar listo. Además, descubrí el código de muestra widgit. * suspiro * – CBFraser

+1

Ha, dos obsesivo-compulsivos tratando de limpiar el mismo mensaje al mismo tiempo. Es como una historia trastornada de O. Henry. –

0

Puede salirse con la suya creando una función y poniendo toda su lógica en ella. Para una "orientación a objetos" completa, puedes hacer algo como esto:

ps - tu código publicado tiene un error en la línea continue - siempre se ejecuta y las 2 últimas líneas nunca se ejecutarán.

class Cleaner: 
    def __init__(...): 
    ...init logic... 
    def Clean(self): 
    for line in open(self.tokenList): 
     ...cleaning logic... 
    return cleanedInput 

def main(argv): 
    cleaner = Cleaner(argv[1]) 
    print cleaner.Clean() 
    return 0 

if '__main__' == __name__: 
    sys.exit(main(sys.argv)) 
+0

Mi error. Al pegar, me olvidé de seguir con la sangría y todo lo de arriba para mantenerlo dentro de la primera afirmación if. – greenie

0

Si el código presentado es todo código ¡No añada ninguna clase!

¡Su código es demasiado simple para eso! El enfoque OOP agregaría una complejidad innecesaria.

Pero si todavía no lo hace. Ponga todo el código en funcionamiento, por ej.

def parse_tokenized_input(file): 
    tokenList = open(file, 'r') 
    cleanedInput = '' 
    prevLine = 0 
    #rest of code 

añadir al final:

if __name__ == '__main__': 
    parse_tokenized_input(sys.argv[1]) 

Si el código funciona correcta definición de la función de venta al nuevo archivo por ejemplo (y todas las importaciones necesarias!). mymodyle.py

su guión será ahora:

from mymodule.py import parse_tokenized_input 

if __name__ == '__main__': 
     parse_tokenized_input(sys.argv[1]) 

Ah, y pensar en un mejor nombre para su función y módulo (módulo debe tener nombre general).

Cuestiones relacionadas