自定义.format()

时间:2017-11-11 14:23:02

标签: python python-3.x eval

我得到了一个包含4个变量的元组列表,每个变量将打印在一行中,我希望用户指定这些打印的格式。

到目前为止,这是我的代码:

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}"之前编辑字符串,阻止访问任何python命令,但允许他只使用变量他想要的,并不总是他们中的4个。一些例子可能是:

  • {0},{1}的{1}
  • {3},{1}月份{2}
  • {0},来自{3}年{2}的{1}
  • {1}无论{2} - {3}

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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])

答案 2 :(得分:0)

我的建议:坚持使用标准化的datetime格式。使用datetime,我们可以使用强大的strftime根据一组预定义模式打印日期时间。考虑这个例子:

<meta name="description" content="{% page_attribute "meta_description" %}">

打印:

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

如果您想创建自己的语言&#34;使用字典映射到标准字典。考虑这个例子:

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

使用最少的代码,您可以获得所需的所有灵活性。