Tengo una clase anidada:

clase WidgetType(objeto): 

clase FloatType(objeto): 
pasar 

clase TextType(objeto): 
pasar 

.. y una oject que se refiere a la clase anidada tipo (no de una instancia de ella) como este

clase ObjectToPickle(objeto): 
def __init__(self): 
auto.tipo = WidgetType.TextType 

Tratando de serializar una instancia de la ObjectToPickle resultados de la clase en:

PicklingError: no se Puede pickle <clase
‘setmanager.app.sitio.widget_data_types.TextType’>

Hay una forma de pepinillo clases anidadas en python?

InformationsquelleAutor prinzdezibel | 2009-12-22

6 Comentarios

  1. 29

    El módulo pickle está tratando de obtener la TextType de la clase del módulo. Pero puesto que la clase es anidada no funciona. jasonjs la sugerencia de trabajo.
    Aquí están las líneas en pickle.py responsable de la mensaje de error:

        try:
            __import__(module)
            mod = sys.modules[module]
            klass = getattr(mod, name)
        except (ImportError, KeyError, AttributeError):
            raise PicklingError(
                "Can't pickle %r: it's not found as %s.%s" %
                (obj, module, name))

    klass = getattr(mod, name) no trabajo en la clase anidada caso de curso. Para demostrar lo que se va a tratar de agregar estas líneas antes de decapado de la instancia:

    import sys
    setattr(sys.modules[__name__], 'TextType', WidgetType.TextType)

    Este código agrega TextType como un atributo para el módulo. El decapado debería funcionar bien. Yo no aconsejamos el uso de este hack, aunque.

    • A+. Corrió hacia el este, y parece que la salmuera es malo para el uso de estos días. También se encuentra el terminal de progressbar, que es realmente bueno!
    • Es un poco más simple alias el interior el nombre de la clase al final del módulo con TextType = WidgetType.TextType, aunque no por ello menos chapucero y todavía los gritos de «quitar el interior de la clase».
  2. 28

    Sé que esto es una muy vieja pregunta, pero nunca he explícitamente visto una solución satisfactoria a esta pregunta aparte de lo obvio, y lo más probable es correcta, la respuesta a re-estructurar el código.

    Por desgracia, no siempre es práctico para hacer tal cosa, en cuyo caso, como un último recurso, se es posible pickle las instancias de las clases que se definen dentro de otra clase.

    La documentación de python para la __reducen__ función estados que puede volver

    Objeto invocable que serán llamados a crear la versión inicial del objeto. El siguiente elemento de la tupla que se proporcionan argumentos para esto se puede llamar.

    Por lo tanto, todo lo que usted necesita es un objeto que puede devolver una instancia de la clase correspondiente. Esta clase debe ser en sí mismo picklable (por lo tanto, debe vivir en la __main__ nivel), y podría ser tan simple como:

    class _NestedClassGetter(object):
        """
        When called with the containing class as the first argument, 
        and the name of the nested class as the second argument,
        returns an instance of the nested class.
        """
        def __call__(self, containing_class, class_name):
            nested_class = getattr(containing_class, class_name)
            # return an instance of a nested_class. Some more intelligence could be
            # applied for class construction if necessary.
            return nested_class()

    Todo lo que queda por lo tanto, es la de devolver los argumentos correspondientes, en un __reduce__ método en FloatType:

    class WidgetType(object):
    
        class FloatType(object):
            def __reduce__(self):
                # return a class which can return this class when called with the 
                # appropriate tuple of arguments
                return (_NestedClassGetter(), (WidgetType, self.__class__.__name__, ))

    El resultado es una clase que está anidada, pero instancias puede ser en escabeche (se necesita trabajar más para volcado de la carga de la __state__ información, pero esto es relativamente sencillo, como por la __reduce__ documentación).

    Esta misma técnica (con ligeras modificaciones del código) puede ser aplicado para la anidación de las clases.

    Totalmente trabajado ejemplo:

    import pickle
    
    
    class ParentClass(object):
    
        class NestedClass(object):
            def __init__(self, var1):
                self.var1 = var1
    
            def __reduce__(self):
                state = self.__dict__.copy()
                return (_NestedClassGetter(), 
                        (ParentClass, self.__class__.__name__, ), 
                        state,
                        )
    
    
    class _NestedClassGetter(object):
        """
        When called with the containing class as the first argument, 
        and the name of the nested class as the second argument,
        returns an instance of the nested class.
        """
        def __call__(self, containing_class, class_name):
            nested_class = getattr(containing_class, class_name)
    
            # make an instance of a simple object (this one will do), for which we can change the
            # __class__ later on.
            nested_instance = _NestedClassGetter()
    
            # set the class of the instance, the __init__ will never be called on the class
            # but the original state will be set later on by pickle.
            nested_instance.__class__ = nested_class
            return nested_instance
    
    
    
    if __name__ == '__main__':
    
        orig = ParentClass.NestedClass(var1=['hello', 'world'])
    
        pickle.dump(orig, open('simple.pickle', 'w'))
    
        pickled = pickle.load(open('simple.pickle', 'r'))
    
        print type(pickled)
        print pickled.var1

    Mi última nota en este es el recuerdo de lo que las otras respuestas han dicho:

    Si usted está en una posición para hacerlo, considere la posibilidad de re-factorizar el código para
    evite las clases anidadas en el primer lugar.

    • Para hacer que funcione en Python 3, reemplazar el w con wb y también r con rb.
    • Para el registro, este funciona como un encanto para las clases generadas dinámicamente (por ejemplo, clases devuelto por una función), también. Aunque debemos señalar que las clases no son técnicamente es la misma en una sola Python proceso, en este caso, a menos que la caché y la reutilización de las clases generadas a partir de los parámetros.
  3. 5

    Si utiliza dill en lugar de pickle, funciona.

    >>> import dill
    >>> 
    >>> class WidgetType(object):
    ...   class FloatType(object):
    ...     pass
    ...   class TextType(object):
    ...     pass
    ... 
    >>> class ObjectToPickle(object):
    ...   def __init__(self):
    ...     self.type = WidgetType.TextType
    ... 
    >>> x = ObjectToPickle()
    >>> 
    >>> _x = dill.dumps(x)
    >>> x_ = dill.loads(_x)
    >>> x_
    <__main__.ObjectToPickle object at 0x10b20a250>
    >>> x_.type
    <class '__main__.TextType'>

    Obtener eneldo aquí: https://github.com/uqfoundation/dill

    • No he podido encontrar información sobre el Eneldo manejo de decapado de los objetos a través de diferentes espacios de nombres (ver this pregunta)?
    • dill puede pickle la clase por referencia o por la definición de la clase (es decir, que se puede adobar el código fuente de la clase).
  4. 5

    En Sage (http://www.sagemath.org), tenemos muchos casos de este decapado problema. La forma en que hemos decidido de forma sistemática de resolver, es poner la exterior clase dentro de un determinado metaclass cuyo objetivo es implementar y ocultar el hack. Tenga en cuenta que este automáticamente se propagan a través de las clases anidadas si hay varios niveles de anidamiento.

    • Más de elaboración o un ejemplo podría ser útil.
  5. 2

    Pickle sólo funciona con las clases definidas en el módulo ámbito de aplicación (nivel superior). En este caso, parece que podría definir las clases anidadas en el ámbito de módulo y, a continuación, configurar como propiedades en WidgetType, suponiendo que hay una razón por la que no acaba de referencia TextType y FloatType en el código. O bien, importar el módulo que está en el uso y widget_type.TextType y widget_type.FloatType.

    • [..]configurar como propiedades en WidgetType[..] Hacerlo rendimientos: «TypeError: no se puede pickle de la propiedad de los objetos»
  6. 1

    De Nadia respuesta es bastante completa – es prácticamente algo que no quieres hacer; está usted seguro de que no puede utilizar la herencia en WidgetTypes en lugar de clases anidadas?

    La única razón para usar las clases anidadas es encapsular las clases de trabajar juntos muy de cerca, su ejemplo específico se parece a una inmediata herencia candidato a mí – no hay ningún beneficio en la anidación WidgetType clases juntos; ponerlas en un módulo y se heredan de la base WidgetType lugar.

    • Me encontré con esto porque yo estaba usando cPickle con hashlib.sha1() para obtener un hash de un objeto dado ejemplo. El uso de un módulo de terceros, dpkt, me encontré con la necesidad de analizar datos ICMP de un libpcap archivo y descubrió que dpkt los usos de las clases anidadas en tanto ICMP() y ICMP6(). Cambiar el código en dpkt del icmp.py obras aorund esto, pero no es una solución práctica en otros sistemas que no controlo. Por lo tanto, su respuesta, mientras sensible, no se aplican a todos los casos debido a cómo la gente va a escribir su código.

Dejar respuesta

Please enter your comment!
Please enter your name here