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.