2016-08-26 4 views
2

Ich schreibe ein Speicherautomatisierungsmodul, das Volumes bereitstellt. Statt das halbe Dutzend vorbei oder mehr Argumente benötigt, um tatsächlich ein Volumen auf den Speicher-Controller zu erstellen, habe ich eine Parameterklasse __slots__ verwenden, die in die create-Methode wie folgt übergeben wird:DocString zu __slots__ descriptor hinzufügen?

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) 

Das obige Beispiel funktioniert gut mit Eine kleine Ausnahme. Es ist nicht offensichtlich, wie man den Deskriptoren in der Parameterklasse Docstrings hinzufügt. Es scheint wie es mit einer Metaklasse möglich sein sollte, aber die Deskriptoren sind nicht definiert, wenn die Metaklasse instanziiert wird.

Ich bin mir sehr wohl bewusst, dass einige mit meiner Seite einen Nebeneffekt von __slots__ nicht zustimmen können, aber ich mag, dass es hilft, Tippfehler zu beseitigen. Versuchen Sie, einen Parameter einzustellen, der nicht existiert, und der Boom, AttributeError, wird erhöht. Alles ohne Vorwahlcode. Es ist wohl mehr Pythonic, wenn es bei der Volumenerstellung fehlschlägt, aber das Ergebnis wäre, dass ein Standard anstelle eines falsch geschriebenen Parameters verwendet wird. Es wäre effektiv ein stiller Fehler.

Ich weiß, es ist möglich, einfach die docstring Parameterklasse zu erweitern, aber das Ergebnis von pydoc und andere Dokumentation Gebäude Skripte ist eine große Freiform-docstring für die Klasse und leer Docstrings für jede der Deskriptoren.

Sicherlich muss es eine Möglichkeit geben, Docstrings für die von __slots__ erstellten Deskriptoren zu definieren? Vielleicht gibt es einen anderen Weg als Slots? Ein veränderbares namedtuple oder ähnliches?

Antwort

3

Nein, es gibt keine Option zum Hinzufügen von Docstrings zu den Deskriptorobjekten, die für die in __slots__ definierten Namen generiert werden. Sie sind in dieser Hinsicht wie normale Nichtdeskriptorattribute.

In regelmäßigen Objekt ohne Schlitze, können Sie bei den meisten, Kommentare hinzufügen und Standardwerte:

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

Das gleiche gilt nach wie vor, auch wenn Sie diese Attribute alle als __slots__ erklärt.

Sie könnten 'wrap' jeder Schlitz in einem anderen Deskriptor in Form eines property Objekt:

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

Beachten Sie, dass dies fast die Anzahl der Deskriptoren für die Klasse verdoppelt:

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

aber Jetzt haben Ihre Eigenschaften mindestens Docstrings:

>>> VolumeParameter.name.__doc__ 
'the name of the volume (str)' 
+0

Danke für die Lösung und Erklärung! –