2012-04-06 62 views
6

Próbuję generować niektóre definicje klas dynamicznie (do pakowania rozszerzenia C++). Poniższy deskryptor działa dobrze, z wyjątkiem sytuacji, gdy próbuję uzyskać dostęp do docstringu dla pola za pomocą help(), to daje domyślną dokumentację dla deskryptora, a nie samego pola. Jednak kiedy robię pomocy (classname), pobiera docstring przekazany do Eurovoc:Tworzenie dynamicznych docstrings w deskryptorze 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) 

"pyton test.py" drukuje:

 
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) 

Każdy pomysł w jaki sposób można uzyskać descriptor.__doc__ dla help(class.field) ? I czy istnieje sposób, aby ominąć to i mieć coś w rodzaju funkcji gettera dla dokumentu zamiast przechowywania ciągu dokumentacyjnego w deskryptorze?

lubię:

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: Właściwie zacząłem z tej definicji __get__:

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

Problemem było to, że kiedy powiedział:

help(DerivedClass.a) 

Python rzucił wyjątek wskazujący, że próbuję wywołać None.get_field. Tak więc help() wywołuje metodę __get__ z obj=None i dtype=DerivedClass. Dlatego zdecydowałem się zwrócić instancję FieldDescriptor, gdy obj = None i dtype! = None. Moje wrażenie było help(xyz) próbuje wyświetlić xyz.__doc__. Zgodnie z tą logiką, jeśli __get__ zwraca descriptor_instance, wówczas descriptor_instance.__doc__ powinno zostać wydrukowane przez help(), co ma miejsce w przypadku całej klasy, ale nie w przypadku pojedynczego pola [].

+0

Jestem pewien, że wszystko tam jest, ale czy mógłbyś wyjaśnić, które połączenia podają ci nieprawidłową pomoc? Zbyt wiele wysiłku, aby odgadnąć, czego się spodziewałeś czytając kod. – alexis

+0

Jak jsbueno zwrócił uwagę, to jest to help (DerivedClass.a), który wyświetla dokumentację dla deskryptora zamiast dokumentacji dla pola (zapisanego w deskryptorze .__ doc__). – subhacom

Odpowiedz

1

Co się dzieje, to, że pytając: help(DerivedClass.a) - Python oblicza wyrażenie w nawiasach - który jest obiektem zwróconym przez metodę deskryptora - __get__ - i wyszukuje pomoc (w tym docstring) na tym obiekcie.

Sposób na to, działający, łącznie z generowaniem dynamicznych docstrukcji, polega na tym, że należy użyć metody __get__ do ponownego wygenerowania dynamicznie generowanego obiektu zawierającego pożądany ciąg dokumentów. Ale ten obiekt sam w sobie musi być właściwym obiektem proxy do oryginalnego obiektu i może spowodować pewne obciążenie kodu - i wiele specjalnych przypadków.

W każdym razie, jedynym sposobem na sprawne działanie jest zmodyfikowanie obiektów zwracanych przez samą __get__, aby zachowywały się tak, jak powinny.

Id sugerują, że jeśli chcemy w pomocy jest trochę informacji, takich jak robisz, może chcesz obiekty wrócił z __get__ się klasy, które definiują metody __repr__ (raczej niż tylko __doc__ ciąg).

+0

Podejście "__repr__" nie działa. Zamiast tego drukuje dokumentację klasy, która implementuje '__repr__'. Używanie właściwości zamiast zwykłych deskryptorów działa, ale cierpi na statyczne przechowywanie docstrukcji. Śledzenie funkcji pomocy za pomocą pdb ujawniło, że testy w pydoc'help() 'są agnostyczne dla deskryptorów zdefiniowanych w Pythonie, ale zajmują się właściwością i różnymi deskryptorami zdefiniowanymi przy użyciu C API. Dzięki za twój wkład i tak zachęcałem mnie do wypróbowania innych sposobów. – subhacom

Powiązane problemy