如何挑选一个写在函数内的类的实例?

时间:2012-08-04 08:09:01

标签: python pickle

假设我在模块中有这个片段

def func(params):
   class MyClass(object):
       pass

如何挑选MyClass类的实例?

4 个答案:

答案 0 :(得分:1)

您不能,因为可选对象的类定义必须位于导入模块的范围内。只需将您的课程放在模块范围内,就可以了。

也就是说,在Python中,通过对机器内部(本例中为sys.modules)进行一些黑客攻击是无法实现的,但我不建议这样做。

答案 1 :(得分:1)

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_codefunc函数的代码,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

答案 2 :(得分:1)

您可以通过将类定义作为字符串包含在实例的pickle数据中来解决可以导入类定义的pickle要求,并通过添加exec()方法进行unpickling时自己__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')

显然,将每个类实例选中的类定义字符串包含在内是没有效率的,因此冗余可能会使其不切实际,具体取决于所涉及的类实例的数量。

答案 3 :(得分:1)

这有点难以做到,因为Pickle默认使用来自用户定义类的对象的方式是创建类的新实例 - 使用对象的__class__.__name__属性在对象的原始模块中检索其类型。这意味着:对于在定义模块中具有明确定义名称的类,pickling和unpickling仅适用于(默认情况下)。

当一个人在一个函数中定义一个类时,usulay就不会有一个模块级别(即全局)变量,它保存在函数内部创建的每个类的名称。

pickle和npickle的行为可以通过类上的__getstate____setstate__方法进行自定义 - 检查docs - 但即便如此,为动态类做正确的行为也可以棘手,但我设法为另一个SO创建了它的工作实现问题 - 检查我的答案: Pickle a dynamically parameterized sub-class