2012-08-02 17 views
3

我試圖製作一個非常基本的腳本,它利用TkInter根據用戶輸入繪製線條。用戶將通過命令行執行腳本,使用參數確定繪製的線條。創建解析字典以與sys.argv一起使用

可能的命令包括:​​

(# 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'] 

所以我的問題是這樣的:如何創建方法的有效字典,上述陣列工作和執行爲基礎的方法在ar上ray元素,一個接一個?我甚至開始懷疑是否有可能由於某些方法需要傳遞屬性(數字),而有些則不會。任何人都可以闡明這件事情?

請注意,我是非常新的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) 

回答

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) 
+0

絕對完美。我認爲在我完全理解代碼之前需要一段時間,但我的目標是直到我研究它爲止。我正在使用Python 3.x,但我自己可以輕鬆更改打印語句。 – Djentleman 2012-08-02 06:19:06

+0

無論如何,我改變了它,以備將來參考。 – 2012-08-02 17:03:51

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': 
+1

我意識到這一點,但以這種方式做它會使我的代碼中最重要的部分被破壞。 'self.source.append(「P」+ str(sys.argv [i + 1]))''將不再起作用,因爲'i'不再用於遍歷'sys.argv'。 – Djentleman 2012-08-02 01:05:11

+0

看看我的另一個答案替代... – larsks 2012-08-02 01:11:11

+0

請使用'爲我,枚舉(argv)的參數:'而不是上述解決方案(或您自己的)。 – 2012-08-02 02:26:19

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 functions,但你說你是Python新手,所以我手動定義了這個函數。

+0

我忘記了一些我已經編輯到問題底部的關鍵信息。抱歉! – Djentleman 2012-08-02 01:27:19

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'] 
+0

這不回答我的問題,但仍然是非常有用的代碼,所以謝謝。 – Djentleman 2012-08-02 01:20:19

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)