YAML загружает 5e-6 как строку, а не число

Когда я загружаю число с e-формой дампа JSON с YAML, число загружается как строка, а не float.

Я думаю, что этот простой пример может объяснить мою проблему.

import json
import yaml

In [1]: import json

In [2]: import yaml

In [3]: All = {'one':1,'low':0.000001}

In [4]: jAll = json.dumps(All)

In [5]: yAll = yaml.safe_load(jAll)

In [6]: yAll
Out[6]: {'low': '1e-06', 'one': 1}

YAML загружает 1е-06 как строку, а не как число? Как я могу это исправить?

Ответ 1

Проблема заключается в том, что YAML Resolver настроен для соответствия поплавкам следующим образом:

Resolver.add_implicit_resolver(
    u'tag:yaml.org,2002:float',
    re.compile(u'''^(?:[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+][0-9]+)?
    |\\.[0-9_]+(?:[eE][-+][0-9]+)?
    |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*
    |[-+]?\\.(?:inf|Inf|INF)
    |\\.(?:nan|NaN|NAN))$''', re.X),
    list(u'-+0123456789.'))

тогда как спецификация YAML определяет регулярное выражение для научной нотации как:

-? [1-9] ( \. [0-9]* [1-9] )? ( e [-+] [1-9] [0-9]* )?

последняя делает точку необязательной, чего нет в приведенном выше шаблоне re.compile() в неявном преобразователе.

Сопоставление чисел с плавающей запятой можно исправить, поэтому оно будет принимать значения с плавающей запятой с e/E, но без десятичной точки и с показателями без знака (то есть подразумевается +):

import yaml
import json
import re

All = {'one':1,'low':0.000001}

jAll = json.dumps(All)

loader = yaml.SafeLoader
loader.add_implicit_resolver(
    u'tag:yaml.org,2002:float',
    re.compile(u'''^(?:
     [-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)?
    |[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
    |\\.[0-9_]+(?:[eE][-+][0-9]+)?
    |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*
    |[-+]?\\.(?:inf|Inf|INF)
    |\\.(?:nan|NaN|NAN))$''', re.X),
    list(u'-+0123456789.'))

data = yaml.load(jAll, Loader=loader)
print 'data', data

приводит к:

data {'low': 1e-06, 'one': 1}

Существует несоответствие между тем, что JSON допускает в числах, и регулярным выражением в спецификации YAML 1.2 (относительно требуемой точки в числе и e в нижнем регистре). JSON-спецификация очень понятна IMO в том смысле, что для нее не требуется точка перед 'e/E', а для нее не требуется знак после 'e/E':

enter image description here

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

ruamel.yaml (моя расширенная версия PyYAML) имеет обновленный шаблон и работает правильно:

import ruamel.yaml
import json

All = {'one':1,'low':0.000001}

jAll = json.dumps(All)

data = ruamel.yaml.load(jAll)
print 'data', data

с выводом:

data {'low': 1e-06, 'one': 1}

ruamel.yaml также принимает число "1.0e6", которое PyYAML также видит в виде строки.