Backporting Python 3 open (encoding = „utf-8”) do Python 2

152

Mam bazę kodu w Pythonie, zbudowaną dla Pythona 3, która używa stylu open () w Pythonie 3 z parametrem kodowania:

https://github.com/miohtama/vvv/blob/master/vvv/textlineplugin.py#L47

    with open(fname, "rt", encoding="utf-8") as f:

Teraz chciałbym przenieść ten kod do Pythona 2.x, aby mieć bazę kodu, która działa z Pythonem 2 i Pythonem 3.

Jaka jest zalecana strategia obejścia open()różnic i braku parametru kodowania?

Czy mogę mieć open()moduł obsługi plików w stylu Python 3 , który przesyła strumieniowo bajty, więc działałby jak Python 2 open()?

Mikko Ohtamaa
źródło

Odpowiedzi:

176

1. Aby uzyskać parametr kodowania w Pythonie 2:

Jeśli potrzebujesz tylko obsługi Pythona 2.6 i 2.7, możesz użyć io.openzamiast open. ioto nowy podsystem io dla Pythona 3, który istnieje również w Pythonie 2,6 i 2,7. Należy pamiętać, że w Pythonie 2.6 (a także 3.0) jest zaimplementowany wyłącznie w Pythonie i bardzo powolny, więc jeśli potrzebujesz szybkości odczytu plików, nie jest to dobra opcja.

Jeśli potrzebujesz szybkości i potrzebujesz obsługi języka Python 2.6 lub starszego, możesz użyć codecs.openzamiast tego. Ma również parametr kodowania i jest bardzo podobny do tego, io.openz wyjątkiem tego, że inaczej obsługuje zakończenia linii.

2. Aby uzyskać open()obsługę plików w stylu Python 3 , która przesyła strumieniowo bajty:

open(filename, 'rb')

Zwróć uwagę na „b”, co oznacza „binarny”.

Lennart Regebro
źródło
11
„B” w rzeczywistości oznacza tryb binarny, a nie bajty. Zobacz docs.python.org/3/library/functions.html#open .
pmdarrow
7
@pmdarrow To samo w tym przypadku, ale ściśle mówiąc, tak.
Lennart Regebro
Napotkałem problem polegający na tym, że nie możesz uruchomić wyrażenia regularnego na strumieniu bajtów dla opcji 2;)
Jonathan Komar
3
@ macmadness86 Musisz użyć wyrażenia regularnego w bajtach.
Lennart Regebro
4
Uwaga z poradnika portowania: „Nie przejmuj się przestarzałą praktyką używania codecs.open (), ponieważ jest to konieczne tylko do zachowania zgodności z Pythonem 2.5”. docs.python.org/3/howto/pyporting.html
Al Sweigart
65

Myślę

from io import open

powinieneś zrobić.

mfussenegger
źródło
7
Myślę, że poniższa odpowiedź Lennarta jest znacznie lepsza, ponieważ zawiera więcej wyjaśnień i ostrzeżenie dotyczące powolnego działania modułu io w wersji 2.x wraz z sugestią użycia codecs.open.
GPS
2
Co się stanie, jeśli from io import openużyję w Pythonie 3? Obecnie nie zależy mi na wydajności.
matth
8
@matth W python3 open from io jest aliasem dla wbudowanego open. Zobacz docs.python.org/3/library/io.html?highlight=io#io.open
mfussenegger
21

Oto jeden sposób:

with open("filename.txt", "rb") as f:
    contents = f.read().decode("UTF-8")
Flimm
źródło
4
to oczywiście nie zadziała, jeśli miałeś inne planyf
użytkownik5359531
8

To może załatwić sprawę:

import sys
if sys.version_info[0] > 2:
    # py3k
    pass
else:
    # py2
    import codecs
    import warnings
    def open(file, mode='r', buffering=-1, encoding=None,
             errors=None, newline=None, closefd=True, opener=None):
        if newline is not None:
            warnings.warn('newline is not supported in py2')
        if not closefd:
            warnings.warn('closefd is not supported in py2')
        if opener is not None:
            warnings.warn('opener is not supported in py2')
        return codecs.open(filename=file, mode=mode, encoding=encoding,
                    errors=errors, buffering=buffering)

Następnie możesz zachować kod w sposób python3.

Zauważ, że niektóre API podoba newline, closefd, openernie działają

user2395922
źródło
1
możesz odwrócić warunek, aby tego uniknąć pass.
bfontaine
2

Jeśli używasz six, możesz spróbować tego, dzięki czemu wykorzystujesz najnowszy interfejs API Python 3 i możesz działać zarówno w Pythonie 2/3:

import six

if six.PY2:
    # FileNotFoundError is only available since Python 3.3
    FileNotFoundError = IOError
    from io import open

fname = 'index.rst'
try:
    with open(fname, "rt", encoding="utf-8") as f:
        pass
        # do_something_with_f ...
except FileNotFoundError:
    print('Oops.')

A porzucenie obsługi Pythona 2 to po prostu usunięcie wszystkiego, co dotyczy six.

YaOzI
źródło