2010-12-17 6 views
7

Actualmente estoy escribiendo un módulo de serialización en Python que puede serializar clases definidas por el usuario. para hacer esto, necesito obtener el espacio de nombre completo del objeto y escribirlo en un archivo. Puedo usar esa cadena para recrear el objeto.obteniendo la ruta de clase o espacio de nombre de una clase en python, incluso si está anidado

por ejemplo, suponer que tenemos la estructura de la clase siguiente en un archivo denominado A.py

class B: 
    class C: 
     pass 

ahora con la suposición de que my_klass_string es la cadena "A::B::C"

klasses = my_klass_string.split("::") 
if globals().has_key(klasses[0]): 
    klass = globals()[klasses[0]] 
else: 
    raise TypeError, "No class defined: %s} " % klasses[0] 
if len(klasses) > 1: 
    for klass_string in klasses: 
     if klass.__dict__.has_key(klass_string): 
      klass = klass.__dict__[klass_string] 
     else: 
      raise TypeError, "No class defined: %s} " % klass_string    
klass_obj = klass.__new__(klass) 

puedo crear una instancia de la clase C aunque se encuentra en la clase B en el módulo A. el código anterior es equivalente a llamar eval(klass_obj = A.B.C.__new__(A.B.C))

nota: estoy usando __new__() aquí porque estoy reconstitución de un objeto serializado y no quiero a inicializar el objeto como no sé qué parámetros de la clase __init__ métodos toma. Quiero crear el objeto sin llamar init y luego asignar atributos a él más tarde.

de cualquier manera que pueda crear un objeto de la clase A.B.C de una cadena. ¿Cómo voy por el otro lado? ¿Cómo obtengo una cadena que describe la ruta completa a la clase desde una instancia de esa clase, incluso si la clase está anidada?

+0

+1 para una pregunta interesante y bien explicada. –

Respuesta

6

No se puede obtener la "ruta completa a la clase dada una instancia de la clase ", por la sencilla razón de que no hay tal cosa en Python. Para ejemplo, construyendo en su ejemplo:

>>> class B(object): 
...  class C(object): 
...    pass 
... 
>>> D = B.C 
>>> x = D() 
>>> isinstance(x, B.C) 
True 

Cuál debe ser la "ruta de clase" de x ser? D o B.C? Ambos son igualmente válidos, y por lo tanto, Python no le da ningún medio para decirle a uno desde el otro.

De hecho, incluso el módulo de Python pickle ha Problemas decapado objeto x:

>>> import pickle 
>>> t = open('/tmp/x.pickle', 'w+b') 
>>> pickle.dump(x, t) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python2.6/pickle.py", line 1362, in dump 
    Pickler(file, protocol).dump(obj) 
    ... 
    File "/usr/lib/python2.6/pickle.py", line 748, in save_global 
    (obj, module, name)) 
    pickle.PicklingError: Can't pickle <class '__main__.C'>: it's not found as __main__.C 

Así que, en general, no veo otra opción de añadir un atributo a todas sus clases (por ejemplo, _class_path), y su código de serialización sería mirar hacia arriba para grabar el nombre de la clase en el formato serializado:

class A(object): 
    _class_path = 'mymodule.A' 
    class B(object): 
    _class_path = 'mymodule.A.B' 
    ... 

incluso se puede hacer esto de forma automática con some metaclass magic (pero también lea los otros comentarios en the same SO post para advertencias que pueden aplicarse si hace el D=B.C anterior).

Dicho esto, si usted puede limitar su código de serialización a (1) casos de las clases de nuevo estilo, y (2) las clases se definen en el de nivel superior de un módulo, a continuación, puedes copiar lo pickle hace (función save_global en las líneas 730--768 en pickle.py de Python 2.6).

La idea es que todas las clases de nuevo estilo define atributos __name__ y __module__, que son cadenas que se expanden al nombre de clase (como que se encuentran en las fuentes) y el nombre del módulo (como se encuentra en sys.modules); por el ahorro de estos se puede importar más tarde el módulo y obtener una instancia de la clase:

__import__(module_name) 
class_obj = getattr(sys.modules[module_name], class_name) 
+0

+1: excelente resaltado del caso fronterizo que probablemente cause muchos dolores de cabeza. –

+0

Creo que esto responde mejor mi pregunta, sé lo estúpido que estoy tratando de hacer, pero sigue siendo la forma más fácil de lograr mi objetivo. si fuera con un serializador que como JSON o XML tendría que escribir un traductor complejo para tomar los datos JSON y construir los objetos en el lado ruby ​​antes de ordenarlos de esta manera, puedo hacer una carga de estrechez y tener el objeto y ellos volcar ellos de nuevo. el enlace mágico Metaclass ayuda mucho y básicamente describe la única forma de determinar la ruta a una clase anidada. – Ryex

1

Actualmente estoy escribiendo un módulo de serialización en Python que puede serializar las clases definidas por el usuario.

No. La biblioteca estándar ya incluye uno. Dependiendo de cómo cuente, en realidad, incluye al menos dos (pickle y shelve).

+0

