Backporting Python 3 abierto (codificación = "utf-8") a Python 2

152

Tengo una base de código Python, creada para Python 3, que usa el estilo Python 3 open () con el parámetro de codificación:

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

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

Ahora me gustaría exportar este código a Python 2.x, para tener una base de código que funcione con Python 2 y Python 3.

¿Cuál es la estrategia recomendada para solucionar las open()diferencias y la falta de parámetros de codificación?

¿Podría tener un open()controlador de archivo de estilo Python 3 que transmita cadenas de bytes, para que actúe como Python 2 open()?

Mikko Ohtamaa
fuente

Respuestas:

176

1. Para obtener un parámetro de codificación en Python 2:

Si solo necesita admitir Python 2.6 y 2.7, puede usarlo en io.openlugar de open. ioes el nuevo subsistema io para Python 3, y también existe en Python 2,6 y 2.7. Tenga en cuenta que en Python 2.6 (así como en 3.0) se implementa únicamente en Python y muy lento, por lo que si necesita velocidad para leer archivos, no es una buena opción.

Si necesita velocidad, y necesita admitir Python 2.6 o anterior, puede usar codecs.openen su lugar. También tiene un parámetro de codificación, y es bastante similar, io.openexcepto que maneja los finales de línea de manera diferente.

2. Para obtener un open()controlador de archivos de estilo Python 3 que transmita cadenas de bytes:

open(filename, 'rb')

Tenga en cuenta la 'b', que significa 'binario'.

Lennart Regebro
fuente
11
La 'b' en realidad significa modo binario, no bytes. Ver docs.python.org/3/library/functions.html#open .
pmdarrow
77
@pmdarrow Lo mismo en este caso, pero estrictamente hablando, sí.
Lennart Regebro
Me encontré con el problema de que no se puede ejecutar regex en una secuencia de bytes para la opción 2;)
Jonathan Komar
3
@ macmadness86 Debe usar una expresión de expresión regular de bytes.
Lennart Regebro
44
Una nota del manual de portabilidad: "No te molestes con la práctica desactualizada de usar codecs.open () ya que eso solo es necesario para mantener la compatibilidad con Python 2.5". docs.python.org/3/howto/pyporting.html
Al Sweigart
65

Yo creo que

from io import open

debería hacer.

mfussenegger
fuente
77
Creo que la respuesta de Lennart a continuación es mucho mejor, ya que proporciona más explicaciones y la advertencia de que el módulo io es lento en 2.x junto con la sugerencia de usar codecs.open.
GPS
2
¿Qué pasa si lo uso from io import openen Python 3? Actualmente no me importa el rendimiento.
Mat
8
@matth En python3 open from io es un alias para el open incorporado. Ver docs.python.org/3/library/io.html?highlight=io#io.open
mfussenegger el
21

Aquí hay una manera:

with open("filename.txt", "rb") as f:
    contents = f.read().decode("UTF-8")
Flimm
fuente
44
esto obviamente no funciona si tuvieras planes diferentes paraf
user5359531
8

Esto puede hacer el truco:

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)

Entonces puede mantener su código de la manera python3.

Tenga en cuenta que algunas API como newline, closefd, openerno funcionan

usuario2395922
fuente
1
puedes revertir la condición para evitar eso pass.
bfontaine
2

Si está utilizando six, puede probar esto, mediante el cual utiliza la última API de Python 3 y puede ejecutarse en 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.')

Y, el abandono del soporte de Python 2 es solo eliminar todo lo relacionado con six.

YaOzI
fuente