2012-04-06 4 views
6

Sto provando a generare alcune definizioni di classe dinamicamente (per il wrapping di un'estensione C++). Il seguente descrittore funziona bene tranne quando provo ad accedere alla docstring per un campo usando help(), fornisce la documentazione predefinita per il descrittore piuttosto che il campo stesso. Tuttavia quando lo faccio aiuto (nome classe), recupera il docstring passato al descrittore:Creazione di docstring dinamici nel descrittore 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) 

stampe "pitone" 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) 

Qualsiasi idea di come si può ottenere il descriptor.__doc__ per help(class.field) ? E c'è un modo per aggirare questo e avere qualcosa come una funzione getter per doc invece di dover memorizzare la doc string nel descrittore?

piace:

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) 

UPDATE: In realtà ho iniziato con questa definizione di __get__:

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

Il problema di questo è che quando ho detto:

help(DerivedClass.a) 

Python ha gettato un'eccezione che indica che stavo cercando di chiamare None.get_field. Pertanto, help() chiama il metodo __get__ con obj=None e dtype=DerivedClass. Ecco perché ho deciso di restituire l'istanza FieldDescriptor quando obj = None e dtype! = None. La mia impressione è stata help(xyz) tenta di visualizzare xyz.__doc__. Con questa logica, se __get__ restituisce descriptor_instance, quindi descriptor_instance.__doc__ deve essere stampato da help(), che è il caso per l'intera classe [help(DerivedClass)], ma non per il campo singolo [help(DerivedClass.a)].

+0

Sono sicuro che è tutto lì, ma potresti chiarire quali chiamate ti danno l'output dell'aiuto sbagliato? È troppo sforzo per indovinare cosa ti aspettavi leggendo il codice. – alexis

+0

Come sottolineato da jsbueno, è la guida (DerivedClass.a) che visualizza la documentazione per il descrittore invece della documentazione per il campo (salvata nel descrittore .__ doc__). – subhacom

risposta

1

Che cosa succede è che quando si richiede help(DerivedClass.a) - pitone calcola l'espressione contenuta tra parentesi - che è l'oggetto restituito dal metodo del descrittore __get__ - e ricerche per l'aiuto (compresi docstring) su tale oggetto.

Un modo per farlo funzionare, inclusa la generazione dinamica di docstring, è di avere il tuo metodo __get__ per ritestare un oggetto generato dinamicamente che presenta la stringa doc desiderata. Ma questo oggetto dovrebbe essere di per sé un oggetto proxy adeguato a quello originale e creerebbe un sovraccarico sul codice e molti casi speciali.

In ogni caso, l'unico modo per farlo funzionare ike si desidera è modificare gli oggetti restituiti da __get__ stesso, in modo che si comportano come ti piacerebbe loro.

Id suggeriscono che, se tutto quello che volete nella guida è un po 'di informazioni, come si sta facendo, forse si desidera che gli oggetti restituiti dal __get__ di essere di una classe che definisce un metodo __repr__ (piuttosto che solo una stringa __doc__).

+0

L'approccio '__repr__' non funziona. Invece, stampa la documentazione della classe che implementa il '__repr__'. L'uso delle proprietà invece dei normali descrittori funziona, ma questo è dovuto al fatto che la docstring viene archiviata staticamente. Tracciare la funzione di aiuto con pdb ha rivelato che i test in pydoc .help() 'sono agnostici dei descrittori definiti in Python, sebbene si occupino delle proprietà e dei vari descrittori definiti usando l'API C. Grazie per il tuo contributo comunque, mi ha incoraggiato a provare altri modi. – subhacom