rsa*_*saw 7 python-3.x ruamel.yaml
过去,我some_fancy_printing_loggin_func(yaml.dump(...), ...)使用 ruamel.yaml 的向后兼容部分做了类似的事情,但我想将我的代码转换为使用最新的 API,以便我可以利用一些新的格式设置。
但是,我讨厌我必须指定一个流来ruamel.yaml.YAML.dump()......我不希望它直接写入流;我只是想让它把输出返回给调用者。我错过了什么?
PS:我知道我可以做类似以下的事情,当然我试图避免它。
f = io.StringIO()
yml.dump(myobj, f)
f.seek(0)
my_logging_func(f.read())
Run Code Online (Sandbox Code Playgroud)
Jef*_*kin 12
在如此频繁地需要此功能后,我将这个答案(围绕 的一个小包装器ruamel.yaml)放入了pip 模块中
pip install ez_yaml
import ez_yaml\n\nez_yaml.to_string(obj=your_object , options={})\n\nez_yaml.to_object(file_path=your_path, options={})\nez_yaml.to_object(string=your_string , options={})\n\nez_yaml.to_file(your_object, file_path=your_path)\nRun Code Online (Sandbox Code Playgroud)\ndef object_to_yaml_str(obj, options=None):\n # \n # setup yaml part (customize this, probably move it outside this def)\n # \n import ruamel.yaml\n yaml = ruamel.yaml.YAML()\n yaml.version = (1, 2)\n yaml.indent(mapping=3, sequence=2, offset=0)\n yaml.allow_duplicate_keys = True\n # show null\n def my_represent_none(self, data):\n return self.represent_scalar(u\'tag:yaml.org,2002:null\', u\'null\')\n yaml.representer.add_representer(type(None), my_represent_none)\n \n # \n # the to-string part\n # \n if options == None: options = {}\n from io import StringIO\n string_stream = StringIO()\n yaml.dump(obj, string_stream, **options)\n output_str = string_stream.getvalue()\n string_stream.close()\n return output_str\nRun Code Online (Sandbox Code Playgroud)\nimport ruamel.yaml\nfrom io import StringIO\nfrom pathlib import Path\n\n# setup loader (basically options)\nyaml = ruamel.yaml.YAML()\nyaml.version = (1, 2)\nyaml.indent(mapping=3, sequence=2, offset=0)\nyaml.allow_duplicate_keys = True\nyaml.explicit_start = False\n# show null\ndef my_represent_none(self, data):\n return self.represent_scalar(u\'tag:yaml.org,2002:null\', u\'null\')\nyaml.representer.add_representer(type(None), my_represent_none)\n\n# o->s\ndef object_to_yaml_str(obj, options=None):\n if options == None: options = {}\n string_stream = StringIO()\n yaml.dump(obj, string_stream, **options)\n output_str = string_stream.getvalue()\n string_stream.close()\n return output_str\n\n# s->o\ndef yaml_string_to_object(string, options=None):\n if options == None: options = {}\n return yaml.load(string, **options)\n\n# f->o\ndef yaml_file_to_object(file_path, options=None):\n if options == None: options = {}\n as_path_object = Path(file_path)\n return yaml.load(as_path_object, **options)\n\n# o->f\ndef object_to_yaml_file(obj, file_path, options=None):\n if options == None: options = {}\n as_path_object = Path(Path(file_path))\n with as_path_object.open(\'w\') as output_file:\n return yaml.dump(obj, output_file, **options)\n\n# \n# string examples\n# \nyaml_string = object_to_yaml_str({ (1,2): "hi" })\nprint("yaml string:", yaml_string)\nobj = yaml_string_to_object(yaml_string)\nprint("obj from string:", obj)\n\n# \n# file examples\n# \nobj = yaml_file_to_object("./thingy.yaml")\nprint("obj from file:", obj)\nobject_to_yaml_file(obj, file_path="./thingy2.yaml")\nprint("saved that to a file")\nRun Code Online (Sandbox Code Playgroud)\n我很欣赏 Mike Night 解决了最初的“我只想将输出返回给调用者”,并指出 Anthon 的帖子未能回答这个问题。我将进一步做:Anthon,你的模块很棒;往返旅行令人印象深刻,是有史以来为数不多的往返旅行之一。但是,(这种情况在 Stack Overflow 上经常发生)让其他人的代码运行高效并不是作者的工作。明确的权衡是伟大的,作者应该帮助人们理解他们选择的后果。添加警告,包括名称中的“慢”等可能非常有帮助。但是,ruamel.yaml 文档中的方法;创建整个继承类,不是“显式的”。它们会造成阻碍和混淆,使得其他人难以执行并且需要花费时间来理解附加代码的内容和原因。
\n至于性能,在没有 YAML 的情况下,我的程序的运行时间是 2 周。50万行的yaml文件在几秒钟内被读取。两周和几秒都与项目无关,因为它们是 CPU 时间,而项目纯粹按开发时间计费。许多用户理所当然地更关心开发时间而不是运行时间,毕竟我们使用的是 python。
\n即使假设运行时很关键,YAML 代码也已经是一个字符串对象,因为正在对其执行其他其他操作。强制将其放入流中实际上会导致更多开销。消除对 YAML 字符串形式的需求将涉及重写几个主要库,并且可能需要数月的努力;在这种情况下,流成为一种非常不切实际的选择。
\n即使假设可以进行流输入,并按 CPU 时间计费;优化 500,000 行 yaml 文件的一次性读取将是 \xe2\x89\xa40.001% 运行时改进。我花额外的时间来找出这个问题的答案,以及其他人花在试图理解我的样板代码的要点上的时间,本可以花在一个每秒被调用 100 次的 c 函数上两周。即使我们确实关心 CPU 时间,优化方法仍然可能不是最佳选择。
\n堆栈溢出帖子忽略了这个问题,同时还建议用户可能花费大量时间重写他们的应用程序,这并不是答案。尊重他人,假设他们通常知道自己在做什么并且知道其他选择。然后,提出的可能更有效的方法将受到赞赏而不是拒绝。
\n[吐槽结束]
\nAnt*_*hon 10
我不确定你是否真的遗漏了一些东西,如果有的话,如果你正在使用流,你应该\xe2\x80\x94preferively\xe2\x80\x94继续使用流。然而,ruamel.yaml 和 PyYAML 的许多用户似乎错过了这一点,因此他们这样做了:
\nprint(dump(data))\nRun Code Online (Sandbox Code Playgroud)\n代替
\ndump(data, sys.stdout)\nRun Code Online (Sandbox Code Playgroud)\n前者对于 (PyYAML) 文档中使用的非真实数据可能没问题,但它会导致真实数据的坏习惯。
\n最好的解决方案是让你的my_logging_func()内容面向流。例如,这可以如下完成:
import sys\nimport ruamel.yaml\n\ndata = dict(user='rsaw', question=47614862)\n\nclass MyLogger:\n def write(self, s):\n sys.stdout.write(s.decode('utf-8'))\n\nmy_logging_func = MyLogger()\nyml = ruamel.yaml.YAML()\nyml.dump(data, my_logging_func)\nRun Code Online (Sandbox Code Playgroud)\n这使:
\nuser: rsaw\nquestion: 47614862\nRun Code Online (Sandbox Code Playgroud)\n但请注意,它MyLogger.write()被调用多次(在本例中为八次),如果您需要一次处理一行,则必须进行行缓冲。
如果您确实需要将 YAML 处理为bytes或str,您可以安装适当的插件(ruamel.yaml.bytesresp. ruamel.yaml.string)并执行以下操作:
yaml = ruamel.yaml.YAML(typ=['rt', 'string'])\ndata = dict(abc=42, help=['on', 'its', 'way'])\nprint('retval', yaml.dump_to_string(data))\nRun Code Online (Sandbox Code Playgroud)\n或者处理 的结果yaml.dump_to_string(data),它相当于yaml.dumps(data)您认为必要的。上面替换string为不会将 UTF-8 流解码回,而是将其保留为.bytesstrbytes
小智 5
总会有一些意想不到的事情需要发生(即使这与通常情况下的最佳实践相矛盾)。下面是一个例子:
在这种情况下,我需要 yaml 作为字符串。不,在这里使用文件而不是字符串不会削减它,因为我将多次创建这个 input_yaml,因为我需要多次进行这个 pypandoc 转换。创建单个文件会更加混乱!
output = pypandoc.convert_text(input_yaml, to='markdown_strict', format='md', filters=filters)
input_yaml = """
---
bibliography: testing.bib
citation-style: ieee-with-url.csl
nocite: |
@*
...
"""
Run Code Online (Sandbox Code Playgroud)
正因为如此,我不得不回到 PyYAML。它允许我
yaml_args = {'bibliography':'testing.bib', 'citation-style':'ieee-with-url.csl'}
test = yaml.dump(yaml_args, default_flow_style=False)
test = "---\n"+ test + "nocite: | \n\n @* \n...\n"
output = pypandoc.convert_text(test, to='markdown_strict', format='md', filters=filters)
Run Code Online (Sandbox Code Playgroud)
笨拙,但在这种情况下我能找到的最好的。
| 归档时间: |
|
| 查看次数: |
6848 次 |
| 最近记录: |