sí gracias esto es muy bueno saberlo, especialmente el sentido de que no ayuda. Soy muy consciente de los módulos de serialización internos de pitones, pero elijo escribir el mío porque necesito que funcione en todos los idiomas. He tenido éxito en ruby ​​y ahora estoy trabajando en Python. si quizás puedes responder mi pregunta, por favor edita tu respuesta, eso sería increíble. gracias tu tiempo – Ryex

+0

Sé que hemos estado insistiendo en esto, pero ** ya hay ** formatos de lenguaje cruzado y de host cruzado bien conocidos para la serialización con librerías/analizadores libremente disponibles (o incluso incorporados a la biblioteca estándar). Se llaman XML, JSON, etc. ¿Al menos puede proporcionar una explicación simple de por qué no se ajustan a sus necesidades? –

6

No puede, de ninguna manera razonable y no loca. Supongo que podría encontrar el nombre de clase y el módulo, y luego, para cada nombre de clase, verificar que exista en el módulo, y si no, recorrer todas las clases que existen en el módulo de forma jerárquica hasta que lo encuentre.

Pero como no hay ninguna razón para tener una jerarquía de clases así, no es un problema. :-)

Además, sé que usted no quiere oír esto a estas alturas de su trabajo, pero:

multiplataforma serialización es un tema interesante, pero hacerlo con objetos de este tipo es poco probable ser muy útil, ya que el sistema de destino debe tener exactamente la misma jerarquía de objetos instalada. Por lo tanto, debe tener dos sistemas escritos en dos idiomas diferentes que sean exactamente equivalente. Eso es casi imposible y es probable que no valga la pena.

Por ejemplo, no podría usar ningún objeto de la biblioteca estándar de Pythons, ya que esos no existen en Ruby. El resultado final es que debe crear su propia jerarquía de objetos que al final solo use tipos básicos como cadenas y números. Y en ese caso, sus objetos acaban de convertirse en contención para las primitivas básicas, y entonces también puede serializar todo con JSON o XML de todos modos.

+0

Si tuviera la opción de definir ambos extremos del sistema, entonces no estaría haciendo esto. pero, por desgracia, tengo que cerrar la brecha entre un motor mal diseñado y un editor. y este sistema DOSIS tiene jerarquías de clase como esa. de hecho, creo que parte de él tiene 4 niveles de profundidad. entonces tu respuesta es ir del nombre '__module__' y luego seguir mi camino hacia abajo hasta que encuentre la clase correcta construyendo el árbol en el camino? eso suena como que podría funcionar se siente como un hack sin embargo. ¿Conoces algún truco de bucle o trucos de prueba tipo que podrían ayudar a limitar la cantidad si las iteraciones? – Ryex

+0

Pero para hacer lo que está tratando de hacer ahora * usted * tiene que ser capaz de definir ambos extremos del sistema. Si no puede, entonces NO puede serializar objetos como este. Entonces, pregunta: ¿POR QUÉ están serializando objetos, en vez de solo serializar datos? –

+0

porque esos objetos contienen datos. Tengo un sistema que lee datos de los archivos de Ruby Marshal y tiene datos contenidos en lo que básicamente son estructuras para modificar esos datos en esos archivos. Necesito poder leer Ruby Marshal. Traté de escribir un lector para el formato en Python con la ayuda de un amigo de min y casi me retiro. pero algún error oscuro me impidió leer los archivos de manera confiable, algunos de los archivos funcionaron y algunos no fallaron por razones extrañas. así que decidí escribir un programa de rubí que realmente podría leer y escribir el formato correctamente y usarlo como traductor. – Ryex

0

Hay dos maneras de hacer esto.

Solución 1

La primera de ellas va a través del colector de basura.

B -> __dict__ -> C 

este es el código:

>>> class B(object): 
    class C(object): 
     pass 

>>> gc.get_referrers(B.C) # last element in the list 
[<attribute '__dict__' of 'C' objects>, <attribute '__weakref__' of 'C' objects>, (<class '__main__.C'>, <type 'object'>), {'__dict__': <attribute '__dict__' of 'B' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'B' objects>, 'C': <class '__main__.C'>, '__doc__': None}] 

>>> gc.get_referrers(gc.get_referrers(B.C)[-1]) # first element in this list 
[<class '__main__.B'>, [<attribute '__dict__' of 'C' objects>, <attribute '__weakref__' of 'C' objects>, (<class '__main__.C'>, <type 'object'>), {'__dict__': <attribute '__dict__' of 'B' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'B' objects>, 'C': <class '__main__.C'>, '__doc__': None}]] 

>>> gc.get_referrers(gc.get_referrers(B.C)[-1])[0] 
<class '__main__.B'> 

Algoritmo:

  1. búsqueda de un dictionairy clase con el mismo __module__ como C
  2. tomar la clase, utilice la 'C' atributo
  3. si esta clase está anidada. hacer 1.recurively

Solución 2

uso del archivo de origen. use inspeccionar para obtener las líneas de la clase y escanear hacia arriba las nuevas clases que anidan.

Nota: No conozco ninguna manera clara en python 2, pero Python 3 proporciona una.

Cuestiones relacionadas