Desactivar un objeto de Python 2 con Python 3

129

Me pregunto si hay una manera de cargar un objeto que se encurtió en Python 2.4, con Python 3.4.

He estado ejecutando 2to3 en una gran cantidad de código heredado de la compañía para actualizarlo.

Una vez hecho esto, cuando ejecuto el archivo aparece el siguiente error:

  File "H:\fixers - 3.4\addressfixer - 3.4\trunk\lib\address\address_generic.py"
, line 382, in read_ref_files
    d = pickle.load(open(mshelffile, 'rb'))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 1: ordinal
not in range(128)

Mirando el objeto encurtido en disputa, es un dicten a dict, que contiene claves y valores de tipo str.

Entonces mi pregunta es: ¿hay alguna forma de cargar un objeto, originalmente en escabeche en Python 2.4, con Python 3.4?

NDevox
fuente
1
¿Python 2.4 tiene el jsonmódulo? Tal vez podría escribir un script 2.4 que desenrede el objeto y lo guarde como un objeto json, y luego escribir un script 3.4 que lea el objeto json y lo guarde como un objeto pickle compatible con 3.4. Esta sería una operación de una sola vez que ejecuta en todos sus archivos pickle.
Kevin
Estaba pensando en líneas similares, considerando que estos son dictos, creo que podría cambiar sys.stdout a un archivo e imprimirlos, pero quiero ver si puedo cargarlos primero
NDevox
Pregunta relacionada que tiene que ver específicamente con fechas
John Y

Respuestas:

189

Tendrá que indicar pickle.load()cómo convertir los datos de la cadena de bytes de Python en cadenas de Python 3, o puede indicar pickleque los deje como bytes.

El valor predeterminado es intentar decodificar todos los datos de cadena como ASCII, y esa decodificación falla. Ver la pickle.load()documentación :

Los argumentos opcionales de palabras clave son fix_imports , codificación y errores , que se utilizan para controlar el soporte de compatibilidad para el flujo de pickle generado por Python 2. Si fix_imports es verdadero, pickle intentará asignar los nombres antiguos de Python 2 a los nuevos nombres utilizados en Python 3. El la codificación y los errores le indican a pickle cómo decodificar instancias de cadena de 8 bits encurtidas por Python 2; estos predeterminados a 'ASCII' y 'estricto', respectivamente. La codificación puede ser 'bytes' para leer estas instancias de cadena de 8 bits como objetos de bytes.

Configurar la codificación le latin1permite importar los datos directamente:

with open(mshelffile, 'rb') as f:
    d = pickle.load(f, encoding='latin1') 

pero deberá verificar que ninguna de sus cadenas esté decodificada usando el códec incorrecto; Latin-1 funciona para cualquier entrada ya que asigna los valores de byte 0-255 a los primeros 256 puntos de código Unicode directamente.

La alternativa sería cargar los datos con encoding='bytes', y decodificar todas las bytesclaves y valores después.

Tenga en cuenta que hasta las versiones de Python anteriores a 3.6.8, 3.7.2 y 3.8.0, el desbloqueo de los datetimedatos del objeto Python 2 está roto a menos que lo use encoding='bytes'.

Martijn Pieters
fuente
1
¿Cómo podría hacerse compatible con Python 2? Aparentemente, el argumento de codificación no está presente para Python 2.
EpicAdv
2
@EpicAdv: no es necesario que este código sea compatible con Python 2; esta pregunta es sobre cómo cargar encurtidos de Python 2 en Python 3. Suelta la encodingpalabra clave por completo para Python 2.
Martijn Pieters
10
@EpicAdv: puede crear un diccionario pickle_options que esté vacío para python 2 o que tenga 'encoding': 'latin1'y enviar ** pickle_options a pickle. De esta manera debería ejecutarse en ambas versiones.
pez pipa
@pipefish: inteligente, pero en algún lugar debe detectar qué versión está utilizando, por lo que también podría hacer la llamada de manera más sencilla de manera diferente (uno con y otro sin el argumento adicional) dependiendo de la versión. Pero al menos tienes la esencia del comentario de EpicAdv, que el comentario de Martijn no aborda en absoluto.
John Y
2
Me doy cuenta de que el datetimecomentario no fue el objetivo principal de esta respuesta, pero para los futuros lectores, me gustaría señalar que incluso las versiones "fijas" de Python 3 todavía requieren encoding='latin-1'que las fechas de Python 2 no estén disponibles. Si los datos en escabeche de Python 2 incluyen fechas y cadenas de bytes codificadas en algo que no sea Latin-1, entonces es mejor que lo use encoding='bytes'después de todo.
John Y
15

El uso encoding='latin1'causa algunos problemas cuando su objeto contiene matrices numpy en él.

Usar encoding='bytes'será mejor.

Consulte esta respuesta para obtener una explicación completa del usoencoding='bytes'

Sreeragh AR
fuente
¿Qué problemas? ¿De qué debo tener cuidado? El uso byteshace cadenas en bytes (), así que prefiero latin1si es posible, pero no me queda claro cuál es el problema.
Gulzar
2
@ sreeragh-ar: ¿Podría dar un ejemplo de los problemas que encontró? Tengo un bidimensional numpy.ndarray(numpy 1.14) en escabeche en Python 2.7 usando cPickle.dumps(), y desbocando en Python 3 con pickle.loads(..., encoding='latin1')buen funcionamiento.
djvg
@djvg Enfrenté problemas cuando tuve que seleccionar las imágenes como una cadena de imágenes y desenlazarlas. El código se puede encontrar aquí. gist.github.com/sreeragh-ar/70205db3a43badbfa69f758faa898be3
Sreeragh AR
@Gulzar Por favor, vea la esencia anterior para el problema. Las imágenes se estaban corrompiendo después de desempaquetar.
Sreeragh AR