2017-11-11 64 views
0

我得到了一個包含4個變量的元組列表,這些元組將被打印在一行中,我希望用戶指定這些打印格式。用戶自定義格式的輸入.format()

這是我到目前爲止的代碼:

mylist = [ 
    ("Monday", "13", "November", "2017"), 
    ("Tuesday", "14", "November", "2017"), 
    ("Wednesday", "15", "November", "2017")] 
# Note: The real list have a thousand of items, not just 3. 

custom_format = self.GUI_field_where_user_types.get_text() 
for item in mylist: 
    weekday = item[0] 
    daynumber = item[1] 
    month = item[2] 
    year = item[3] 
    print(eval(custom_format)) 

# Example custom_format: '"{0}, {1} of {2} from year {3}".format(weekday, daynumber, month, year)' 

做檢查,我只是想與上面的輸入,它顯然作品,但我不想使用eval,因爲它顯然太危險了。所以我的問題是:如何讓用戶以一種安全的方式定義格式?

注:用戶應該能夠在.format()方法"{0}, {1} of {2} from year {3}"之前只能編輯字符串,阻止訪問任何蟒蛇命令,但讓他只能使用變量,他想要的,並不總是4其中。一些實例中可能是:

  • {0},{1}的{2}
  • {3},{1}在一個月{2}
  • {0},{1}的{2 }從今年{3}
  • {1}任何{2} - {3}
+0

對不起,我有點困惑。你輸入的是什麼?這:來自年份{3}「'{2}的{」{0},{1}? –

回答

2

這很簡單。只需撥打.format輸入字符串,並將項目傳遞與使用參數參數拆包語法:

mylist = [ 
    ("Monday", "13", "November", "2017"), 
    ("Tuesday", "14", "November", "2017"), 
    ("Wednesday", "15", "November", "2017")] 

fmt = input('Format? ') 
for item in mylist: 
    print(fmt.format(*item)) 

執行:

Format? {0}, {1} of {2} 
Monday, 13 of November 
Tuesday, 14 of November 
Wednesday, 15 of November 

處理用戶輸入無效的格式,你可以捕獲該異常再試一次:

while True: 
    try: 
     fmt = input('Format? ') 
     for item in mylist: 
      print(fmt.format(*item)) 
     break 
    except (IndexError,ValueError,KeyError): 
     print('Invalid format.') 

執行:

Format? {A} 
Invalid format. 
Format? {4} 
Invalid format. 
Format? { 
Invalid format. 
Format? The year is {3} 
The year is 2017 
The year is 2017 
The year is 2017 
+0

這是針對特定問題的一個很好的解決方案,但在處理時間時不使用'datetime'是一個很大的錯誤(我的觀點)。 –

+0

@Anton只是因爲OP有日期作爲例子並不意味着變量將永遠是日期。這是一個通用的解決方案。 –

+0

@Anton另外,爲什麼在格式化正確的時候通過'datetime'對象格式化字符串呢?如果您不知道「11/13」是11月份的星期一,請使用'datetime'。 –

0

使用的方法來安排格式:

def fill_format(custom_format, date): 
    used_vars = [] 
    used_indexes = [] 

    #Look for what is used 
    for i in range(len(date)): 
     if "{"+str(i)+"}" in custom_format: 
      used_vars.append(date[i]) 
      used_indexes.append(i) 

    #Replace indexes starting from 0 
    for new_index, used_index in enumerate(used_indexes): 
     custom_format = custom_format.replace("{"+str(used_index)+"}", "{"+str(new_index)+"}") 

    return custom_format.format(*used_vars) 

mylist = [ 
    ("Monday", "13", "November", "2017"), 
    ("Tuesday", "14", "November", "2017"), 
    ("Wednesday", "15", "November", "2017")] 

custom_format = "{3}, {1} at month {2}" 
for item in mylist: 
    print(fill_format(custom_format, item)) 

輸出:

2017, 13 at month November 
2017, 14 at month November 
2017, 15 at month November 

編輯:

如果日期一向4個時間單位,您可以使用:

def fill_format(custom_format, date): 
    return custom_format.replace("{0}", date[0]).replace("{1}", date[1]).replace("{2}", date[2]).replace("{3}", date[3]) 
+0

對不起,如果我很困惑問這個問題。關鍵部分是用戶輸入提供了未知數量的{0},所以我不能有一個硬編碼的'.format(1,2,3)',因爲我甚至不知道是否有3個項目或500個測試我自己的反應,因爲它的工作原理。我仍然認爲應該有更好的辦法。編輯:截圖http://prntscr.com/h95x7b注意,用戶可以輸入Formato。 – Saelyth

+0

@Saelyth我改變了功能。我聲稱它甚至可以處理您將項目長度從4更改爲5或另一個數字。假設你的'custom_format'只是一個字符串,包括一些'{0}','{1}','{2}',...告訴我我是錯的還是代碼不起作用。 – Alperen

+0

是的,它的工作原理。不過,我會接受Mark的回答,因爲它會刪除大量的代碼,並保持簡單並具有相同的結果。希望你能理解。 – Saelyth

0

我的建議是:堅持以標準化datetime格式。使用日期時間,我們可以使用強大的strftime根據一組預定義的模式打印日期時間。考慮下面這個例子:

import datetime 

mylist = [ 
    ("Monday", "13", "November", "2017"), 
    ("Tuesday", "14", "November", "2017"), 
    ("Wednesday", "15", "November", "2017")] 

custom_format = "%A, %d of %B from year %Y" 

for item in mylist: 
    # Create datetime object 
    dt = datetime.datetime.strptime('-'.join([*item]),"%A-%d-%B-%Y") 
    # Print with format 
    print(dt.strftime(custom_format)) 

打印:

Monday, 13 of November from year 2017 
Tuesday, 14 of November from year 2017 
Wednesday, 15 of November from year 2017 

如果你想創建「自己的語言」,使用字典映射到通用的標準。考慮下面這個例子:

import datetime 
import re 

mylist = [ 
    ("Monday", "13", "November", "2017"), 
    ("Tuesday", "14", "November", "2017"), 
    ("Wednesday", "15", "November", "2017")] 

d = {"WEEKDAY": "%A", 
    "DAYNUMBER": "%d", 
    "MONTH": "%B", 
    "YEAR": "%Y"} 

input_ = "YEAR-%m-DAYNUMBER is equal to WEEKDAY, DAYNUMBER of MONTH from year YEAR" 

# Use re library to change WEEKDAY --> %A and so on... 
pattern = re.compile(r'\b(' + '|'.join(d.keys()) + r')\b') 
custom_format = pattern.sub(lambda x: d[x.group()], input_) 

for item in mylist: 
    # Create datetime object 
    dt = datetime.datetime.strptime('-'.join([*item]),"%A-%d-%B-%Y") 
    # Print with format 
    print(dt.strftime(custom_format)) 

輸出:

2017-11-13 is equal to Monday, 13 of November from year 2017 
2017-11-14 is equal to Tuesday, 14 of November from year 2017 
2017-11-15 is equal to Wednesday, 15 of November from year 2017 

這給了所有你需要用最少的代碼的靈活性。

相關問題