139 votes

Que signifie le message "Trop peu de méthodes publiques" de Pylint ?

J'exécute Pylint sur du code et je reçois l'erreur "Too few public methods (0/2)". Que signifie ce message ?

El Documentation sur le Pylint n'est pas utile :

Utilisé quand une classe a trop peu de méthodes publiques, donc soyez sûr que cela en vaut vraiment la peine.

154voto

Blender Points 114729

L'erreur dit essentiellement que les classes ne sont pas destinées à juste stocker des données, car vous traitez essentiellement la classe comme un dictionnaire. Les classes doivent avoir au moins quelques méthodes pour opérer sur les données qu'elles contiennent.

Si votre classe ressemble à ça :

class MyClass(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

Pensez à utiliser un dictionnaire ou un namedtuple à la place. Mais si une classe semble être le meilleur choix, utilisez-la. Pylint ne sait pas toujours ce qui est le mieux.

Notez bien que namedtuple est immuable et les valeurs attribuées lors de l'instanciation ne peuvent être modifiées ultérieurement.

53voto

sage Points 555

Si vous étendez une classe, je vous suggère de désactiver systématiquement cet avertissement et de passer à autre chose, par exemple dans le cas des tâches Celery :

class MyTask(celery.Task):  # pylint: disable=too-few-public-methods                                                                                   
    """base for My Celery tasks with common behaviors; extends celery.Task

    ...             

Même si vous n'étendez qu'une seule fonction, vous avez certainement besoin d'une classe pour faire fonctionner cette technique, et étendre est certainement mieux que de pirater les classes tierces !

27voto

Tomasz Gandor Points 426

C'est un autre cas des règles aveugles de Pylint.

"Les classes ne sont pas destinées à stocker des données" - c'est une fausse affirmation. Les dictionnaires ne sont pas bons pour tout. Un membre de données d'une classe est quelque chose de significatif, un élément de dictionnaire est quelque chose d'optionnel. Preuve : vous pouvez faire dictionary.get('key', DEFAULT_VALUE) pour éviter un KeyError mais il n'y a pas de solution simple __getattr__ par défaut.

Méthodes recommandées pour l'utilisation des structs

Je dois mettre à jour ma réponse. Pour l'instant - si vous avez besoin d'un struct vous avez deux grandes options :

a) Utilisez simplement attrs

Il existe une bibliothèque pour cela :

https://www.attrs.org/en/stable/

import attr

@attr.s
class MyClass(object):  # Or just MyClass: for Python 3
    foo = attr.ib()
    bar = attr.ib()

Ce que vous obtenez en plus : ne pas écrire de constructeurs, valeurs par défaut, validation, __repr__ des objets en lecture seule (pour remplacer namedtuples (même en Python 2) et plus encore.

b) Utiliser dataclasses (Py 3.7+)

Suite au commentaire de hwjp, je recommande aussi dataclasses :

https://docs.python.org/3/library/dataclasses.html

C'est presque aussi bien que attrs et est un mécanisme de bibliothèque standard ("batteries incluses"), sans aucune dépendance supplémentaire, à l'exception de Python 3.7+.

Le reste de la réponse précédente

NamedTuple n'est pas génial - surtout avant l'introduction de la fonction typing.NamedTuple : https://docs.python.org/3/library/typing.html#typing.NamedTuple

  • vous devez absolument vérifier la "classe dérivée de NamedTuple " modèle. Python 2 - namedtuples créées à partir de descriptions de chaînes de caractères - est laid, mauvais et stupide pour la "programmation à l'intérieur des chaînes de caractères".

Je suis d'accord avec les deux réponses actuelles ("envisagez d'utiliser autre chose, mais Pylint n'a pas toujours raison" - la réponse acceptée, et "utilisez Pylint pour supprimer les commentaires"), mais j'ai ma propre suggestion.

Permettez-moi de le souligner une fois de plus : Certains cours sont destinés juste pour stocker des données.

Maintenant, l'option pour également envisager - utiliser property - .

class MyClass(object):
    def __init__(self, foo, bar):
        self._foo = foo
        self._bar = bar

    @property
    def foo(self):
        return self._foo

    @property
    def bar(self):
        return self._bar

Ci-dessus, vous avez des propriétés en lecture seule, ce qui est correct pour les Value Objects (comme ceux de Domain Driven Design), mais vous pouvez également fournir des setters - de cette façon, votre classe sera en mesure de prendre la responsabilité des champs que vous avez - par exemple pour faire de la validation, etc, self.foo = foo au lieu d'un direct self._foo = foo (mais attention, les setters peuvent supposer que d'autres champs sont déjà initialisés, et il faut alors une validation personnalisée dans le constructeur).

5voto

Sean Bradley Points 356

C'est difficile quand votre patron s'attend à ce que le principe de responsabilité unique mais Pylint dit non. Alors ajoutez une deuxième méthode à votre classe pour que celle-ci viole le principe de responsabilité unique. Jusqu'où vous êtes censé aller avec le principe de responsabilité unique est dans l'œil de celui qui regarde.

Ma solution

J'ai ajouté une méthode supplémentaire à ma classe, de sorte qu'elle fait maintenant deux choses.

def __str__(self):
    return self.__class__.__name__

Je me demande juste si je dois diviser ma classe en deux fichiers séparés maintenant, et peut-être aussi les modules.

Le problème est résolu, mais pas avec mes collègues qui passent leur journée à discuter des spécifications, plutôt que de s'y atteler, comme si c'était une question de vie ou de mort.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X