如何以Pythonic的方式在Python脚本中存储数据块?

16

Perl允许我在脚本中使用__DATA__标记来标记数据块的开始。我可以使用DATA文件句柄读取数据。那么,在Python中如何以Pythonic的方式将数据块存储在脚本中呢?


2
将其放入单独的文件(模块)中并进行导入,不要内联操作。 - agf
1
@agf - 我不同意。使用三引号字符串包装的StringIO内的类文件对象进行内联,可以创建一个便携式且自包含的测试用例或演示脚本。 - PaulMcG
字符串方法都要求在使用之前在文件中定义字符串。Perl的__DATA__部分在代码之后, 对吗?如果有绕过方式请告诉我。 - spazm
在Python邮件列表中有一个相关的主题线。 - Palec
4个回答

11

取决于你的数据,但字典字面值和多行字符串都是非常好的方法。

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.
"""

2
如果是二进制数据(即没有字节和文本),您可以通过在字符串前面加上b来包含它们。例如:b"\x00\x01\x16\x38"。这在Qt中用于包含资源文件。 - Voo
4
@Voo:b前缀并不会做那件事。在Python 2中会被忽略,在Python 3中表示创建一个字节串而不是字符串(unicode)字面值。二进制数据可以以十六进制转义的形式包含在普通的非前缀字符串中。 - Ned Batchelder
没错,我在Python3模式下。当然,由于Python 2中的“字符串”本身不是Unicode,所以前缀并没有太多意义。但是,在Python 3字符串中真的允许包含非法的Unicode代码点吗?这很令人惊讶,特别是因为从字节(例如从套接字读取)转换为Unicode确实会检查其是否有意义。 - Voo
1
确实。str = "\x80abc" 可以工作,尽管它包含一个非法的 utf-8 码位,而 str = b"\x80abc".decode("utf-8") 则会可预测地失败。这是多么奇怪的行为啊。看起来结果只是被忽略了(就好像你将解码的错误模式设置为“忽略”一样)。 - Voo
U+0080 被定义为 C1 控制字符。它的 UTF-8 编码是 b'\xc2\x80'b"\x80abc" 的问题在于它是一个无效的 UTF-8 序列,这是完全不同的问题。 - chepner

6
使用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之类的东西。


5
除了标准魔术方法,永远不要使用双下划线名称来命名任何东西。 - agf

1

在我看来,这高度取决于数据的类型:如果你只有文本,并且可以确定其中没有可能包含 ''' 或 """ 的情况,那么你可以使用这个版本来存储文本。但是如果你想要存储一些已知包含或可能包含 ''' 或 """ 的文本,该怎么办呢?那么建议:

  • 要么以任何方式编码存储数据,
  • 要么将其放入单独的文件中。

例如:文本为

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

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

import StringIO

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

假设您想要DATA成为一个文件对象,那么现在您已经得到了它,并且可以像其他大多数文件对象一样使用它。例如:
if __name__=="__main__":
    # test myfunc with test data:
    lines = __DATA__.readlines()
    myfunc(lines)

但是如果 DATA 的唯一用途是进行测试,那么您最好创建一个 doctest 或在 PyUnit/Nose 中编写一个测试案例。

例如:

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 的测试代码的方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接