【问题标题】:How do I make string-like variable that iterates over a large string?如何制作迭代大字符串的类似字符串的变量?
【发布时间】:2013-02-16 07:32:16
【问题描述】:

在 Python 中,我可以构建一个类似于字符串但在内部迭代字符串序列的变量吗?

例如

def function_a():
    for i in xrange(100000000):
        yield str(i)

这将遍历一个字符串列表,并且它会有效地完成它 - 一次只在内存中保留一个字符串。但我想要的是这样的:

''.join([s for s in function_a()])

但我敢打赌,这只是做天真的事情,并遍历整个集合并将它们全部连接成内存中的一个大字符串。另一个问题是我想要一个变量,我不想让用户接触到实际执行联接的丑陋工作。所以也许用户会做这样的事情:

magic_str = get_long_but_memory_efficient_str()

然后使用它来有效地打印到屏幕上(并随时释放内存):

print magic_str

或者我的 real 用途是 HTTP 流到服务器:

request = urllib2.Request(url, magic_str)

显然存在这种类似的东西。查看下面的代码,以有效地将文件流式传输到服务器 (from this question)。

f = open('somelargefile.zip','rb')
mmapped_file_as_string = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
request = urllib2.Request(url, mmapped_file_as_string)
request.add_header("Content-Type", "application/zip")
response = urllib2.urlopen(request)

但我的情况不同,因为我正在构建要流式传输到服务器的字符串。

【问题讨论】:

    标签: python string iterator


    【解决方案1】:

    根据您的实际需要更新答案

    >>> class MagicString(str):
        def __init__(self, gen):
            self.gen = gen
        def __str__(self):
            try:
                return self.gen.next()
            except StopIteration:
                return '' #boolean value = False
    
    
    >>> def run_efficiently(some_function, magic_str):
        substr = str(magic_str)
        while substr:
            some_function(substr)
            substr = str(magic_str)
    

    说明:您需要以下组合:

    1. 使用生成器生成子字符串的对象(例如在字符串表示上)
    2. 在对象的表示返回空字符串之前调用另一个函数的函数。

    将此示例扩展到打印

    >>> import sys
    >>> def print_without_breaks(some_string):
        sys.stdout.write(some_string)
    
    >>> s = MagicString(c for c in '12345')
    >>> run_efficiently(print_without_breaks, s)
    12345
    

    您可以根据实际需要使用类似的方法,您可以对每个请求的返回值做一些有用的事情。

    所以也许,您根本不需要变量/对象...只是一些重复运行的简单代码,直到您的生成器吐出 StopIteration 异常。

    【讨论】:

    • 真的很接近!只需将 str 的内容替换为return ''.join(self.gen),它在功能上正是我想要的。下一个问题是“这有效吗?”那么通过我的修改,print a 是否会在内存中创建整个字符串然后打印它?或者 print 遍历字符串的内容并一次打印一个字符(在后台生成字符串时)。
    • 如果您进行连接,它将在打印之前将完整的连接字符串存储在内存中。如果你想通过一次打印一个字符串来打印它的子字符串,你需要重新定义print来重复调用生成器(或者在这种情况下,使用a)。
    【解决方案2】:

    不确定我是否完全理解您想要什么,但在我看来,您担心 Python 字符串的不变性。

    join 不会像你想象的那样创建很多临时对象。如果您已经有一个列表,则 ''.join 将非常有效,并且只会创建一个字符串。

    如果您没有理由使用要连接的对象创建列表,只需使用 cStringIO 模块。这将使用最少的内存。

    如果您仍然担心,或者您是顽固的 C 程序员,不了解人们如何看不到以空结尾的字节序列是上帝希望我们处理字符串的方式,请用 C 编写您的那部分代码,这在 python 中非常有用,例如与 Java 相比。

    【讨论】:

    • 实际上,在Cpython(至少)中,''.join(x for x in whatever)(或多或少)等同于''.join(tuple(whatever))。所以,OP 关于那部分是正确的。
    • 如果 ''.join 创建“一个单一的字符串”,这对我的目的来说效率不高。当然,它很快,但我不想一次将整个字符串保存在内存中。内存消耗是我主要关心的问题。
    猜你喜欢
    • 2021-10-02
    • 1970-01-01
    • 1970-01-01
    • 2019-07-15
    • 1970-01-01
    • 2015-11-28
    • 1970-01-01
    • 2017-07-25
    • 1970-01-01
    相关资源
    最近更新 更多