2009-03-06 35 views
6

Bỏ qua dù việc sử dụng các isinstance is harmful, tôi đã chạy vào câu hỏi hóc búa sau khi cố gắng đánh giá isinstance sau serializing/deserializing một đối tượng thông qua Pickle:Tại sao tôi nhận được hành vi không mong muốn trong Python đang phục hồi sau khi tẩy?

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__) 

bất cứ ai có thể làm sáng tỏ về lý do tại sao isinstance sẽ thất bại trong này tình hình? Nói cách khác, tại sao Python nghĩ rằng các đối tượng này là hai lớp khác nhau? Khi tôi xóa định nghĩa lớp thứ hai, isinstance hoạt động tốt.

+0

Tại sao bạn thay thế lớp học? Bạn đang tạo một đối tượng mới có tên tương tự. Nhưng vấn đề là gì? –

+0

Đó là một ví dụ về đồ chơi. Trong thực tế, giả sử tôi muốn chọn một vật, gửi nó qua dây và tháo nó ra phía bên kia. Đầu nhận sẽ cần phải có một định nghĩa riêng biệt của lớp, đó là những gì tôi đang cố gắng để chứng minh ở đây. –

+0

@Ben Hoffstein: Ngoại trừ bạn không phải vì đó là tất cả trong một quá trình. Hãy thử chia nó thành hai để tạo ra một ví dụ thực tế hơn. –

Trả lời

4

Đây là cách thức hoạt động 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 

Để tìm và thuyết minh một lớp. Vì vậy, tất nhiên nếu bạn thay thế một lớp học với một lớp được đặt tên giống hệt nhau, thì klass = getattr(mod, name) sẽ trả về lớp mới, và cá thể sẽ thuộc lớp mới, và vì vậy việc khôi phục sẽ thất bại.

+0

Cảm ơn bạn, có ý nghĩa. Chỉ cần cố gắng tìm ra cách điều này tác động đến khái niệm gửi các đối tượng được tuần tự hóa giữa một máy chủ và máy khách nơi các định nghĩa lớp sẽ khác biệt về mặt vật lý. –

+0

Nó sẽ không ảnh hưởng đến nó. Vì bạn không bao giờ có thể gọi điện thoại giữa hai trình thông dịch riêng biệt. Vì vậy, chúng sẽ là các lớp khác nhau trong các trình thông dịch khác nhau, nhưng hoạt động giống nhau. Giả sử bạn đang chia sẻ mã. –

+0

Oh và nhân tiện, không bao giờ gửi dưa chua qua dây. ví dụ: (không chạy) pickle.loads ("cposix \ nsystem \ np0 \ n (S'cat/etc/passwd '\ np1 \ ntp2 \ nRp3 \ n.") –

2

Thay đổi mã của bạn để in id của x.__class__x2.__class__ và bạn sẽ thấy rằng họ là khác nhau:

$ python foo4.py 
199876736 
200015248 
4

Câu trả lời rõ ràng, bởi vì không phải cùng lớp của nó.

Đây là lớp tương tự nhưng không giống nhau.

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 
Các vấn đề liên quan