Desfazer a seleção de um objeto python 2 com python 3


129

Eu estou querendo saber se existe uma maneira de carregar um objeto que foi em conserva em Python 2.4, com Python 3.4.

Eu tenho executado o 2to3 em uma grande quantidade de código legado da empresa para atualizá-lo.

Feito isso, ao executar o arquivo, recebo o seguinte erro:

  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)

Observando o objeto em conserva na disputa, é um dictem um dict, contendo chaves e valores do tipo str.

Então, minha pergunta é: Existe uma maneira de carregar um objeto, originalmente em conserva no python 2.4, com o python 3.4?


1
O Python 2.4 possui o jsonmódulo? Talvez você possa escrever um script 2.4 que remova o objeto e o salve como objeto json e, em seguida, escreva um script 3.4 que leia o objeto json e salve-o como um objeto pickle compatível com 3.4. Essa seria uma operação única executada em todos os seus arquivos pickle.
— Kevin

Eu estava pensando em moldes semelhantes, considerando que estes são dicts Eu acho que eu poderia apenas mudar sys.stdout para um arquivo e imprimi-los, mas eu quero ver se eu posso carregá-los primeiro
— NDevox

Pergunta relacionada que diz respeito especificamente às datetime: stackoverflow.com/questions/24805105/…
— John Y

Respostas:


189

Você precisará dizer pickle.load()como converter dados de Python bytestring em strings do Python 3 ou pode pickledeixá-los em bytes.

O padrão é tentar decodificar todos os dados da cadeia como ASCII, e essa decodificação falhará. Veja a pickle.load()documentação :

Os argumentos opcionais da palavra-chave são fix_imports , codificação e erros , usados ​​para controlar o suporte de compatibilidade para o fluxo de pickle gerado pelo Python 2. Se fix_imports for verdadeiro, pickle tentará mapear os nomes antigos do Python 2 para os novos nomes usados ​​no Python 3. codificação e erros informam ao pickle como decodificar instâncias de string de 8 bits selecionadas pelo Python 2; esses padrões são 'ASCII' e 'strict', respectivamente. A codificação pode ser 'bytes' para ler essas instâncias de cadeia de 8 bits como objetos de bytes.

Definir a codificação como latin1permite importar os dados diretamente:

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

mas você precisará verificar se nenhuma de suas seqüências de caracteres foi decodificada usando o codec errado; O Latin-1 funciona para qualquer entrada, pois mapeia os valores de bytes de 0 a 255 para os primeiros 256 pontos de código Unicode diretamente.

A alternativa seria carregar os dados encoding='bytes'e decodificar todas as byteschaves e valores posteriormente.

Observe que, até as versões Python anteriores a 3.6.8, 3.7.2 e 3.8.0, a remoção dos datetimedados do objeto Python 2 é interrompida, a menos que você o use encoding='bytes'.


1
Como isso pode ser tornado compatível com o Python 2? Aparentemente, o argumento de codificação não está presente no Python 2.
— EpicAdv

2
@ EpicAdv: você não precisa tornar esse código compatível com o Python 2; Esta pergunta é sobre como carregar Python 2 pickles em Python 3. Drop the encodingpalavra-chave por completo para Python 2.
— Martijn Pieters

10
@EpicAdv: você pode criar um dicionário pickle_options vazio para python 2 ou que possui 'encoding': 'latin1'e enviar ** pickle_options para pickle. Dessa forma, ele deve ser executado nas duas versões.
— pipefish

@pipefish - Inteligente, mas em algum lugar é necessário detectar qual versão você está usando, para que você também possa fazer mais claramente a chamada de maneira diferente (uma com e outra sem o argumento extra), dependendo da versão. Mas pelo menos você entendeu o comentário do EpicAdv, que o comentário de Martijn não aborda.
— John Y

2
Sei que o datetimecomentário não foi o principal objetivo desta resposta, mas, para futuros leitores, gostaria de ressaltar que mesmo as versões "fixas" do Python 3 ainda precisam encoding='latin-1'desatar as datas do Python 2. Se seus dados selecionados do Python 2 incluirem datetime e bytestrings codificados em algo que não seja o Latin-1, você ainda poderá usar melhor, encoding='bytes'afinal.
— John Y

15

O uso encoding='latin1'causa alguns problemas quando seu objeto contém matrizes numpy.

Usar encoding='bytes'será melhor.

Consulte esta resposta para obter uma explicação completa do usoencoding='bytes'


Quais questões? Com o que devo ter cuidado? usando bytesfaz strings em bytes (), então prefiro, latin1se possível, mas não está claro para mim qual é o problema.
— Gulzar

2
@ sreeragh-ar: você poderia dar um exemplo dos problemas que encontrou? Eu tenho um bidimensional numpy.ndarray(numpy 1.14) em conserva em Python 2.7 usando cPickle.dumps(), e unpickling em Python 3 com pickle.loads(..., encoding='latin1')funciona bem.
— djvg 17/01

@djvg Eu enfrentei problemas quando tive que escolher imagens como uma sequência de imagens e removê-las. O código pode ser encontrado aqui. gist.github.com/sreeragh-ar/70205db3a43badbfa69f758faa898be3
— Sreeragh AR

@Gulzar Por favor, veja a lista acima para o problema. As imagens foram corrompidas após o cancelamento.
— Sreeragh AR 17/01
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.