2016-05-26 15 views
14

Ich habe gegoogelt und auch auf SO für den Unterschied zwischen diesen Puffermodulen suchen. Allerdings verstehe ich immer noch nicht sehr gut und ich denke, einige der Beiträge, die ich gelesen habe, sind veraltet.Verwirrend über StringIO, cStringIO und ByteIO

In Python 2.7.11 habe ich eine Binärdatei eines bestimmten Formats mit r = requests.get(url) heruntergeladen. Dann übergab ich StringIO.StringIO(r.content), cStringIO.StringIO(r.content) und io.BytesIO(r.content) an eine Funktion zum Parsen des Inhalts.

Alle diese drei Methoden sind verfügbar. Ich meine, selbst wenn die Datei binär ist, ist es immer noch möglich, StringIO zu verwenden. Warum?

Eine andere Sache betrifft ihre Effizienz.

In [1]: import StringIO, cStringIO, io 

In [2]: from numpy import random 

In [3]: x = random.random(1000000) 

In [4]: %timeit y = cStringIO.StringIO(x) 
1000000 loops, best of 3: 736 ns per loop 

In [5]: %timeit y = StringIO.StringIO(x) 
1000 loops, best of 3: 283 µs per loop 

In [6]: %timeit y = io.BytesIO(x) 
1000 loops, best of 3: 1.26 ms per loop 

Wie oben dargestellt, cStringIO > StringIO > BytesIO.

Ich fand jemanden erwähnt, dass io.BytesIO immer eine neue Kopie macht, die mehr Zeit kostet. Es gibt aber auch einige Posts, die in späteren Python-Versionen behoben wurden.

Also, kann jemand einen gründlichen Vergleich zwischen diesen IO s, in beiden neuesten Python 2.x und 3.x machen?


Einige der Referenz ich gefunden:

  • https://trac.edgewall.org/ticket/12046

    io.StringIO requires a unicode string. io.BytesIO requires a bytes string. StringIO.StringIO allows either unicode or bytes string. cStringIO.StringIO requires a string that is encoded as a bytes string.

  • Aber cStringIO.StringIO('abc') erhebt keinen Fehler

      .

      Es ist ein Update-Patch in diesem Beitrag in 2014.

      • Viele SO-Posts, die hier nicht aufgeführt sind.

      Hier werden die Python 2.7 Ergebnisse zum Beispiel Eric sind

      %timeit cStringIO.StringIO(u_data) 
      1000000 loops, best of 3: 488 ns per loop 
      %timeit cStringIO.StringIO(b_data) 
      1000000 loops, best of 3: 448 ns per loop 
      %timeit StringIO.StringIO(u_data) 
      1000000 loops, best of 3: 1.15 µs per loop 
      %timeit StringIO.StringIO(b_data) 
      1000000 loops, best of 3: 1.19 µs per loop 
      %timeit io.StringIO(u_data) 
      1000 loops, best of 3: 304 µs per loop 
      # %timeit io.StringIO(b_data) 
      # error 
      # %timeit io.BytesIO(u_data) 
      # error 
      %timeit io.BytesIO(b_data) 
      10000 loops, best of 3: 77.5 µs per loop 
      

      Wie bei 2.7, cStringIO.StringIO und StringIO.StringIO sind wesentlich effizienter als io.

    +0

    Können Sie jedes Ihrer Sni beschriften als Python 2 oder Python 3? – Eric

    +0

    @Eric, ich habe alle meine Tests in Python 2.7.11. Es scheint, dass "(c) StringIO" in 3. durch "io" ersetzt wird. Ich benutze hauptsächlich 2.7. Aber ich denke, es wäre für andere Leser sinnvoll, beide Versionen zu diskutieren. – Lee

    +1

    ['io'] (https://docs.python.org/2/library/io.html) ist in Python 2 ebenso – Eric

    Antwort

    13

    Sie sollten io.StringIO oder io.BytesIO verwenden, abhängig davon, ob Ihre Daten in Python 2 und 3 binär sind, für die Abwärtskompatibilität (das ist alles, was 3 zu bieten hat).


    Hier ist ein besserer Test (für Python 2 und 3), die nicht Umrüstungskosten nicht enthalten von numpy zu str/bytes

    import numpy as np 
    import string 
    b_data = np.random.choice(list(string.printable), size=1000000).tobytes() 
    u_data = b_data.decode('ascii') 
    u_data = u'\u2603' + u_data[1:] # add a non-ascii character 
    

    Und dann:

    import io 
    %timeit io.StringIO(u_data) 
    %timeit io.StringIO(b_data) 
    %timeit io.BytesIO(u_data) 
    %timeit io.BytesIO(b_data) 
    

    In Python 2, können Sie auch testen:

    import StringIO, cStringIO 
    %timeit cStringIO.StringIO(u_data) 
    %timeit cStringIO.StringIO(b_data) 
    %timeit StringIO.StringIO(u_data) 
    %timeit StringIO.StringIO(b_data) 
    

    Einige davon werden abstürzen, über Nicht-ASCII-Zeichen beschweren


    Python 3.5 Ergebnisse:

    >>> %timeit io.StringIO(u_data) 
    100 loops, best of 3: 8.61 ms per loop 
    >>> %timeit io.StringIO(b_data) 
    TypeError: initial_value must be str or None, not bytes 
    >>> %timeit io.BytesIO(u_data) 
    TypeError: a bytes-like object is required, not 'str' 
    >>> %timeit io.BytesIO(b_data) 
    The slowest run took 6.79 times longer than the fastest. This could mean that an intermediate result is being cached 
    1000000 loops, best of 3: 344 ns per loop 
    

    Python 2.7 Ergebnisse (laufen auf einer anderen Maschine):

    >>> %timeit io.StringIO(u_data) 
    1000 loops, best of 3: 304 µs per loop 
    >>> %timeit io.StringIO(b_data) 
    TypeError: initial_value must be unicode or None, not str 
    >>> %timeit io.BytesIO(u_data) 
    TypeError: 'unicode' does not have the buffer interface 
    >>> %timeit io.BytesIO(b_data) 
    10000 loops, best of 3: 77.5 µs per loop 
    
    >>> %timeit cStringIO.StringIO(u_data) 
    UnicodeEncodeError: 'ascii' codec cant encode character u'\u2603' in position 0: ordinal not in range(128) 
    >>> %timeit cStringIO.StringIO(b_data) 
    1000000 loops, best of 3: 448 ns per loop 
    >>> %timeit StringIO.StringIO(u_data) 
    1000000 loops, best of 3: 1.15 µs per loop 
    >>> %timeit StringIO.StringIO(b_data) 
    1000000 loops, best of 3: 1.19 µs per loop 
    
    +0

    Also in' 3.x ist 'BytesIO' deutlich von und viel schneller als' StringIO ', im Gegensatz zu 2.x. – Lee

    +0

    'io.BytesIO' und' io.StringIO' sind nicht vergleichbar, da man nur an binären Eingängen arbeitet und das andere nur an Unicode-Strings arbeitet. – Eric

    +0

    Ich habe die 2.7 Tests ergänzt. Vielleicht können Sie sie in Ihre Post legen? – Lee