Когда используется StringIO, а не присоединение к списку строк?

Использование StringIO в качестве строкового буфера медленнее, чем использование списка в качестве буфера.

Когда используется StringIO?

from io import StringIO


def meth1(string):
    a = []
    for i in range(100):
        a.append(string)
    return ''.join(a)

def meth2(string):
    a = StringIO()
    for i in range(100):
        a.write(string)
    return a.getvalue()


if __name__ == '__main__':
    from timeit import Timer
    string = "This is test string"
    print(Timer("meth1(string)", "from __main__ import meth1, string").timeit())
    print(Timer("meth2(string)", "from __main__ import meth2, string").timeit())

Полученные результаты:

16.7872819901
18.7160351276

person simha    schedule 19.01.2011    source источник
comment
Возможно, вы имеете в виду «Когда» вместо «Где» выше?   -  person Lennart Regebro    schedule 19.01.2011
comment
Попробуйте meth1 с пониманием списка.   -  person Barney Szabolcs    schedule 20.03.2020


Ответы (4)


Если вы измеряете скорость, вы должны использовать cStringIO.

Из документов:

Модуль cStringIO предоставляет интерфейс, аналогичный интерфейсу модуля StringIO. Интенсивное использование объектов StringIO.StringIO можно сделать более эффективным, используя вместо этого функцию StringIO() из этого модуля.

Но суть StringIO в том, чтобы быть подобным файлу объектом, когда что-то ожидает этого, а вы не хотите использовать настоящие файлы.

Редактировать: я заметил, что вы используете from io import StringIO, так что вы, вероятно, используете Python >= 3 или по крайней мере 2.6. Отдельные StringIO и cStringIO исчезли в Py3. Не уверен, какую реализацию они использовали для предоставления io.StringIO. Также есть io.BytesIO.

person plundra    schedule 19.01.2011
comment
Попробуйте с cStringIO. Результаты: Список: 17, cString: 33. - person user225312; 19.01.2011
comment
io.StringIO — это реализация C, если она существует на вашей платформе. Если нет, он использует резервную реализацию Python. Причина, по которой это происходит медленнее, заключается в том, что он делает что-то, для чего ему вообще не нужен StringIO. - person Lennart Regebro; 19.01.2011
comment
Модуль cStringIO был удален в Python 3. - person Jeyekomon; 02.08.2021

Главное преимущество StringIO в том, что его можно использовать там, где ожидался файл. Итак, вы можете сделать, например (для Python 2):

import sys
import StringIO

out = StringIO.StringIO()
sys.stdout = out
print "hi, I'm going out"
sys.stdout = sys.__stdout__
print out.getvalue()
person TryPyPy    schedule 19.01.2011
comment
Можно ли использовать его с with в python 2? Из того, что я вижу здесь нет: bugs.python.org/issue1286 - person Mr_and_Mrs_D; 30.12.2014
comment
@Mr_and_Mrs_D см. http://bugs.python.org/issue1286#msg176512, в котором говорится, что он будет работать от 2,5 и выше. Чего еще ты хочешь, крови на нем? :D - person Mark Lawrence; 03.07.2016
comment
@MarkLawrence: нет, не будет - перечитайте комментарий, на который вы ссылаетесь, - вам нужно свернуть свой собственный контекстный менеджер - person Mr_and_Mrs_D; 03.07.2016

Ну, я не знаю, хотел бы я назвать это, используя его как «буфер», вы просто умножаете строку в 100 раз двумя сложными способами. Вот несложный способ:

def meth3(string):
    return string * 100

Если мы добавим это к вашему тесту:

if __name__ == '__main__':

    from timeit import Timer
    string = "This is test string"
    # Make sure it all does the same:
    assert(meth1(string) == meth3(string))
    assert(meth2(string) == meth3(string))
    print(Timer("meth1(string)", "from __main__ import meth1, string").timeit())
    print(Timer("meth2(string)", "from __main__ import meth2, string").timeit())
    print(Timer("meth3(string)", "from __main__ import meth3, string").timeit())

В качестве бонуса получается намного быстрее:

21.0300650597
22.4869811535
0.811429977417

Если вы хотите создать набор строк, а затем соединить их, meth1() будет правильным способом. Нет смысла писать его в StringIO, это нечто совсем другое, а именно строка с файлоподобным потоковым интерфейсом.

person Lennart Regebro    schedule 19.01.2011

Другой подход основан на подходе Леннарта Регебро. Это быстрее, чем метод списка (meth1)

def meth4(string):
    a = StringIO(string * 100)
    contents = a.getvalue()
    a.close()
    return contents

if __name__ == '__main__':
    from timeit import Timer
    string = "This is test string"
    print(Timer("meth1(string)", "from __main__ import meth1, string").timeit())
    print(Timer("meth2(string)", "from __main__ import meth2, string").timeit())
    print(Timer("meth3(string)", "from __main__ import meth3, string").timeit())
    print(Timer("meth4(string)", "from __main__ import meth4, string").timeit())

Результаты (сек.):

мет1 = 7,731315963647944

мет2 = 9,609279402186985

мет3 = 0,26534052061106195

мет4 = 2,915035489152274

person Jagadeesh Sali    schedule 14.03.2018
comment
Я не думаю, что когда-либо может быть какая-либо польза для переноса строки в StringIO только для того, чтобы немедленно снова преобразовать ее в строку и отбросить объект StringIO, особенно если вы заботитесь о времени выполнения. - person Nathan; 29.01.2019