3

我正在尝试制作一个非常基本的脚本,该脚本利用 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)
4

5 回答 5

2

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

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 函数,但你说你是 Python 新手,所以我手动定义了函数。

于 2012-08-02T01:03:04.997 回答
1

这是一个稍微棘手的问题,最好使用一个体面的解析框架来解决。我通常推荐 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)
于 2012-08-02T03:10:48.737 回答
0

这不是答案,而是对您的代码的扩展评论。我猜您过去曾使用 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':
于 2012-08-02T01:02:47.410 回答
0
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)
于 2012-08-02T01:14:17.283 回答
0

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

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']
于 2012-08-02T01:10:50.330 回答