Backporting Python 3 aberto (codificação = “utf-8”) para Python 2

152

Eu tenho uma base de código Python, criada para o Python 3, que usa o estilo Python 3 open () com o parâmetro de codificação:

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

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

Agora eu gostaria de portar esse código no Python 2.x, para ter uma base de código que funcione com o Python 2 e o Python 3.

Qual é a estratégia recomendada para solucionar as open()diferenças e a falta de parâmetro de codificação?

Eu poderia ter um open()manipulador de arquivos no estilo Python 3 que transmita bytestrings, para que funcionasse como o Python 2 open()?

Mikko Ohtamaa
fonte

Respostas:

176

1. Para obter um parâmetro de codificação no Python 2:

Se você precisar apenas suportar o Python 2.6 e 2.7, poderá usar em io.openvez de open. ioé o novo subsistema io para o Python 3 e também existe no Python 2,6 e 2,7. Esteja ciente de que no Python 2.6 (assim como no 3.0) ele é implementado puramente em python e muito lento; portanto, se você precisar de velocidade na leitura de arquivos, não é uma boa opção.

Se você precisa de velocidade e precisa dar suporte ao Python 2.6 ou anterior, pode usar codecs.open. Ele também possui um parâmetro de codificação e é bastante semelhante a io.openele, exceto que lida com as terminações de linha de maneira diferente.

2. Para obter um open()manipulador de arquivos no estilo Python 3 que transmita bytestrings:

open(filename, 'rb')

Observe o 'b', que significa 'binário'.

Lennart Regebro
fonte
11
O 'b' na verdade significa modo binário, não bytes. Consulte docs.python.org/3/library/functions.html#open .
Pmdarrow # 9/14
7
@pmdarrow A mesma coisa neste caso, mas estritamente falando, sim.
Lennart Regebro 13/10
Corri para o problema que você não pode executar regex ao longo de um fluxo de bytes para a opção 2;)
Jonathan Komar
3
@ macmadness86 Você precisa usar uma expressão de byte regexp.
Lennart Regebro
4
Uma observação do tutorial de portabilidade: "Não se preocupe com a prática desatualizada de usar codecs.open (), pois isso é necessário apenas para manter a compatibilidade com o Python 2.5." docs.python.org/3/howto/pyporting.html
Al Sweigart
65

eu acho que

from io import open

deveria fazer.

mfussenegger
fonte
7
Acho que a resposta de Lennart abaixo é muito melhor, pois fornece mais explicações e a advertência sobre o módulo io ser lento em 2.x, juntamente com a sugestão de usar codecs.open.
gps
2
O que acontece se eu usar from io import openno Python 3? Eu não ligo para desempenho atualmente.
Matth
8
@matth No python3, o open from io é um alias para o open interno. Veja docs.python.org/3/library/io.html?highlight=io#io.open
mfussenegger
21

Aqui está uma maneira:

with open("filename.txt", "rb") as f:
    contents = f.read().decode("UTF-8")
Flimm
fonte
4
isso obviamente não funciona se você tivesse planos diferentes para #f
user5359531
8

Isso pode fazer o truque:

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)

Então você pode manter o código da maneira python3.

Note-se que algumas APIs gosto newline, closefd, openernão funcionam

user2395922
fonte
1
você pode reverter a condição para evitar isso pass.
bfontaine
2

Se você estiver usando six, você pode tentar fazer isso utilizando a API mais recente do Python 3 e executar no Python 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.')

E o abandono do suporte ao Python 2 está apenas excluindo tudo relacionado a six.

YaOzI
fonte