Используйте ConfigParser с различными типами переменных в Python

Я пытаюсь использовать ConfigParser в следующей ситуации. Я запускаю некоторый код, после которого у меня есть объект с несколькими атрибутами. Я выбираю некоторые из этих атрибутов и записываю их в файл .ini с помощью configparser. Это отлично работает, и мой файл .ini выглядит примерно так.

[Section]
path = "D:\\"
number = 10.0
boolean = False

Затем с помощью другого скрипта я хочу прочитать файл и добавить элементы в качестве атрибутов к другому объекту (самостоятельно).

parser.read('file.ini')
self.__dict__.update(dict(parser.items("Section")))

Это не удается, потому что все значения считываются configparser как строки, и теперь все атрибуты являются строками. parser.items("Section") выглядит так:

 [('path', '"D:\\"'), ('number', '10.0'), ('boolean', 'False')]

Теперь я мог пойти и указать числа с плавающей запятой, целые числа и логические значения по их ключам и использовать соответствующие методы parser.getfloat, getint и getboolean для получения правильных типов Python. Однако это означает создание дополнительного списка ключей и цикла для их получения для каждого типа данных, чего я не хочу. Кроме того, даже путь теперь заключен в двойные кавычки, и мне нужно начать удалять кавычки.

Такое поведение делает ConfigParser почти полностью бесполезным для меня, и я сомневаюсь, правильно ли я его использую, и если ConfigParser - лучший вариант для моей цели, которая состоит в том, чтобы просто записать атрибуты объекта в файл, а позже прочитать файл и добавить параметры нового объекта в качестве атрибутов. Могу ли я эффективно использовать ConfigParser для этого? Или мне начать искать другой метод?


person R.Meijer    schedule 31.07.2017    source источник
comment
Мне очень не нравится eval, но здесь можно было бы: {item[0]: eval(item[1]) for item in parser.items('section')}   -  person Arount    schedule 31.07.2017
comment
Спасибо, @Around! Это делает работу просто отлично.   -  person R.Meijer    schedule 31.07.2017


Ответы (1)


INI — это не JSON. Нет типов данных. Есть разделы и пары ключ/значение. Материал до = является ключом, материал после него является значением. Все текстовое.

Нет никаких «строк», что означает, что нет необходимости ничего двойных кавычек. То же самое касается «экранированных» обратных косых черт. Концепция экранирования не существует в формате файла INI.

Итак, во-первых, ваш файл должен выглядеть так:

[Section]
path = D:\
number = 10.0
boolean = False

Далее, я считаю это опасной операцией

parser.read('file.ini')
self.__dict__.update(dict(parser.items("Section")))

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

Я бы написал более явный способ чтения и проверки данных конфигурации. Я чувствую, что причина, по которой вы этого не делаете, — лень или ложное чувство простоты; обе эти причины не очень хороши.

Если вам нужен полуавтоматический способ преобразования типов, пометьте ключи типом данных.

[Section]
s_path = D:\
f_number = 10.0
b_boolean = False

и написать функцию, которая знает, как с ними обращаться и выбрасывает, когда что-то не так.

def type_convert(items):
    result = []
    for (key, value) in items:
        type_tag = key[:2]
        if type_tag == "s_":
            result.append((key[2:], value))
        elif type_tag == "f_":
            result.append((key[2:], float(value)))
        elif type_tag == "b_":
            result.append((key[2:], bool(value)))
        else:
            raise ValueError('Invalid type tag "%s" found in ini file.' % type_tag)
            # alternatively: "everything else defaults to string"
    return result

который вы можете использовать, чтобы сделать операцию несколько более удобной:

self.__dict__.update(dict(type_convert(parser.items("Section"))))

Конечно, вы по-прежнему рискуете испортить свой экземпляр класса, переопределив ключи, которые не следует переопределять.

person Tomalak    schedule 31.07.2017
comment
Спасибо за расширенный ответ @Tomalak. Вы определенно правы в том, что я не стал писать более явный метод, но я ожидал немного большего автоматизированного функционирования класса ConfigParser, и я до сих пор не понимаю, насколько это действительно полезно, если вы не можете писать и читать, не имея делать так много явной работы самостоятельно. Тем не менее, я не думал об этом подходе с тегами, который, я думаю, является хорошим способом. Я также понимаю, что шаг self.__dict__.update немного рискован, но он должен подойти для моего приложения. - person R.Meijer; 31.07.2017
comment
@R.Meijer Если вы хотите пропустить такое ручное преобразование типов, подумайте об использовании JSON в качестве формата файла конфигурации. Анализатор JSON выполняет преобразование типов автоматически. Взамен вы должны снова начать убегать от своих вещей. Всегда есть компромисс. - person Tomalak; 31.07.2017