Вы действительно не можете делать то, что просите в PyYAML, даже для небольшого примера, который у вас есть.Печать запрошенного значения работает, но когда вы обновите свой YAML, вы увидите, что он изменяет несколько выходных строк в дополнение к значению accountID
.Если предположить, что ваш YAML-файл test.yaml
:
from pathlib import Path
import yaml
yaml_file = Path('test.yaml')
with yaml_file.open() as fp:
data = yaml.safe_load(fp)
user_settings = data['main']['user-settings']
print('acount id:', user_settings['accountID'])
user_settings['accountID'] = 102
print('region: ', data['main']['client settings']['region'])
with yaml_file.open('w') as fp:
yaml.safe_dump(data, fp, indent=4, default_flow_style=False)
, то вы получите:
acount id: 666
region: OK
И содержимое test.yaml
будет:
main:
client settings:
nickname: mainPC
region: OK
user-settings:
accountID: 102
timestamp: 00:00
Youполучите Python dict
для любого сопоставления в файле YAML и Python list
, если бы в YAML была последовательность.Чтобы напечатать желаемое значение, вы просто пересекаете вложенные dict
s.
Как видите, вы теряете кавычки вокруг OK
и mainPC
и получаете их около 00:00
.Последнее связано с тем, что PyYAML не был обновлен для обработки текущего стандарта YAML 1.2, выпущенного еще в 2009 году. В YAML 1.1 предполагается, что скаляры в форме XX: YY равны sexagesimals и преобразованы (и выгружены) какцелые числа (или с плавающей запятой, если была десятичная точка).То, что ваш 00:00
загружен как строка, происходит из-за начального нуля.Если бы это было 12:00
, то ваш выходной файл был бы timestamp: 720
.
Порядок ключей в вашем отображении также не сохраняется.Семантически это не имеет никакого значения, но если этот файл находится под контролем ревизии, такие изменения быстро приводят в замешательство.
Вы должны использовать safe_load()
, так как «стандартный» load()
может быть небезопасен при неконтролируемом вводе YAMLи хотя это задокументировано, большинство людей не осознают этого или того, что это по сути никогда не требуется.(Небезопасно, как в «harddrive wiped», или еще хуже.)
Если вы хотите обновить файл YAML, я рекомендую использовать ruamel.yaml
(отказ от ответственности: я авторэтого пакета).
from pathlib import Path
import ruamel.yaml
yaml_file = Path('test.yaml')
yaml = ruamel.yaml.YAML()
yaml.indent(mapping=4)
yaml.preserve_quotes = True
data = yaml.load(yaml_file)
user_settings = data['main']['user-settings']
print('acount id:', user_settings['accountID'])
user_settings['accountID'] = 102
print('region: ', data['main']['client settings']['region'])
yaml.dump(data, yaml_file)
, который также выводит:
acount id: 666
region: OK
И содержимое test.yaml
будет:
main:
user-settings:
accountID: 102
timestamp: 00:00
client settings:
nickname: "mainPC"
region: "OK"
Не толькокавычки правильно сохранены, но не обязательно заключать в кавычки 00:00
, потому что ruamel.yaml
обрабатывает YAML 1.2, в котором шестнадцатеричные значения вообще отброшены¹Порядок ключей в сопоставлениях сохраняется.
Если бы в test.yaml
были комментарии, то PyYAML
отбросило бы их, тогда как ruamel.yaml
также сохранило бы их.
В ruamel.yaml
по умолчанию YAML()
используется безопасный загрузчик.
¹ Вы можете использовать sexagesimals, если создаете класс Sexagesimal
и помечаете их соответствующим образом.В этом случае вы также можете решить иметь начальные нули (!sexagesimal 00:00
) и правильно сбросить эти значения обратно как скаляры с тегами без кавычек.