使Python脚本面向对象

时间:2009-11-28 17:19:36

标签: python oop

我正在用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

在按照下面的Alex Martelli建议后,我现在有以下代码,它给出了与原始代码相同的输出。

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 个答案:

答案 0 :(得分:49)

为了显着加快现有代码的速度,请在分配到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(和/或模块化)时所追求的优势,我建议澄清缩进/正确的情况,应用我的增强功能提议,并留在那;;)。

编辑:由于OP现在已经应用了我的大部分建议,让我以一种合理的方式跟进一个单独模块中的大多数功能。在新文件中,例如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 :(得分: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]) 

答案 2 :(得分: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))

答案 3 :(得分:0)

如果呈现的代码是所有代码只是不添加任何类!!

你的代码太简单了!! OOP方法会增加不必要的复杂性。

但如果仍然不会。 将所有代码放入功能,例如。

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

哦,为你的功能和模块想出更好的名字(模块应该有通用名称)。