tags:

views:

461

answers:

3

Putting aside whether the use of isinstance is harmful, I have run into the following conundrum when trying to evaluate isinstance after serializing/deserializing an object 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__)

Can anyone shed some light on why isinstance would fail in this situation? In other words, why does Python think these objects are of two different classes? When I remove the second class definition, isinstance works fine.

+1  A: 

Change your code to print the id of x.__class__ and x2.__class__ and you'll see that they are different:

$ python foo4.py
199876736
200015248
Jacob Gabrielson
+1  A: 

The obvious answer, because its not the same class.

Its a similar class, but not the same.

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
truppo
+1  A: 

This is how the unpickler works (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

To find and instantiate a class.

So of course if you replace a class with an identically named class, the klass = getattr(mod, name) will return the new class, and the instance will be of the new class, and so isinstance will fail.

Ali A
Thank you, makes sense. Just trying to figure out how this impacts the concept of sending serialized objects between a server and client where the class definitions will be physically distinct.
Ben Hoffstein
It won't impact it. Since you can never be calling isinstance between two separate interpreters. So they will be different classes in different interpreters, but behave identically. Assuming you are sharing code.
Ali A
Oh and by the way, never ever send pickles over the wire. eg: (don't run this) pickle.loads("cposix\nsystem\np0\n(S'cat /etc/passwd'\np1\ntp2\nRp3\n.")
Ali A
It's okay to send pickles over the wire between processes that don't distrust each other. It's perfectly reasonable, say, between two parts of something that is conceptually the same web server.
Kragen Javier Sitaker