2

I'm writing a storage automation module that provisions volumes. Instead of passing the half dozen or more arguments needed to actually create a volume on the storage controller, I created a parameter class using __slots__ that is passed into the create method like this:

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)

上記の例は、1 つの小さな例外を除いてうまく機能します。パラメーター クラスの記述子に docstring を追加する方法は明らかではありません。メタクラスで可能であるように思われますが、メタクラスがインスタンス化されるときに記述子が定義されていません。

の副作用を使用することに同意しない人がいることを痛感して__slots__いますが、タイプミスをなくすのに役立つことは気に入っています。存在しないパラメータを設定してみると、ブーン、AttributeErrorと上がります。定型コードなしですべて。ボリュームの作成時に失敗させるのは間違いなくよりPythonicですが、その結果、スペルが間違っているパラメーターの代わりにデフォルトが使用されることになります。それは事実上、サイレントエラーになります。

パラメータ クラスの docstring を単純に拡張することは可能ですがpydoc、他のドキュメント作成スクリプトの結果は、クラスの大きな自由形式の docstringと各記述子の空の docstring になります。

確かに、によって作成された記述子のdocstringを定義する方法が必要__slots__です? おそらく、スロット以外に別の方法がありますか?可変namedtupleまたは類似?

4

1 に答える 1

3

いいえ、 で定義された名前に対して生成される記述子オブジェクトに docstring を追加するオプションはありません__slots__。その点では、通常の非記述子属性と同様です。

スロットのない通常のオブジェクトでは、せいぜいコメントを追加してデフォルト値を設定するだけです。

class VolumeParameter(object):
     # the name of the volume (str)
     name = ''
     # volume size in bytes (int)
     size = 0
     # ...

これらすべての属性を として宣言した場合でも、同じことが当てはまります__slots__

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)',
        # ...
    }

これにより、クラスの記述子の数がほぼ 2 倍になることに注意してください。

>>> 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>})

しかし今、あなたのプロパティには少なくともdocstringがあります:

>>> VolumeParameter.name.__doc__
'the name of the volume (str)'
于 2016-08-26T17:33:48.523 に答える