【问题标题】:Creating a parsing dictionary to use with sys.argv创建解析字典以与 sys.argv 一起使用
【发布时间】:2012-08-02 00:52:39
【问题描述】:

我正在尝试制作一个非常基本的脚本,该脚本利用 TkInter 根据用户输入来绘制线条。用户将通过命令行执行脚本,使用参数来确定绘制的线条。

可能的命令是:

(# signifies any whole number the user chooses)

P #   (selects a pen to draw with)
D     (pen down / starts drawing)
N #   (moves the pen north # increments)
E #   (moves the pen east # increments)
S #   (moves the pen south # increments)
W #   (moves the pen west # increments)
U     (pen up / stops drawing)

例子:

Parser.py P 3 D N 4 E 2 S 4 W 2 U

上面的例子将绘制一个高矩形。

我正在尝试为 sys.argv 创建一个基本解析器,它将遍历所有参数并根据所述参数及其顺序执行字典中的方法。

这是我的代码,它通过 sys.argv 运行并根据参数创建一个数组(我将添加错误检查,所以现在假设一个快乐的一天):

class JoshSourceReader(object):
    """ responsibe for providing source text for parsing and drawing
        Initiates the Draw use-case.
        Links to a parser and passes the source text onwards """
    def __init__(self):
        self.parser = JoshParser()
        self.source = []
    def go(self):
        i = 1
        x = len(sys.argv)
        while i < x:
            if sys.argv[i]=="P":
                self.source.append("P " + str(sys.argv[i + 1]))
                i += 2
            elif sys.argv[i]=="D":
                self.source.append("D")
                i += 1
            elif sys.argv[i]=="N":
                self.source.append("N " + str(sys.argv[i + 1]))
                i += 2
            elif sys.argv[i]=="E":
                self.source.append("E " + str(sys.argv[i + 1]))
                i += 2
            elif sys.argv[i]=="S":
                self.source.append("S " + str(sys.argv[i + 1]))
                i += 2
            elif sys.argv[i]=="W":
                self.source.append("W " + str(sys.argv[i + 1]))
                i += 2
            elif sys.argv[i]=="U":
                self.source.append("U")
                i += 1
        self.parser.parse(self.source)

因此,从我上面的示例生成的数组将如下所示:

source=['P 3', 'D', 'N 4', 'E 2', 'S 4', 'W 2', 'U']

所以我的问题是这样的:我怎样才能创建一个有效的方法字典来处理上述数组并根据数组元素一个一个地执行方法?我什至开始怀疑这是否可能,因为某些方法需要传递属性(数字),而有些则不需要。任何人都可以对这个问题有所了解吗?

请记住,我对 Python 非常是新手。

编辑:我忘记了一个重要信息。 N、E、S、W 都指同一个方法,但每次都采用不同的参数。方法如下(还没画):

def drawLine(self, direction, distance):
    print("drawing line of length " + str(distance) + " at "
          + str(direction))

所以如果我使用“S 3”调用这个方法,我会这样称呼它:

drawer.drawLine(180, 3)

如果我用“W 1”来称呼它,我会这样称呼它:

drawer.drawLine(270, 1)

【问题讨论】:

    标签: python parsing dictionary python-3.x


    【解决方案1】:

    您可以为函数提供默认参数。因此,只需执行以下操作:

    parsedict = {}
    def func_P(times=0):
        #do whatever here
    parsedict["P"] = func_P
    #repeat however many times
    for x in source:
        splitx = x.split(" ")
        if len(splitx) >= 2:
            parsedict[splitx[0]](splitx[1])
        else:
            parsedict[splitx[0]]()
    

    如果您需要其他任何内容,请发表评论。应该可以,但我没有测试。你也可以使用lambda functions,但是你说你是Python新手,所以我手动定义了这个函数。

    【讨论】:

    • 我忘记了我在问题底部编辑的一条重要信息。对不起!
    【解决方案2】:

    这是一个稍微棘手的问题,最好使用一个体面的解析框架来解决。我通常推荐 pyparsing 来处理这样的事情。但是,这是一个不使用任何外部框架的解决方案。

    它有点难看,但基本上,它会将输入字符串(比如“P 1”)解析为一个操作和一个整数。该字典包含要调用的方法,以及要传递给该方法的任何其他参数(对于 drawLine() 情况)。

    我在没有使用类的情况下完成了这项工作,但只需将self 作为第一个参数添加到所有内容中即可解决。

    def selectPen(pen):
        print('called selectPen with pen', pen)
    
    def penDown():
        print('called penDown')
    
    def drawLine(direction, length):
        print('called drawLine with direction', direction, 'and length', length)
    
    def penUp():
        print('called penUp')
    
    def parseSource(source):
        tempSource = [op.split(' ') for op in source]
        parsedSource = []
        for op in tempSource:
            parsedOp = []
            for i, el in enumerate(op):
                if i == 0:
                    parsedOp.append(el)
                else:
                    try:
                        parsedOp.append(int(el))
                    except ValueError:
                        parsedOp.append(el)
            parsedSource.append(tuple(parsedOp))
    
        return parsedSource
    
    def dispatch(parsedSource):
        opDict = {'P':(selectPen,), 'D':(penDown,), 'N': (drawLine, 0), 'S':(drawLine, 180), 
        'E': (drawLine, 90), 'W': (drawLine, 270), 'U': (penUp,)}
    
        for op in parsedSource:
            methodName = op[0]
            methodToCall = opDict[methodName][0] 
            args = op[1:]
            if len(opDict[methodName])>1:
                args = opDict[methodName][1:] + args
    
            methodToCall(*args)
    
    if __name__ == '__main__':
        source=['P 3', 'D', 'N 4', 'E 2', 'S 4', 'W 2', 'U']
        parsedSource = parseSource(source)
        dispatch(parsedSource)
    

    【讨论】:

    • 绝对完美。我认为我需要一段时间才能完全理解代码,但我的目标是学习它直到我理解为止。我正在使用 Python 3.x,但我自己很容易更改了打印语句。
    • 我还是改了,供以后参考。
    【解决方案3】:

    这不是答案,而是对您的代码的扩展评论。我猜您过去曾使用 C 或类似 C 的语言(Java、JavaScript 等)工作过。您正在像这样迭代sys.argv

    i = 1
    x = len(sys.argv)
    while i < x:
        if sys.argv[i]=="P":
    

    在 Python 中,有更简单的方法来遍历列表。例如:

    for arg in sys.argv:
        if arg == 'P':
    

    【讨论】:

    • 我知道这一点,但是以这样的方式这样做会使我的代码中最重要的部分损坏。 self.source.append("P " + str(sys.argv[i + 1])) 将不再工作,因为 i 不再用于迭代 sys.argv
    • 请使用for i, arg in enumerate(argv): 代替上述任一解决方案(或您自己的)。
    • 如果您想提供更多帮助,请说明您的理由。谢谢!
    • i,x in enumerate(iterable) 是标准的 Python 做事方式。一方面,它不鼓励对列表进行就地修改。另一方面,它在 Python 中更惯用,并且在阅读代码时需要更少的程序员时间来解析。
    【解决方案4】:

    您可以这样做,其中字典将“命令”映射到它们采用的参数数量:

    argdict = {
        'P': 1,
        'D': 0,
        'N': 1,
        'E': 1,
        'S': 1,
        'W': 1,
        'U': 0
    }
    
    args = sys.argv[1:]
    source = []
    
    while args:
        command = [args.pop(0)]
        nargs = argdict[command[0]]
        command.extend(args[:nargs])
        args = args[nargs:]
        source.append(' '.join(command))
    
    print source
    

    根据您的示例输入,这将创建一个如下所示的列表:

    ['P 3', 'D', 'N 4', 'E 2', 'S 4', 'W 2', 'U']
    

    【讨论】:

    • 这没有回答我的问题,但仍然是非常有用的代码,所以谢谢。
    【解决方案5】:
    class Painter(object):                                                            
        pencil_width = 1
        is_drawing = False
    
        def set_pencil(self, width=1):
            self.pencil_width = int(width)
        P = set_pencil
    
        def draw_north(self, blocks=1):
            if self.is_drawing:
                ...
        N = draw_north
    
        def process_command(self, str_):
            func_name = str_.split(' ')[0]
            args = str_.split(' ')[1:]
            try:
                func = getattr(self, func_name)
            except AttributeError:
                raise ValueError('Method %s does not exists'
                                  % func_name)
            func(*args)
    
    
    painter = Painter()
    for command in source:
        painter.process_command(command)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-28
      • 2019-01-14
      • 1970-01-01
      • 2018-06-21
      • 1970-01-01
      • 2016-12-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多