Was bedeutet die Meldung "Zu wenige öffentliche Methoden" von pylint?

110

Ich führe pylint für einen Code aus und erhalte die Fehlermeldung "Zu wenige öffentliche Methoden (0/2)". Was bedeutet diese Nachricht? Die Pylint-Dokumente sind nicht hilfreich:

Wird verwendet, wenn die Klasse zu wenige öffentliche Methoden hat. Stellen Sie also sicher, dass es sich wirklich lohnt.

Monsur
quelle
1
Wie sieht deine Klasse aus? Tut die Klasse etwas anderes als Daten zu speichern?
Blender
1
Die Klasse speichert lediglich Daten.
Monsur
2
Nun, da ist dein Problem. Klassen sind nicht zum Speichern von Daten gedacht. Dafür sind Datenstrukturen wie Wörterbücher und Listen gedacht.
Blender
Interessant, danke! Die Pylint-Fehlermeldung könnte nützlicher gemacht werden. Wie auch immer, zögern Sie nicht, Ihren Kommentar in eine Antwort umzuwandeln, und ich werde zustimmen.
Monsur
6
Aber wo ist die Definition von "wenigen"? Ich habe genau eine Methode. Das ist der Grund, warum die Klasse existiert. Wie definiert Pylint "wenige"? Mehr als 2? Warum?
Zordid

Antworten:

124

Der Fehler besagt im Grunde, dass Klassen nicht nur zum Speichern von Daten gedacht sind , da Sie die Klasse im Grunde genommen als Wörterbuch behandeln. Klassen sollten mindestens einige Methoden haben, um mit den Daten zu arbeiten, die sie enthalten.

Wenn Ihre Klasse so aussieht:

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

Verwenden Sie stattdessen ein Wörterbuch oder ein namedtuple. Wenn eine Klasse die beste Wahl zu sein scheint, verwenden Sie sie. Pylint weiß nicht immer, was am besten ist.

Beachten Sie, dass dies namedtupleunveränderlich ist und die bei der Instanziierung zugewiesenen Werte später nicht mehr geändert werden können.

Mixer
quelle
72
+1 für "pylint weiß nicht, was am besten ist" - verwenden Sie Ihr eigenes Urteilsvermögen, aber in der Regel, wenn Sie eine "Struktur" benötigen, verwenden Sie ein dictoder namedtuple. Verwenden Sie eine Klasse, wenn Sie Ihrem Objekt eine Logik hinzufügen möchten (z. B. wenn beim Erstellen etwas passieren soll, wenn es hinzugefügt wird, müssen einige spezielle Dinge geschehen, wenn Sie einige Operationen daran ausführen und steuern möchten, wie es erstellt wird angezeigt, etc.)
Burhan Khalid
Danke für die ausführlichen Antworten! Mein Anwendungsfall ähnelt dem, was Burhan erwähnt hat. Ich verarbeite die Daten bei ihrer Erstellung.
Monsur
6
Dieser Fehler ist nicht sinnvoll, wenn Sie Meta (Metaklasse) in Ihrer Klassendefinition haben.
alexander_ch
11
namedtuplescheiße - zusätzlich zu der hässlichen Syntax können Sie sie nicht einfach dokumentieren oder Standardwerte bereitstellen.
rr-
6
Jedes Mal, wenn ich verwendet habe, namedtuplehabe ich die Entscheidung bereut. Es ist inkonsistent, sowohl benannte als auch indizierte Zugriffsattribute zuzulassen.
Theorifice
39

Wenn Sie eine Klasse erweitern, ist mein Vorschlag, diese Warnung systematisch zu deaktivieren und fortzufahren, z. B. bei Sellerie-Aufgaben:

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

    ...             

Selbst wenn Sie nur eine einzelne Funktion erweitern, benötigen Sie definitiv eine Klasse, damit diese Technik funktioniert, und das Erweitern ist definitiv besser als das Hacken von Klassen von Drittanbietern!

Salbei
quelle
Wenn ich dieses Diable habe, gibt mir Pre-Commit jetzt: Schlechter Optionswert 'zu wenig öffentliche Methode' (schlechter Optionswert)
Mercury
Haben Sie die 's' on-Methoden angegeben? Ihre Nachricht mit einem schlechten Optionswert enthält sie nicht.
Salbei
4
Eine bessere Möglichkeit, dies zu deaktivieren, ist wahrscheinlich die Einstellung min-public-methods=0im [BASIC]Abschnitt der Konfigurationsdatei. Auf diese Weise können Sie es in eine separate Zeile von all Ihren Dingen disable=(in [MESSAGE CONTROL]) einfügen, was meiner Meinung nach das Hinzufügen detaillierter Kommentare darüber, warum Sie Dinge zusammen mit der Konfigurationsänderung aktiviert und deaktiviert haben, erleichtert.
cjs
15

