2012-04-06 10 views
6

Estoy intentando generar algunas definiciones de clase dinámicamente (para envolver una extensión de C++). El siguiente descriptor funciona bien, excepto cuando trato de acceder a la docstring para un campo usando help(), proporciona la documentación predeterminada para el descriptor en lugar del campo en sí. Sin embargo cuando hago ayuda (nombre de clase), recupera la cadena de documentación se pasa al descriptor:Creación de cadenas de documentos dinámicas en el descriptor de Python

class FieldDescriptor(object): 
    def __init__(self, name, doc='No documentation available.'): 
     self.name = name 
     self.__doc__ = doc 

    def __get__(self, obj, dtype=None): 
     if obj is None and dtype is not None: 
      print 'Doc is:', self.__doc__ 
      return self 
     return obj.get_field(self.name) 

    def __set__(self, obj, value): 
     obj.set_field(self.name, value) 

class TestClass(object): 
    def __init__(self): 
     self.fdict = {'a': None, 'b': None} 

    def get_field(self, name): 
     return self.fdict[name] 

    def set_field(self, name, value): 
     self.fdict[name] = value 

fields = ['a', 'b'] 
def define_class(class_name, baseclass): 
    class_obj = type(class_name, (baseclass,), {}) 
    for field in fields: 
     setattr(class_obj, field, FieldDescriptor(field, doc='field %s in class %s' % (field, class_name))) 
    globals()[class_name] = class_obj 


if __name__ == '__main__': 
    define_class('DerivedClass', TestClass) 
    help(DerivedClass.a) 
    help(DerivedClass) 
    v = DerivedClass() 
    help(v.a) 

imprime "pitón" test.py:

 
Doc is: field a in class DerivedClass 
Help on FieldDescriptor in module __main__ object: 

class FieldDescriptor(__builtin__.object) 
| Methods defined here: 
| 
| __get__(self, obj, dtype=None) 
| 
| __init__(self, name, doc='No documentation available.') 
| 
| __set__(self, obj, value) 
| 
| ---------------------------------------------------------------------- 
| Data descriptors defined here: 
| 
| __dict__ 
|  dictionary for instance variables (if defined) 
| 
| __weakref__ 
|  list of weak references to the object (if defined) 

Doc is: field a in class DerivedClass 
Doc is: field b in class DerivedClass 
Help on class DerivedClass in module __main__: 

class DerivedClass(TestClass) 
| Method resolution order: 
|  DerivedClass 
|  TestClass 
|  __builtin__.object 
| 
| Data descriptors defined here: 
| 
| a 
|  field a in class DerivedClass 
| 
| b 
|  field b in class DerivedClass 
| 
| ---------------------------------------------------------------------- 
| Methods inherited from TestClass: 
| 
| __init__(self) 
| 
| get_field(self, name) 
| 
| set_field(self, name, value) 
| 
| ---------------------------------------------------------------------- 
| Data descriptors inherited from TestClass: 
| 
| __dict__ 
|  dictionary for instance variables (if defined) 
| 
| __weakref__ 
|  list of weak references to the object (if defined) 

Help on NoneType object: 

class NoneType(object) 
| Methods defined here: 
| 
| __hash__(...) 
|  x.__hash__() hash(x) 
| 
| __repr__(...) 
|  x.__repr__() repr(x) 

alguna idea de cómo se puede conseguir el descriptor.__doc__ para help(class.field) ? ¿Y hay alguna manera de eludir esto y tener algo así como una función getter para doc en lugar de tener que almacenar la cadena de documentación en el descriptor?

gustan:

class FieldDescriptor(object): 
    def __init__(self, name, doc='No documentation available.'): 
     self.name = name 
     self.__doc__ = doc 

    def __get__(self, obj, dtype=None): 
     if obj is None and dtype is not None: 
      print 'Doc is:', self.__doc__ 
      return self 
     return obj.get_field(self.name) 

    def __set__(self, obj, value): 
     obj.set_field(self.name, value) 

    # This is what I'd like to have 
    def __doc__(self, obj, dtype): 
     return dtype.generate_docstring(self.name) 

ACTUALIZACIÓN: En realidad empecé con esta definición de __get__:

def __get__(self, obj, dtype=None): 
    return obj.get_field(self.name) 

El problema con esto es que cuando dije:

help(DerivedClass.a) 

Python lanzó una excepción que indica que estaba tratando de llamar al None.get_field. Por lo tanto, help() está llamando al método __get__ con obj=None y dtype=DerivedClass. Es por eso que decidí devolver la instancia de FieldDescriptor cuando obj = None y dtype! = None. Mi impresión fue help(xyz) intenta mostrar xyz.__doc__. Según esa lógica, si __get__ devuelve descriptor_instance, entonces descriptor_instance.__doc__ se debe imprimir con ayuda(), que es el caso para toda la clase [help(DerivedClass)], pero no para el campo único [help(DerivedClass.a)].

+0

Estoy seguro de que todo está ahí, pero ¿podría aclarar qué llamadas le están dando el resultado de ayuda incorrecto? Es demasiado esfuerzo adivinar lo que esperaba al leer el código. – alexis

+0

Como señaló jsbueno, es la ayuda (DerivedClass.a) la que muestra la documentación del descriptor en lugar de la documentación del campo (guardada en el descriptor .__ doc__). – subhacom

Respuesta

1

Lo que pasa es que cuando se solicita help(DerivedClass.a) - pitón calcula la expresión entre paréntesis - que es el objeto devuelto por el método del descriptor __get__ - y les busque por la ayuda (incluida la cadena de documentación) en ese objeto.

Una forma de hacer que esto funcione, incluida la generación de docstring dinámica, es tener su método __get__ para retudnar un objeto generado dinámicamente que tenga la cadena de documentación deseada. Pero este objeto en sí mismo debería ser un objeto proxy adecuado al original, y crearía una sobrecarga en su código, y en muchos casos especiales.

De todos modos, la única manera de hacerlo funcionar como lo desea es modificar los objetos devueltos por __get__, para que se comporten como usted quisiera.

Id sugieren que si todo lo que quiere en la ayuda es un poco de información como el que está haciendo, tal vez desea que los objetos devueltos desde su __get__ ser de una clase que define un método __repr__ (en lugar de sólo una cadena __doc__)

+0

El enfoque '__repr__' no funciona. En lugar, imprime la documentación de la clase que implementa el '__repr__'. El uso de propiedades en lugar de las descripciones ordinarias funciona, pero sufre el almacenamiento estático de la cadena de documentación. El seguimiento de la función de ayuda con pdb reveló que las pruebas en pydoc '.help()' son independientes de los descriptores definidos en Python, aunque se ocupan de la propiedad y varios descriptores definidos mediante la API C. Gracias por su aporte de todos modos, me animó a probar otras formas. – subhacom

Cuestiones relacionadas