Как хранить и извлекать словарь кортежей в парсере конфигурации python?

Я использую библиотеку configparser в python для управления файлами конфигурации.

Но я не могу найти метод для хранения и извлечения структуры данных кортежей через него.

Мои данные - это словарь кортежей.

name_mapper = {
    0 = (0, 0)
    1 = (0, 1)
    2 = (0, 2)
    3 = (1, 0)
    4 = (1, 1)
    5 = (1, 2)
    6 = (2, 0)
    7 = (2, 1)
    8 = (2, 2)
    9 = (3, 0)
    10 = (3, 1)
    11 = (3, 2)
}

Когда я пишу этот словарь через configparser, все становится строкой.

myconfig.ini

[NAME_MAPPER]
0 = (0, 0)
1 = (0, 1)
2 = (0, 2)
3 = (1, 0)
4 = (1, 1)
5 = (1, 2)
6 = (2, 0)
7 = (2, 1)
8 = (2, 2)
9 = (3, 0)
10 = (3, 1)
11 = (3, 2)

Теперь при чтении "my_config.ini"

config = configparser.ConfigParser()
config.read('config_params.ini')
name_mapper = dict(config['NAME_MAPPER'])

Но словарь больше не содержит кортежи, это просто кортеж, отформатированный как строка.

name_mapper = {
    '0' = '(0, 0)'
    '1' = '(0, 1)'
    '2' = '(0, 2)'
    '3' = '(1, 0)'
    '4' = '(1, 1)'
    '5' = '(1, 2)'
    '6' = '(2, 0)'
    '7' = '(2, 1)'
    '8' = '(2, 2)'
    '9' = '(3, 0)'
    '10' = '(3, 1)'
    '11' = '(3, 2)'
}

Я нашел способ исправить это, используя метод ast.literal_eval.

from ast import literal_eval

new_name_mapper = dict()
for each in name_mapper:
    new_name_mapper[int(each)] = literal_eval(name_mapper[each])

Теперь new_name_mapper правильно отформатирован.

name_mapper = {
    0 = (0, 0)
    1 = (0, 1)
    2 = (0, 2)
    3 = (1, 0)
    4 = (1, 1)
    5 = (1, 2)
    6 = (2, 0)
    7 = (2, 1)
    8 = (2, 2)
    9 = (3, 0)
    10 = (3, 1)
    11 = (3, 2)
}

Но я уверен, что это не лучший подход. Кто-нибудь получил лучше и больше питонических идей.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
1 136
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Парсер конфигурации всегда будет возвращать строки, если только вы не используете явный преобразователь с помощью методов getbool getint``etc. Однако вы можете написать свою собственную функцию конвертера и зарегистрировать ее в своем синтаксическом анализаторе, а затем использовать ее для извлечения значения любым удобным для вас способом.

Из Документация по конфигурационному синтаксическому анализатору:

converters, default value: not set

Config parsers provide option value getters that perform type conversion. By default getint(), getfloat(), and getboolean() are implemented. Should other getters be desirable, users may define them in a subclass or pass a dictionary where each key is a name of the converter and each value is a callable implementing said conversion. For instance, passing {'decimal': decimal.Decimal} would add getdecimal() on both the parser object and all section proxies. In other words, it will be possible to write both parser_instance.getdecimal('section', 'key', fallback=0) and parser_instance['section'].getdecimal('key', 0).

If the converter needs to access the state of the parser, it can be implemented as a method on a config parser subclass. If the name of this method starts with get, it will be available on all section proxies, in the dict-compatible form (see the getdecimal() example above).

Таким образом, вы можете написать функцию для разбора кортежа

 >>> def parse_tuple(input):
 ...     return tuple(k.strip() for k in input[1:-1].split(','))

 >>> parse_tuple('(1, 2, 3)')
 >>> ('1', '2', '3')

или если вам нужны целые числа:

 >>> def parse_int_tuple(input):
 ...     return tuple(int(k.strip()) for k in input[1:-1].split(','))

 >>> parse_int_tuple('(1, 2, 3)')
 >>> (1, 2, 3)

создайте свой объект configparser, передав этот преобразователь:

>>> parser = ConfigParser(converters = {'tuple': parse_int_tuple})
>>> parser.read_dict({'NAME_MAPPER': name_mapper})
>>> parser['NAME_MAPPER'].gettuple('0')
(0, 0)

IMO, это самый подход питонический, поскольку он использует только хорошо документированные функции.

Другие вопросы по теме