Создание сценария Python объектно-ориентированного - PullRequest
22 голосов
/ 28 ноября 2009

Я пишу приложение на 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()

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

Ответы [ 4 ]

50 голосов
/ 28 ноября 2009

Чтобы измерить существующий код быстрее, добавьте 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 было бы неопределенным. Возможно, ваш опубликованный код содержит ошибки отступа, т. Е. Отличается ли отступ того, что вы опубликовали, от кода вашего фактического кода?

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

Редактировать : так как ОП теперь применил большинство моих предложений, позвольте мне найти один разумный способ выделить большую часть функциональности для класса в отдельном модуле. В новом файле, например 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()
1 голос
/ 28 ноября 2009

Когда я делаю этот конкретный рефакторинг, я обычно начинаю с первоначального преобразования в первом файле. Шаг 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 голосов
/ 28 ноября 2009

Если код представлен полностью, просто не добавляйте класс !!

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

Но если все-таки не получится. Поместите весь код в функцию, например.

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

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

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

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

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

from mymodule.py import parse_tokenized_input

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

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

0 голосов
/ 28 ноября 2009

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

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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...