Dies ist ein weiterer Fall von pylintBlindregeln.

"Klassen sollen keine Daten speichern" - dies ist eine falsche Aussage. Wörterbücher sind nicht für alles gut. Ein Datenelement einer Klasse ist etwas Sinnvolles, ein Wörterbuchelement ist etwas Optionales. Beweis: Sie können tun dictionary.get('key', DEFAULT_VALUE), um ein zu verhindern KeyError, aber es gibt keine einfache __getattr__mit Standard.

BEARBEITEN - empfohlene Methoden zur Verwendung von Strukturen

Ich muss meine Antwort aktualisieren. Im Moment - wenn Sie eine benötigen struct, haben Sie zwei großartige Möglichkeiten:

a) Einfach benutzen attrs

Dies ist eine Bibliothek dafür:

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

Was Sie zusätzlich erhalten: Schreiben von Konstruktoren, Standardwerten, Validierung, __repr__schreibgeschützten Objekten (zu ersetzen namedtuples, auch in Python 2) und mehr.

b) Verwenden Sie dataclasses(Py 3.7+)

Nach dem Kommentar von hwjp empfehle ich außerdem dataclasses:

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

Dies ist fast so gut wie attrsund ist ein Standardbibliotheksmechanismus ("Batterien enthalten") ohne zusätzliche Abhängigkeiten, außer Python 3.7+.

Rest der vorherigen Antwort

NamedTupleist nicht großartig - besonders vor Python 3 typing.NamedTuple: https://docs.python.org/3/library/typing.html#typing.NamedTuple - Sie sollten auf jeden Fall das NamedTupleMuster "Klasse abgeleitet von " überprüfen . Python 2 - namedtupleserstellt aus String-Beschreibungen - ist hässlich, schlecht und "Programmieren in String-Literalen" dumm.

Ich stimme den beiden aktuellen Antworten zu ("Überlegen Sie, etwas anderes zu verwenden, aber Pylint ist nicht immer richtig" - die akzeptierte und "Verwenden Sie einen Kommentar zur Unterdrückung von Pylint"), aber ich habe meinen eigenen Vorschlag.

Lassen Sie mich noch einmal darauf hinweisen: Einige Klassen dienen nur zum Speichern von Daten.

Jetzt die Option auch zu berücksichtigen - use property-ies.

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

Oben haben Sie schreibgeschützte Eigenschaften, die für Value Object in Ordnung sind (z. B. wie bei Domain Driven Design), aber Sie können auch Setter bereitstellen. Auf diese Weise kann Ihre Klasse beispielsweise die Verantwortung für die Felder übernehmen, über die Sie verfügen Um eine Validierung usw. durchzuführen (wenn Sie Setter haben, können Sie diese im Konstruktor zuweisen, dh self.foo = fooanstatt direkt self._foo = foo, aber vorsichtig, die Setter können davon ausgehen, dass andere Felder bereits initialisiert sind, und dann benötigen Sie eine benutzerdefinierte Validierung im Konstruktor). .

Tomasz Gandor
quelle
2
In Python 3.7 und höher bieten Datenklassen eine gute Lösung, um die Hässlichkeit von Namedtuples zu beheben, und sie eignen sich perfekt für DDD-Wertobjekte.
Hwjp
Ich stimme zu und ab 2020 ist dies der Standardweg. Um einen Mechanismus mit großem Versionsbereich zu haben (2.7, 3.3+, wenn ich mich recht erinnere), könnten Sie die attrsBibliothek verwenden, die eigentlich die Blaupause für die Erstellung des dataclassesModuls war.
Tomasz Gandor
namedtupleshaben eine seltsame Syntax für die Vererbung ... jede Klasse, die eine verwendet, muss wissen, dass es sich um ein benanntes Tupel handelt, und __new__stattdessen verwenden __init__. dataclasseshabe diese Einschränkung nicht
Erik Aronesty
4

Es ist schwer, wenn Ihr Chef das Prinzip der Einzelverantwortung erwartet, aber Pylint sagt nein. Fügen Sie Ihrer Klasse also eine zweite Methode hinzu, damit Ihre Klasse gegen das Prinzip der Einzelverantwortung verstößt. Wie weit Sie mit dem Prinzip der Einzelverantwortung gehen sollen, liegt im Auge des Betrachters.

Mein Fix,

Ich habe meiner Klasse eine zusätzliche Methode hinzugefügt, sodass sie jetzt zwei Dinge erledigt.

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

Ich frage mich nur, ob ich meine Klasse jetzt in zwei separate Dateien und möglicherweise auch in Module aufteilen muss.

Problem gelöst, aber nicht mit meinen Kollegen, die den ganzen Tag damit verbringen, über die Spezifikation zu streiten, anstatt damit weiterzumachen, als wäre es Leben und Tod.

Sean Bradley
quelle