将文档字符串添加到 __slots__ 描述符?
Adding docstring to __slots__ descriptor?
我正在编写一个配置卷的存储自动化模块。我没有传递在存储控制器上实际创建卷所需的六个或更多参数,而是使用 __slots__
创建了一个参数 class,它被传递到 create 方法中,如下所示:
from mock import Mock
from six import with_metaclass
class VolumeParameterMeta(type):
def __new__(mcs, name, bases, dct):
# set __slots__ docstrings here?
return super(VolumeParameterMeta, mcs).__new__(mcs, name, bases, dct)
class VolumeParameter(with_metaclass(VolumeParameterMeta, object)):
__slots__ = ('name', 'size', 'junctionPath', 'svmName', 'securityStyle'
'spaceReserve')
def __init__(self, name):
self.name = name
class Volume(object):
def __init__(self, driver, name):
self.driver = driver
self.name = name
@classmethod
def create(cls, driver, param):
# do sanity check on param (volume not too large, etc)
driver.provision(param)
return cls(driver, param.name)
if __name__ == '__main__':
param = VolumeParameter('volume1')
param.svmName = 'vserver1'
param.junctionPath = '/some/path'
param.size = 2 ** 30
param.spaceReserve = param.size * 0.1
param.securityStyle = 'mixed'
volume = Volume.create(driver=Mock(), param=param)
除了一个小例外,上面的例子效果很好。如何向参数 class 中的描述符添加文档字符串并不明显。 似乎 应该可以使用 metaclass,但是在实例化 metaclass 时未定义描述符。
我很清楚有些人可能不同意我使用 __slots__
的副作用,但我喜欢它有助于消除拼写错误。尝试设置一个不存在的参数并引发 AttributeError
。全部没有任何样板代码。让它在卷创建时失败可以说是更 Pythonic,但结果将是使用默认值而不是拼写错误的参数。这实际上是一个无声的失败。
我意识到可以简单地扩展参数 class 文档字符串,但是 pydoc
和其他文档构建脚本的结果是 class 和空 每个描述符的文档字符串。
肯定有办法为 __slots__
创建的描述符定义文档字符串?也许除了插槽之外还有另一种方式?可变 namedtuple
或类似的?
不,没有选项可以将文档字符串添加到为 __slots__
中定义的名称生成的描述符对象。在这方面,它们就像常规的非描述符属性。
在没有插槽的常规对象上,您最多可以添加注释并设置默认值:
class VolumeParameter(object):
# the name of the volume (str)
name = ''
# volume size in bytes (int)
size = 0
# ...
即使您将所有这些属性声明为 __slots__
。
您可以 'wrap' 另一个描述符中的每个槽以 property
对象的形式:
class VolumeParameterMeta(type):
@staticmethod
def _property_for_name(name, docstring):
newname = '_' + name
def getter(self):
return getattr(self, newname)
def setter(self, value):
setattr(self, newname, value)
def deleter(self):
delattr(self, newname)
return newname, property(getter, setter, deleter, docstring)
def __new__(mcs, name, bases, dct):
newslots = []
clsslots = dct.pop('__slots__', ())
slotdocs = dct.pop('__slot_docs__', {})
if isinstance(clsslots, str):
clsslots = clsslots.split()
for name in clsslots:
newname, prop = mcs._property_for_name(name, slotdocs.get(name))
newslots.append(newname)
dct[name] = prop
if newslots:
dct['__slots__'] = tuple(newslots)
return super(VolumeParameterMeta, mcs).__new__(mcs, name, bases, dct)
class VolumeParameter(with_metaclass(VolumeParameterMeta, object)):
__slots__ = ('name', 'size', 'junctionPath', 'svmName', 'securityStyle'
'spaceReserve')
__slot_docs__ = {
'name': 'the name of the volume (str)',
# ...
}
请注意,这几乎使 class 上的描述符数量翻了一番:
>>> pprint(VolumeParameter.__dict__)
mappingproxy({'__doc__': None,
'__module__': '__main__',
'__slots__': ('_name',
'_size',
'_junctionPath',
'_svmName',
'_securityStylespaceReserve'),
'_junctionPath': <member '_junctionPath' of 'securityStylespaceReserve' objects>,
'_name': <member '_name' of 'securityStylespaceReserve' objects>,
'_securityStylespaceReserve': <member '_securityStylespaceReserve' of 'securityStylespaceReserve' objects>,
'_size': <member '_size' of 'securityStylespaceReserve' objects>,
'_svmName': <member '_svmName' of 'securityStylespaceReserve' objects>,
'junctionPath': <property object at 0x105edbe08>,
'name': <property object at 0x105edbd68>,
'securityStylespaceReserve': <property object at 0x105edb098>,
'size': <property object at 0x105edbdb8>,
'svmName': <property object at 0x105edb048>})
但现在你的属性至少有文档字符串:
>>> VolumeParameter.name.__doc__
'the name of the volume (str)'
我正在编写一个配置卷的存储自动化模块。我没有传递在存储控制器上实际创建卷所需的六个或更多参数,而是使用 __slots__
创建了一个参数 class,它被传递到 create 方法中,如下所示:
from mock import Mock
from six import with_metaclass
class VolumeParameterMeta(type):
def __new__(mcs, name, bases, dct):
# set __slots__ docstrings here?
return super(VolumeParameterMeta, mcs).__new__(mcs, name, bases, dct)
class VolumeParameter(with_metaclass(VolumeParameterMeta, object)):
__slots__ = ('name', 'size', 'junctionPath', 'svmName', 'securityStyle'
'spaceReserve')
def __init__(self, name):
self.name = name
class Volume(object):
def __init__(self, driver, name):
self.driver = driver
self.name = name
@classmethod
def create(cls, driver, param):
# do sanity check on param (volume not too large, etc)
driver.provision(param)
return cls(driver, param.name)
if __name__ == '__main__':
param = VolumeParameter('volume1')
param.svmName = 'vserver1'
param.junctionPath = '/some/path'
param.size = 2 ** 30
param.spaceReserve = param.size * 0.1
param.securityStyle = 'mixed'
volume = Volume.create(driver=Mock(), param=param)
除了一个小例外,上面的例子效果很好。如何向参数 class 中的描述符添加文档字符串并不明显。 似乎 应该可以使用 metaclass,但是在实例化 metaclass 时未定义描述符。
我很清楚有些人可能不同意我使用 __slots__
的副作用,但我喜欢它有助于消除拼写错误。尝试设置一个不存在的参数并引发 AttributeError
。全部没有任何样板代码。让它在卷创建时失败可以说是更 Pythonic,但结果将是使用默认值而不是拼写错误的参数。这实际上是一个无声的失败。
我意识到可以简单地扩展参数 class 文档字符串,但是 pydoc
和其他文档构建脚本的结果是 class 和空 每个描述符的文档字符串。
肯定有办法为 __slots__
创建的描述符定义文档字符串?也许除了插槽之外还有另一种方式?可变 namedtuple
或类似的?
不,没有选项可以将文档字符串添加到为 __slots__
中定义的名称生成的描述符对象。在这方面,它们就像常规的非描述符属性。
在没有插槽的常规对象上,您最多可以添加注释并设置默认值:
class VolumeParameter(object):
# the name of the volume (str)
name = ''
# volume size in bytes (int)
size = 0
# ...
即使您将所有这些属性声明为 __slots__
。
您可以 'wrap' 另一个描述符中的每个槽以 property
对象的形式:
class VolumeParameterMeta(type):
@staticmethod
def _property_for_name(name, docstring):
newname = '_' + name
def getter(self):
return getattr(self, newname)
def setter(self, value):
setattr(self, newname, value)
def deleter(self):
delattr(self, newname)
return newname, property(getter, setter, deleter, docstring)
def __new__(mcs, name, bases, dct):
newslots = []
clsslots = dct.pop('__slots__', ())
slotdocs = dct.pop('__slot_docs__', {})
if isinstance(clsslots, str):
clsslots = clsslots.split()
for name in clsslots:
newname, prop = mcs._property_for_name(name, slotdocs.get(name))
newslots.append(newname)
dct[name] = prop
if newslots:
dct['__slots__'] = tuple(newslots)
return super(VolumeParameterMeta, mcs).__new__(mcs, name, bases, dct)
class VolumeParameter(with_metaclass(VolumeParameterMeta, object)):
__slots__ = ('name', 'size', 'junctionPath', 'svmName', 'securityStyle'
'spaceReserve')
__slot_docs__ = {
'name': 'the name of the volume (str)',
# ...
}
请注意,这几乎使 class 上的描述符数量翻了一番:
>>> pprint(VolumeParameter.__dict__)
mappingproxy({'__doc__': None,
'__module__': '__main__',
'__slots__': ('_name',
'_size',
'_junctionPath',
'_svmName',
'_securityStylespaceReserve'),
'_junctionPath': <member '_junctionPath' of 'securityStylespaceReserve' objects>,
'_name': <member '_name' of 'securityStylespaceReserve' objects>,
'_securityStylespaceReserve': <member '_securityStylespaceReserve' of 'securityStylespaceReserve' objects>,
'_size': <member '_size' of 'securityStylespaceReserve' objects>,
'_svmName': <member '_svmName' of 'securityStylespaceReserve' objects>,
'junctionPath': <property object at 0x105edbe08>,
'name': <property object at 0x105edbd68>,
'securityStylespaceReserve': <property object at 0x105edb098>,
'size': <property object at 0x105edbdb8>,
'svmName': <property object at 0x105edb048>})
但现在你的属性至少有文档字符串:
>>> VolumeParameter.name.__doc__
'the name of the volume (str)'