2017-11-11 108 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 

这给了所有你需要用最少的代码的灵活性。