2009-11-28 2 views
18

Я пишу приложение на Python, которое будет иметь множество разных функций, поэтому логически я подумал, что лучше всего разбить мой скрипт на разные модули. В настоящее время мой скрипт читается в текстовом файле, который содержит код, который был преобразован в токены и письма. Затем скрипт восстанавливает код в строку с пустыми строками, в которых комментарии были бы в исходном коде.Создание сценария Python Объектно-ориентированный

У меня проблема с объектно-ориентированным сценарием. Что бы я ни пытался, я не могу заставить программу работать так же, как если бы это был всего лишь один файл сценария. В идеале я хотел бы иметь два файла сценариев, один из которых содержит класс и функцию, которая очищает и восстанавливает файл. Второй скрипт просто вызовет функцию из класса в другом файле в файле, указанном в качестве аргумента из командной строки. Это мой текущий сценарий:

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 

После следуя совету Алекс Мартелли ниже, теперь у меня есть следующий код, который дает мне тот же результат, как мой исходный код.

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() 

Я все же хотел бы разделить мой код на несколько модулей. У «очищенного файла» в моей программе будут выполняться другие функции, поэтому, естественно, очищенный файл должен быть классом сам по себе?

+0

Предположим, у вас был объект, который вы хотите. Как бы вы его использовали? Другими словами, какой синтаксис вы хотите? –

+1

В этой ситуации я бы рассматривал входной файл как объект. Процесс очистки - это функция, которую я хотел бы выполнять на указанном объекте. Имея это в виду, я хотел бы, как правило, использовать цикл for и сделать эту функцию очистки в моем классе входных файлов. – greenie

ответ

45

Чтобы ускорить существующий код измерим, добавьте def main(): до присвоения tokenList, отступы всего после этого 4 пробела, и в конце поставить обычную идиому

if __name__ == '__main__': 
    main() 

(охранник на самом деле не нужен, но это хорошая привычка, чтобы тем не менее, так как, для сценариев с повторно используемыми функциями, что делает их ввоз из других модулей).

Это не имеет ничего общего с «объектно-ориентированным»: это просто быстрее, в Python, чтобы сохранить весь ваш существенный код в функциях, не как код модуля верхнего уровня.

Второе убыстрение, изменение cleanedInput в список, то есть его первое назначение должно быть = [], и где бы вы теперь +=, используйте .append вместо этого. В конце, ''.join(cleanedInput), чтобы получить окончательную итоговую строку. Это заставляет ваш код принимать линейное время в зависимости от размера ввода (O(N) - это нормальный способ выражения), в то время как в настоящее время он принимает квадратичное время (O(N squared)).

Затем правильность: два утверждения сразу после continue никогда не выполняются. Вам они нужны или нет? Удалите их (и continue), если это не необходимо, удалите continue, если эти два утверждения действительно необходимы. И тесты, начинающиеся с if diff, потерпят неудачу, если только предыдущий if не был выполнен, потому что тогда diff не определился. Имеет ли ваш код как опубликованный, возможно, ошибки отступов, т. Е. Является отступом того, что вы выложили, отличным от вашего фактического кода?

Учитывая эти важные необходимые улучшения и тот факт, что трудно понять, какую выгоду вы преследуете при создании этого крошечного кода OO (и/или модульного), я предлагаю уточнить ситуацию с отступом/правильностью, применяя улучшения I ' предложил и оставил его на этом ;-).

Редактировать: поскольку в настоящее время ОП применяет большинство моих предложений, позвольте мне проследить один разумный способ убрать большую часть функциональности для класса в отдельном модуле. В новом файле, например foobar.py, в том же каталоге, что и оригинальный сценарий (или в site-packages, или в другом месте на sys.path), поместите этот код:

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 

Ваш главный сценарий становится просто:

import sys 
import foobar 

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

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

Чтобы немного расширить один из точек Алекса: в Python строки неизменяемы. 's + = 'abc'' делает совершенно новую копию' s' с добавлением 'abc'. Поэтому, если вы создаете длинную строку с конкатенацией, вы копируете постоянно растущую строку снова и снова.Добавление элементов в список (или использование модуля 'StringIO'), а затем выполнение единственного' join' в конце позволяет избежать этого. –

+0

Спасибо за те комментарии Алекс. Да, с того момента, когда я вставлял его, были ошибки с отступом. Я исправил это, чтобы отразить то, что я просматриваю в своей среде IDE. Причина, по которой мне хотелось бы, чтобы этот код стал модульным, заключается в том, что в конечном итоге программа будет рекурсивным спускающим парсером, поэтому я хотел бы разделить вещи для простоты. – greenie

+1

@greenie, спасибо за исправление отступа, но проблемы, которые я указал (код не в функции, строка + =), остаются, и есть другие, такие как полное повторение сложного раздельного и полосового выражения, которые ** far ** более высокий приоритет, чтобы улучшить качество этого кода, чем отбрасывать несколько строк на метод, а не на функцию. Сначала первые! -) –

1

Когда я делаю этот конкретный рефакторинг, я обычно начинаю с первоначального преобразования в первом файле. Шаг 1: переместите функциональность в метод в новом классе. Шаг 2: добавить магический вызов ниже, чтобы получить файл, чтобы снова запустить как сценарий:

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

Я скорректировал ваш отступ. Однако 'clean()' и 'arg' остаются неопределенными. –

+0

Извините, нажмите кнопку отправки, прежде чем я буду готов. Кроме того, я просто вычислил код widdit. * вздох * – CBFraser

+1

Ха, два обсессивно-компульсивных, пытающихся очистить один и тот же пост одновременно. Это похоже на сумасшедшую историю О. Генри. –

0

Вы можете уйти от создания функции и поместить в нее всю свою логику. Для полной «объектной ориентированности» вы можете сделать что-то вроде этого:

ps - у вашего опубликованного кода есть ошибка в строке continue - она ​​всегда выполняется, и последние две строки никогда не будут выполняться.

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

Моя ошибка. При вставке я забыл отступать и все выше, чтобы сохранить его в первом утверждении if. – greenie

0

Если представленный код - это код. Просто не добавляйте класс!

Ваш код слишком просто для этого! ООП-подход добавил бы излишнюю сложность.

Но если все еще привыкаешь. Включите весь код, например.

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

в конце добавить:

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

Если код работает правильно путы размораживания функции в новый файл например (и весь необходимого импорт!). mymodyle.py

ваш скрипт теперь будет:

from mymodule.py import parse_tokenized_input 

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

О, и думаю, что лучше, имя для функции и модуля (модуль должен иметь общее название).

Смежные вопросы