I am trying to generate some class definitions dynamically (for wrapping a C++ extension). The following descriptor works fine except when I try to access the docstring for a field using help(), it gives default documentation for the descriptor rather than the field it self. However when I do help(classname), it retrieves the docstring passed to the descriptor:
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)
"python test.py" prints:
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)
Any idea how one can get the descriptor.__doc__
for help(class.field)
?
And is there a way to bypass this and have something like a getter function for doc in stead of having to store the doc string in the descriptor?
like:
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:
Actually I started with this definition of __get__
:
def __get__(self, obj, dtype=None):
return obj.get_field(self.name)
The problem with this was that when I said:
help(DerivedClass.a)
Python threw an Exception indicating that I was trying to call None.get_field
. Thus help()
is calling the __get__
method with obj=None
and dtype=DerivedClass
. That is why I decided to return the FieldDescriptor instance when obj=None and dtype!=None.
My impression was help(xyz)
tries to display xyz.__doc__
. By that logic, if __get__
returns descriptor_instance
, then descriptor_instance.__doc__
should be printed by help(), which is the case for the whole class [help(DerivedClass)
], but not for the single field [help(DerivedClass.a)
].
What goes on is that when you request help(DerivedClass.a)
- python calculates the expression inside the parentheses - which is the object returned by the descriptor's __get__
method - and them searchs for the help (including docstring) on that object.
A way to have this working, including the dynamic docstring generation, is to have your __get__
method to retudn a dynamically generated object that features the desired doc string. But this object would itself need to be a proper proxy object to the original one, and would create some overhead on your code - and a lot of special cases.
Anyway, the only way to get it working ike you want is to modify the objects returned by __get__
itself, so that they behave like you'd like them to.
Id suggest that if all you want in the help is a bit of information like you are doing, maybe you want the objects returned from your __get__
to be of a class that define a __repr__
method (rather than just a __doc__
string) .
来源:https://stackoverflow.com/questions/10045192/creating-dynamic-docstrings-in-python-descriptor