问题 在Python脚本中存储数据块的Pythonic方法是什么?


Perl允许我使用 __DATA__ 脚本中的标记,用于标记数据块的开头。我可以使用DATA文件句柄读取数据。在脚本中存储数据块的Pythonic方法是什么?


11093
2017-08-04 14:04


起源

将它放在一个单独的文件(模块)中并导入它。不要内联。 - agf
@agf - 我不同意。使用包装在StringIO中的三引号字符串内联类文件对象可构成可移植且独立的测试用例或演示脚本。 - PaulMcG
谢谢! stackoverflow社区太棒了! - Charlie Guo
字符串方法都需要在使用它们之前在文件中定义字符串。 perl 数据 部分在代码之后。对?如果有解决办法,请告诉我。 - spazm
有一个 Python邮件列表中的相关线程。 - Palec


答案:


这取决于你的数据,但dict文字和多行字符串都是非常好的方法。

state_abbr = {
    'MA': 'Massachusetts',
    'MI': 'Michigan',
    'MS': 'Mississippi',
    'MN': 'Minnesota',
    'MO': 'Missouri',
    }

gettysburg = """
Four score and seven years ago,
our fathers brought forth on this continent
a new nation, 
conceived in liberty
and dedicated to the proposition
that all men are created equal.
"""

8
2017-08-04 14:07



如果它是二进制数据(即没有字节,没有文本),您可以通过在字符串前加b来包含这些数据。即:b“\ x00 \ x01 \ x16 \ x38”。例如,Qt用它来包含资源文件 - Voo
@Voo:b前缀不会这样做。它在Python 2中被忽略,而在Python 3中则意味着创建一个字节文字而不是字符串(unicode)文字。二进制数据可以作为十六进制转义包含在常规的无前缀字符串中。 - Ned Batchelder
哦,对,是在Python3模式。当然,因为python 2中的“字符串”不是unicode开头的前缀不会有多大意义。但是你真的被允许在python 3字符串中包含非法的unicode代码点吗?这是令人惊讶的,特别是因为从字节(例如从套接字读取)到unicode的转换确实检查它是否有意义。 - Voo
确实。 str = "\x80abc" 虽然它包含非法的utf-8代码点,但仍有效 str = b"\x80abc".decode("utf-8") 可以预见地失败。多么奇怪的行为。似乎结果只是被忽略(即好像你将解码的错误模式设置为“忽略”) - Voo


使用StringIO模块创建类似源文件的对象:

from StringIO import StringIO

textdata = """\
Now is the winter of our discontent,
Made glorious summer by this sun of York.
"""

# in place of __DATA__ = open('richard3.txt')
__DATA__ = StringIO(textdata)
for d in __DATA__:
    print d

__DATA__.seek(0)
print __DATA__.readline()

打印:

Now is the winter of our discontent,

Made glorious summer by this sun of York.

Now is the winter of our discontent,

(我刚刚打电话给你 __DATA__ 与原始问题保持一致。在实践中,这不是很好的Python命名风格 - 类似于 datafile 会更合适。)


4
2017-08-04 14:33



除了标准的魔术方法之外,切勿使用双下划线名称。 - agf


不熟悉Perl的 __DATA__ 变量谷歌告诉我它经常用于测试。假设您也在考虑测试代码,您可能需要考虑doctest(http://docs.python.org/library/doctest.html)。例如,而不是

import StringIO

__DATA__ = StringIO.StringIO("""lines
of data
from a file
""")

假设你想要 数据 成为一个文件对象,现在你已经拥有了,你可以像往常一样使用大多数其他文件对象。例如:

if __name__=="__main__":
    # test myfunc with test data:
    lines = __DATA__.readlines()
    myfunc(lines)

但如果只使用 数据 用于测试你最好在PyUnit / Nose中创建doctest或编写测试用例。

例如:

import StringIO

def myfunc(lines):
    r"""Do something to each line

    Here's an example:

    >>> data = StringIO.StringIO("line 1\nline 2\n")
    >>> myfunc(data)
    ['1', '2']
    """
    return [line[-2] for line in lines]

if __name__ == "__main__":
    import doctest
    doctest.testmod()

运行这样的测试:

$ python ~/doctest_example.py -v
Trying:
    data = StringIO.StringIO("line 1\nline 2\n")
Expecting nothing
ok
Trying:
    myfunc(data)
Expecting:
    ['1', '2']
ok
1 items had no tests:
    __main__
1 items passed all tests:
   2 tests in __main__.myfunc
2 tests in 2 items.
2 passed and 0 failed.
Test passed.

Doctest做了很多不同的事情,包括在纯文本文件中查找python测试并运行它们。就个人而言,我不是一个大粉丝,更喜欢更有条理的测试方法(import unittest但它确实是一种测试代码的pythonic方法。


0
2017-08-04 14:35





IMO它在很大程度上取决于数据的类型:如果你只有文本,并且可以确定没有任何机会在里面的“'或”“”,你可以使用这个版本来存储文本。但是例如,如果您愿意,可以将某些文本存储在已知或可能存在“或”或“”的地方?那么它是值得建议的

  • 要么以任何方式存储数据,要么存储
  • 把它放在一个单独的文件中

示例:文本是

Python库中有许多和“”。

在这种情况下,通过三重报价可能很难做到这一点。所以你可以做到

__DATA__ = """There are many '''s and \"""s in Python libraries.""";
print __DATA__

但是在编辑或替换文本时你必须注意。 在这种情况下,它可能更有用

$ python -c 'import sys; print sys.stdin.read().encode("base64")'
There are many '''s and """s in Python libraries.<press Ctrl-D twice>

那你得到

VGhlcmUgYXJlIG1hbnkgJycncyBhbmQgIiIicyBpbiBQeXRob24gbGlicmFyaWVzLg==

作为输出。拿这个并把它放到你的脚本中,例如

__DATA__ = 'VGhlcmUgYXJlIG1hbnkgJycncyBhbmQgIiIicyBpbiBQeXRob24gbGlicmFyaWVzLg=='.decode('base64')
print __DATA__

并看到结果。


0
2017-08-05 15:15