Utiliser la docstring d'une méthode pour écraser automatiquement celle d'une autre méthode

StackOverflow https://stackoverflow.com/questions/71817

  •  09-06-2019
  •  | 
  •  

Question

Le problème: j'ai une classe qui contient une méthode modèle execute qui appelle une autre méthode _execute . Les sous-classes sont supposées écraser _execute pour implémenter des fonctionnalités spécifiques. Cette fonctionnalité doit être documentée dans la docstring de _execute . Les utilisateurs avancés peuvent créer leurs propres sous-classes pour étendre la bibliothèque. Cependant, un autre utilisateur traitant avec une telle sous-classe ne devrait utiliser que execute . Il ne verra donc pas la docstring correcte s'il utilise help (execute) .

Par conséquent, il serait intéressant de modifier la classe de base de telle sorte que, dans une sous-classe, la chaîne de documents de execute soit automatiquement remplacée par celle de _execute . Des idées sur la façon dont cela pourrait être fait?

Je pensais aux métaclasses pour faire cela, pour rendre cela complètement transparent pour l'utilisateur.

Était-ce utile?

La solution

Eh bien, si la copie de la méthode d'origine dans la sous-classe ne vous dérange pas, vous pouvez utiliser la technique suivante.

import new

def copyfunc(func):
    return new.function(func.func_code, func.func_globals, func.func_name,
                        func.func_defaults, func.func_closure)

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        for key in attrs.keys():
            if key[0] == '_':
                skey = key[1:]
                for base in bases:
                    original = getattr(base, skey, None)
                    if original is not None:
                        copy = copyfunc(original)
                        copy.__doc__ = attrs[key].__doc__
                        attrs[skey] = copy
                        break
        return type.__new__(meta, name, bases, attrs)

class Class(object):
    __metaclass__ = Metaclass
    def execute(self):
        '''original doc-string'''
        return self._execute()

class Subclass(Class):
    def _execute(self):
        '''sub-class doc-string'''
        pass

Autres conseils

Y a-t-il une raison pour laquelle vous ne pouvez pas remplacer directement la fonction execute de la classe de base?

class Base(object):
    def execute(self):
        ...

class Derived(Base):
    def execute(self):
        """Docstring for derived class"""
        Base.execute(self)
        ...stuff specific to Derived...

Si vous ne voulez pas faire ce qui précède:

Les objets de méthode ne supportant pas l'écriture dans l'attribut __ doc __ , vous devez donc modifier __ doc __ dans l'objet de fonction réel. Puisque vous ne voulez pas remplacer celui de la classe de base, vous devez donner à chaque sous-classe sa propre copie de execute :

class Derived(Base):
    def execute(self):
        return Base.execute(self)

    class _execute(self):
        """Docstring for subclass"""
        ...

    execute.__doc__= _execute.__doc__

mais cela ressemble à un moyen détourné de redéfinir execute ...

Regardez le décorateur functools.wraps (); il fait tout cela, mais je ne sais pas si vous pouvez le faire tourner dans le bon contexte

La chaîne de documentation est stockée dans __ doc __ afin qu'il ne soit pas trop difficile de la réaffecter en fonction de la chaîne de documentation de _execute après coup. .

Fondamentalement:

class MyClass(object):
    def execute(self):
        '''original doc-string'''
        self._execute()

class SubClass(MyClass):
    def _execute(self):
        '''sub-class doc-string'''
        pass

    # re-assign doc-string of execute
    def execute(self,*args,**kw):
        return MyClass.execute(*args,**kw)
    execute.__doc__=_execute.__doc__

Execute doit être à nouveau déclaré en indiquant que la chaîne de documentation est attachée à la version de execute pour la SubClass et non pour MyClass (qui interfèrerait sinon avec d'autres sous-classes).

Ce n'est pas une façon très ordonnée de le faire, mais du POV de l'utilisateur d'une bibliothèque, cela devrait donner le résultat souhaité. Vous pouvez ensuite résumer cela dans une méta-classe afin de faciliter la tâche des personnes qui font des sous-classes.

Je conviens que la méthode la plus simple et la plus pythonique consiste à simplement redéfinir execute dans vos sous-classes et à l'appeler la méthode execute de la classe de base:

class Sub(Base):
    def execute(self):
        """New docstring goes here"""
        return Base.execute(self)

Ceci est très peu de code pour accomplir ce que vous voulez; le seul inconvénient est que vous devez répéter ce code dans chaque sous-classe qui étend Base. Cependant, c’est un petit prix à payer pour le comportement que vous souhaitez.

Si vous voulez une méthode bâclée et détaillée pour vous assurer que la docstring for execute est générée de manière dynamique, vous pouvez utiliser le protocole de descripteur, qui consisterait beaucoup moins en code que les autres propositions présentées ici. Ceci est gênant car vous ne pouvez pas définir un descripteur sur une fonction existante, ce qui signifie que execute doit être écrit en tant que classe séparée avec une méthode __ call __ .

Voici le code pour le faire, mais gardez à l'esprit que mon exemple ci-dessus est beaucoup plus simple et plus pythonique:

class Executor(object):
    __doc__ = property(lambda self: self.inst._execute.__doc__)

    def __call__(self):
        return self.inst._execute()

class Base(object):
    execute = Executor()

class Sub(Base):
    def __init__(self):
        self.execute.inst = self

    def _execute(self):
        """Actually does something!"""
        return "Hello World!"

spam = Sub()
print spam.execute.__doc__  # prints "Actually does something!"
help(spam)                  # the execute method says "Actually does something!"
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top