Por que recebo um comportamento inesperado no Python isinstance após decapagem?
Pergunta
Pondo de lado se o uso de isinstance é prejudicial, eu tenho que correr para o seguinte dilema quando se tenta avaliar isinstance depois de serialização / desserialização de um objeto via Pickle:
from __future__ import with_statement
import pickle
# Simple class definition
class myclass(object):
def __init__(self, data):
self.data = data
# Create an instance of the class
x = myclass(100)
# Pickle the instance to a file
with open("c:\\pickletest.dat", "wb") as f:
pickle.dump(x, f)
# Replace class with exact same definition
class myclass(object):
def __init__(self, data):
self.data = data
# Read an object from the pickled file
with open("c:\\pickletest.dat", "rb") as f:
x2 = pickle.load(f)
# The class names appear to match
print x.__class__
print x2.__class__
# Uh oh, this fails...(why?)
assert isinstance(x2, x.__class__)
Alguém pode lançar alguma luz sobre por que isinstance iria falhar nesta situação? Em outras palavras, por que Python acho que esses objetos são de duas classes diferentes? Quando eu remover a segunda definição de classe, isinstance
funciona bem.
Solução
Esta é a forma como as obras unpickler (site-packages / pickle.py):
def find_class(self, module, name):
# Subclasses may override this
__import__(module)
mod = sys.modules[module]
klass = getattr(mod, name)
return klass
Para encontrar e instanciar uma classe.
Então é claro que se você substituir uma classe com uma classe de nome idêntico, o klass = getattr(mod, name)
voltará a nova classe, ea instância será da nova classe, e assim isinstance irá falhar.
Outras dicas
A resposta óbvia, porque não é da mesma classe.
Seu uma classe semelhante, mas não o mesmo.
class myclass(object):
pass
x = myclass()
class myclass(object):
pass
y = myclass()
assert id(x.__class__) == id(y.__class__) # Will fail, not the same object
x.__class__.foo = "bar"
assert y.__class__.foo == "bar" # will raise AttributeError
Alterar o código para imprimir o id
de x.__class__
e x2.__class__
e você verá que eles são diferentes:
$ python foo4.py
199876736
200015248