在加载旧对象时使用 python 的 属性
Using python's property while loading old objects
我有一个相当大的项目,包括一个 class Foo,它最近需要使用 @property
装饰器进行更新以创建自定义 getter 和 setter 方法。
我还在我的硬盘上存储了几个 Foo
的实例,在某些时候我可能需要重新加载。我的问题是,我无法访问这些旧对象上用 属性 修饰的属性。
考虑以下示例:
import pickle
# define Class and create instance
class Foo:
def __init__(self):
self.val = 1
foo = Foo()
# dump foo into file
with open("foo.pickle", 'wb') as handle:
pickle.dump(foo, handle, pickle.HIGHEST_PROTOCOL)
# overwrite and add @property in the class definition
class Foo:
def __init__(self):
self._val = "new_foo"
@property
def val(self):
return self._val
@val.setter
def val(self, val):
self._val = val
foo_new = Foo()
print(foo_new.val)
# reload foo
with open("foo.pickle", "rb") as handle:
foo_old = pickle.load(handle)
# try to access attributes
print(foo_old.val)
最后一行加注:
NameError: name '_val' is not defined
我还有哪些选项可以访问我的存档实例的属性?
编辑:在第二个 Foo 定义的构造函数中将 self.val
更改为 self._val
。
这可能完全是黑客攻击 -- 我不确定。但是,我能够使用以下代码重建从您的第一个 "Foo" class 中腌制的对象;
import pickle
class Foo:
def __init__(self):
self._val = "new_foo"
@property
def val(self):
try:
return self._val
except AttributeError:
self._val = self.__dict__['val']
self.__dict__.pop('val')
return self._val
@val.setter
def val(self, val):
self._val = val
with open("foo.pickle", "rb") as handle:
foo_old = pickle.load(handle)
print(foo_old.val)
一种可能性是使用自定义 Unpickler
,尽管您需要保留旧的 class(使用不同的名称,如果需要则隐藏)并定义转换逻辑旧对象 class 到新对象。这是一个基本示例:
import pickle
# define Class and create instance
class Foo:
def __init__(self):
self.val = 1
foo = Foo()
# dump foo into file
with open("foo.pickle", 'wb') as handle:
pickle.dump(foo, handle, pickle.HIGHEST_PROTOCOL)
# Old class is kept with a new name
FooOld = Foo
# overwrite and add @property in the class definition
class Foo:
def __init__(self):
self._val = "new_foo"
@property
def val(self):
return self._val
@val.setter
def val(self, val):
self._val = val
foo_new = Foo()
print(foo_new.val)
# Custom Unpickler
class FooOldUnpickler(pickle.Unpickler):
def __init__(self, *args, **kwargs):
super(FooOldUnpickler, self).__init__(*args, **kwargs)
def load(self):
obj = super(FooOldUnpickler, self).load()
if type(obj) is FooOld:
# Object conversion logic
newObj = Foo()
newObj.val = obj.val
obj = newObj
return obj
def find_class(self, module, name):
# Use old class instead of new for loaded objects
if module == __name__ and name == 'Foo':
return FooOld
return super(FooOldUnpickler, self).find_class(module, name)
# reload foo
with open("foo.pickle", "rb") as handle:
# Use custom unpickler
foo_old = FooOldUnpickler(handle).load()
# try to access attributes
print(foo_old.val)
泡菜documentation说:
When a class instance is unpickled, its __init__()
method is usually not invoked.
这就是未定义 _val
属性的原因您可以通过在替换 Foo
class 中定义 __new__
方法并设置实例属性来解决此问题那里:
import pickle
# define Class and create instance
class Foo:
def __init__(self):
self.val = 1
foo = Foo()
# dump foo into file
with open("foo.pickle", 'wb') as handle:
pickle.dump(foo, handle, pickle.HIGHEST_PROTOCOL)
# overwrite and add @property in the class definition
class Foo:
def __new__(cls, val=None):
inst = super().__new__(cls)
inst._val = "new_foo" if val is None else val
return inst
@property
def val(self):
return self._val
@val.setter
def val(self, val):
self._val = val
foo_new = Foo()
print(foo_new.val) # -> new_foo
# reload foo
with open("foo.pickle", "rb") as handle:
foo_old = pickle.load(handle)
print(foo_old.val) # -> new_foo
我有一个相当大的项目,包括一个 class Foo,它最近需要使用 @property
装饰器进行更新以创建自定义 getter 和 setter 方法。
我还在我的硬盘上存储了几个 Foo
的实例,在某些时候我可能需要重新加载。我的问题是,我无法访问这些旧对象上用 属性 修饰的属性。
考虑以下示例:
import pickle
# define Class and create instance
class Foo:
def __init__(self):
self.val = 1
foo = Foo()
# dump foo into file
with open("foo.pickle", 'wb') as handle:
pickle.dump(foo, handle, pickle.HIGHEST_PROTOCOL)
# overwrite and add @property in the class definition
class Foo:
def __init__(self):
self._val = "new_foo"
@property
def val(self):
return self._val
@val.setter
def val(self, val):
self._val = val
foo_new = Foo()
print(foo_new.val)
# reload foo
with open("foo.pickle", "rb") as handle:
foo_old = pickle.load(handle)
# try to access attributes
print(foo_old.val)
最后一行加注:
NameError: name '_val' is not defined
我还有哪些选项可以访问我的存档实例的属性?
编辑:在第二个 Foo 定义的构造函数中将 self.val
更改为 self._val
。
这可能完全是黑客攻击 -- 我不确定。但是,我能够使用以下代码重建从您的第一个 "Foo" class 中腌制的对象;
import pickle
class Foo:
def __init__(self):
self._val = "new_foo"
@property
def val(self):
try:
return self._val
except AttributeError:
self._val = self.__dict__['val']
self.__dict__.pop('val')
return self._val
@val.setter
def val(self, val):
self._val = val
with open("foo.pickle", "rb") as handle:
foo_old = pickle.load(handle)
print(foo_old.val)
一种可能性是使用自定义 Unpickler
,尽管您需要保留旧的 class(使用不同的名称,如果需要则隐藏)并定义转换逻辑旧对象 class 到新对象。这是一个基本示例:
import pickle
# define Class and create instance
class Foo:
def __init__(self):
self.val = 1
foo = Foo()
# dump foo into file
with open("foo.pickle", 'wb') as handle:
pickle.dump(foo, handle, pickle.HIGHEST_PROTOCOL)
# Old class is kept with a new name
FooOld = Foo
# overwrite and add @property in the class definition
class Foo:
def __init__(self):
self._val = "new_foo"
@property
def val(self):
return self._val
@val.setter
def val(self, val):
self._val = val
foo_new = Foo()
print(foo_new.val)
# Custom Unpickler
class FooOldUnpickler(pickle.Unpickler):
def __init__(self, *args, **kwargs):
super(FooOldUnpickler, self).__init__(*args, **kwargs)
def load(self):
obj = super(FooOldUnpickler, self).load()
if type(obj) is FooOld:
# Object conversion logic
newObj = Foo()
newObj.val = obj.val
obj = newObj
return obj
def find_class(self, module, name):
# Use old class instead of new for loaded objects
if module == __name__ and name == 'Foo':
return FooOld
return super(FooOldUnpickler, self).find_class(module, name)
# reload foo
with open("foo.pickle", "rb") as handle:
# Use custom unpickler
foo_old = FooOldUnpickler(handle).load()
# try to access attributes
print(foo_old.val)
泡菜documentation说:
When a class instance is unpickled, its
__init__()
method is usually not invoked.
这就是未定义 _val
属性的原因您可以通过在替换 Foo
class 中定义 __new__
方法并设置实例属性来解决此问题那里:
import pickle
# define Class and create instance
class Foo:
def __init__(self):
self.val = 1
foo = Foo()
# dump foo into file
with open("foo.pickle", 'wb') as handle:
pickle.dump(foo, handle, pickle.HIGHEST_PROTOCOL)
# overwrite and add @property in the class definition
class Foo:
def __new__(cls, val=None):
inst = super().__new__(cls)
inst._val = "new_foo" if val is None else val
return inst
@property
def val(self):
return self._val
@val.setter
def val(self, val):
self._val = val
foo_new = Foo()
print(foo_new.val) # -> new_foo
# reload foo
with open("foo.pickle", "rb") as handle:
foo_old = pickle.load(handle)
print(foo_old.val) # -> new_foo