假设我在模块中有这个片段
def func(params):
class MyClass(object):
pass
如何腌制 MyClass 类的实例?
你不能,因为可挑选对象的类定义必须驻留在导入模块的范围内。只需将您的课程放在模块范围内,您就可以开始了。
也就是说,在 Python 中,通过对机器内部(在本例中为 sys.modules )进行一些黑客攻击,几乎无法实现,但我不建议这样做。
您可以通过将类定义作为字符串包含在为实例腌制的数据中并在取消腌制exec()
时通过添加__reduce__()
将类定义传递给可调用对象的方法来解决腌制要求,即类定义是可导入的。这是一个简单的例子来说明我的意思:
from textwrap import dedent
# Scaffolding
definition = dedent('''
class MyClass(object):
def __init__(self, attribute):
self.attribute = attribute
def __repr__(self):
return '{}({!r})'.format(self.__class__.__name__, self.attribute)
def __reduce__(self):
return instantiator, (definition, self.attribute)
''')
def instantiator(class_def, init_arg):
""" Create class and return an instance of it. """
exec(class_def)
TheClass = locals()['MyClass']
return TheClass(init_arg)
# Sample usage
import pickle
from io import BytesIO
stream = BytesIO() # use a memory-backed file for testing
obj = instantiator(definition, 'Foo') # create instance of class from definition
print('obj: {}'.format(obj))
pickle.dump(obj, stream)
stream.seek(0) # rewind
obj2 = pickle.load(stream)
print('obj2: {}'.format(obj2))
输出:
obj: MyClass('Foo')
obj2: MyClass('Foo')
显然,在每个腌制的类实例中包含类定义字符串是低效的,因此冗余可能使其不切实际,具体取决于所涉及的类实例的数量。
这有点难做,因为 Pickle 默认情况下对用户定义类中的对象所做的方式是创建该类的新实例 - 使用对象的__class__.__name__
属性在对象的原始模块中检索其类型。这意味着:pickling 和 unpickling 仅适用于(默认情况下)在它们定义的模块中具有明确定义名称的类。
当在函数内部定义一个类时,通常不会有一个模块级(即全局)变量来保存在函数内部创建的每个类的名称。
pickle 和 npickle 的行为可以通过类上的__getstate__
和__setstate__
方法进行自定义-检查docs
-但即使是它们,为动态类正确执行它也可能很棘手,但我设法为另一个 SO 问题创建了它的工作实现--在这里查看我的答案:
Pickle a dynamic parameterized sub-class
MyClass
定义是函数的局部变量func
。您不能直接创建它的实例,但您可以将其功能映射到新类,然后使用新类,因为它是原始类。这是一个例子:
def func(params):
class MyClass(object):
some_param = 100
def __init__(self, *args):
print "args:", args
def blabla(self):
self.x = 123
print self.some_param
def getme(self):
print self.x
func.func_code
是func
函数的代码,func.func_code.co_consts[2]
包含MyClass
定义的字节码:
In : func.func_code.co_consts
Out:
(None,
'MyClass',
<code object MyClass at 0x164dcb0, file "<ipython-input-35-f53bebe124be>", line 2>)
所以我们需要MyClass
函数的字节码:
In : eval(func.func_code.co_consts[2])
Out:
{'blabla': <function blabla at 0x24689b0>,
'__module__': '__main__',
'getme': <function getme at 0x2468938>,
'some_param': 100,
'__init__': <function __init__ at 0x219e398>}
最后我们用元类创建一个新类,将 MyClass 函数分配给新类:
def map_functions(name, bases, dict):
dict.update(eval(func.func_code.co_consts[2]))
return type(name, bases, dict)
class NewMyClass(object):
__metaclass__ = map_functions
n = NewMyClass(1, 2, 3, 4, 5)
>> args: (1, 2, 3, 4, 5)
n.blabla()
>> 100
n.getme()
>> 123