Когда используется StringIO, а не присоединяется к списку строк? - PullRequest
48 голосов
/ 19 января 2011

Использование 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

Ответы [ 4 ]

32 голосов
/ 19 января 2011

Основным преимуществом 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()
26 голосов
/ 19 января 2011

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

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

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

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

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

17 голосов
/ 19 января 2011

Ну, я не знаю, хотел бы я назвать это, используя его как «буфер», вы просто умножаете строку в 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, который является чем-то совершенно другим, а именно строкой с файловым интерфейсом потока.

0 голосов
/ 14 марта 2018

Другой подход, основанный на подходе Леннарта Регебро. Это быстрее, чем метод списка (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())

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

meth1 = 7.731315963647944

meth2 = 9.609279402186985

meth3 = 0.26534052061106195

meth4 = 2.915035489152274

...