2012-08-04 12 views

Respuesta

1

No se puede, porque las definiciones de clase de los objetos seleccionables deben residir en el alcance de un módulo importado. Simplemente ponga su clase dentro del alcance del módulo y estará listo para comenzar.

Dicho esto, en Python hay muy poco que no se puede lograr con un poco de hackear el interior de la maquinaria (sys.modules en este caso), pero no lo recomendaría.

1

La definición MyClass es una variable local para la función func. No puede crear directamente una instancia de la misma, pero puede asignar sus funciones a una nueva clase, y luego usar la nueva clase ya que es la original. He aquí un ejemplo:

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 es el código de la función func y func.func_code.co_consts[2] contiene el código de bytes de la definición MyClass:

In : func.func_code.co_consts 
Out: 
(None, 
'MyClass', 
<code object MyClass at 0x164dcb0, file "<ipython-input-35-f53bebe124be>", line 2>) 

por lo que necesitamos el código de bytes para los MyClass funciones:

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>} 

Y finalmente creamos una nueva clase con metaclase, que asigna la función MyClass ns hasta la nueva clase:

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 
+0

Interesante ... pero no me queda claro cómo se puede utilizar para resumir una instancia de la clase definida en la función, ni se pueden recortar objetos de código. – martineau

1

Puede solucionar el requisito de la salmuera que las definiciones de clase sean importarse mediante la inclusión de la definición de clase como una cadena en los datos en escabeche para la instancia y exec() buyendo usted mismo cuando deserialiación mediante la adición de una __reduce__() método que pasa la definición de clase a un invocable. Aquí está un ejemplo trivial que ilustra lo que quiero decir:

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

Salida:

obj: MyClass('Foo') 
obj2: MyClass('Foo') 

Obviamente, es ineficaz para incluir la cadena de definición de clase con cada instancia de clase en escabeche, por lo que la redundancia puede hacer que sea poco práctico, dependiendo de el número de instancias de clase involucradas.

1

Esto es algo difícil de hacer porque la forma en que Pickle hace con objetos de clases definidas por el usuario es crear una nueva instancia de la clase utilizando el atributo __class__.__name__ del objeto para recuperar su tipo en el módulo original del objeto. Lo que significa que: decapado y descarte solo funciona (por defecto) para las clases que tienen nombres bien definidos en el módulo que se definen.

Cuando se define una clase dentro de una función, usualmente no habrá una variable de nivel de módulo (es decir, global) que contenga el nombre de cada clase que se creó dentro de la función.

El comportamiento de la salmuera y npickle se puede personalizar a través de los métodos __getstate__ y __setstate__ en la clase - comprobar el docs - pero incluso ellos, hacer las cosas bien para la clase dinámica puede ser complicado, pero me las arreglé para crear una implementación funcional de para otro SOpregunta - marcar mi respuesta aquí: Pickle a dynamically parameterized sub-class

Cuestiones relacionadas