Jak wygląda twoja klasa? Czy klasa robi coś innego niż przechowywanie danych?
Blender
1
Wszystko, co robi klasa, to przechowywanie danych.
monsur
2
Cóż, jest twój problem. Klasy nie są przeznaczone do przechowywania danych. Do tego służą struktury danych, takie jak słowniki i listy.
Blender
Ciekawe, dzięki! Komunikat o błędzie pylint może być bardziej przydatny. W każdym razie możesz zamienić swój komentarz w odpowiedź, a ja ją zatwierdzę.
monsur
6
Ale gdzie jest definicja „niewielu”? Mam dokładnie jedną metodę. To jest powód, dla którego ta klasa istnieje. Jak pylint definiuje „kilka”? Więcej niż 2? Czemu?
Zordid,
Odpowiedzi:
124
Błąd zasadniczo mówi, że klasy nie są przeznaczone tylko do przechowywania danych, ponieważ traktujesz w zasadzie klasę jako słownik. Klasy powinny mieć co najmniej kilka metod do operowania na danych, które przechowują.
Jeśli Twoja klasa wygląda tak:
classMyClass(object):def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
Rozważ użycie słownika lub namedtuplezamiast niego. Chociaż jeśli klasa wydaje się najlepszym wyborem, skorzystaj z niej. pylint nie zawsze wie, co jest najlepsze.
Należy pamiętać, że namedtuplejest niezmienny, a wartości przypisane podczas tworzenia wystąpienia nie mogą być później modyfikowane.
+1 dla „pylint nie wie, co jest najlepsze” - kieruj się własnym osądem, ale z reguły, jeśli potrzebujesz „struktury”, użyj dictlub namedtuple. Użyj klasy, jeśli chcesz dodać logikę do swojego obiektu (na przykład chcesz, aby coś się wydarzyło, gdy zostanie utworzony, potrzebujesz specjalnych rzeczy, gdy zostanie dodany, chcesz wykonać na nim pewne operacje, kontrolować sposób wyświetlane itp.)
Burhan Khalid,
Dzięki za szczegółowe odpowiedzi! Mój przypadek użycia jest podobny do tego, o którym wspomniał Burhan, przetwarzam dane podczas ich tworzenia.
monsur,
6
Ten błąd nie ma sensu, jeśli w definicji klasy znajduje się Meta (metaklasa).
alexander_ch
11
namedtuplejest do bani - oprócz brzydkiej składni nie można jej łatwo udokumentować ani podać wartości domyślnych.
rr-
6
Za każdym razem namedtupleżałowałem tej decyzji. Niespójne jest zezwalanie na dostęp zarówno nazwany, jak i indeksowany.
teoria
39
Jeśli przedłużasz klasę, to moja sugestia jest taka, aby systematycznie wyłączać to ostrzeżenie i przejść dalej, np. W przypadku zadań Seler:
classMyTask(celery.Task):# pylint: disable=too-few-public-methods """base for My Celery tasks with common behaviors; extends celery.Task
...
Nawet jeśli rozszerzasz tylko jedną funkcję, zdecydowanie potrzebujesz klasy, aby ta technika działała, a rozszerzanie jest zdecydowanie lepsze niż hakowanie klas zewnętrznych!
Posiadanie tego wiarygodnego, wstępnego zatwierdzenia daje mi teraz: Zła wartość opcji „zbyt mało-metoda-publiczna” (zła-wartość-opcji)
Mercury
Czy włączyłeś metody „s”? Twoja wiadomość o złej wartości opcji tego nie zawiera.
mędrzec
4
Prawdopodobnie lepszym sposobem na wyłączenie tego jest ustawienie min-public-methods=0w [BASIC]sekcji pliku konfiguracyjnego. Pozwala to umieścić go w osobnej linii od wszystkich twoich disable=rzeczy (w [MESSAGE CONTROL]), co, jak uważam, ułatwia dodawanie szczegółowych komentarzy na temat tego, dlaczego włączyłeś i wyłączyłeś rzeczy wraz ze zmianą konfiguracji.
cjs
15
To kolejny przypadek pylint ślepych zasad.
„Klasy nie służą do przechowywania danych” - to jest fałszywe stwierdzenie. Słowniki nie nadają się do wszystkiego. Element danych klasy jest znaczący, element słownika jest opcjonalny. Dowód: możesz zrobić, dictionary.get('key', DEFAULT_VALUE)aby zapobiec KeyError, ale nie jest to proste__getattr__ domyślnego.
EDYCJA - zalecane sposoby korzystania ze struktur
Muszę zaktualizować swoją odpowiedź. Teraz - jeśli potrzebujeszstruct , masz dwie świetne opcje:
import attr
@attr.s
classMyClass(object):# or just MyClass: for Python 3
foo = attr.ib()
bar = attr.ib()
Co zyskujesz dodatkowo: nie pisanie konstruktorów, wartości domyślne, walidacja, __repr__obiekty tylko do odczytu (do zastąpienianamedtuples , nawet w Pythonie 2) i więcej.
b) Użyj dataclasses (Py 3.7+)
Idąc za komentarzem hwjp, polecam również dataclasses:
To jest prawie tak dobre, jak attrs i jest to standardowy mechanizm biblioteki („w zestawie baterie”), bez dodatkowych zależności, z wyjątkiem Pythona 3.7+.
Reszta poprzedniej odpowiedzi
NamedTuplenie jest świetny - szczególnie przed Pythonem 3 typing.NamedTuple:
https://docs.python.org/3/library/typing.html#typing.NamedTuple
- zdecydowanie powinieneś sprawdzić NamedTuplewzorzec „klasa pochodna ”. Python 2 -namedtuples stworzony z opisów łańcuchów - jest brzydki, zły i "programowanie wewnątrz literałów łańcuchowych" jest głupie.
Zgadzam się z dwiema obecnymi odpowiedziami („rozważ użycie czegoś innego, ale pylint nie zawsze ma rację” - przyjęta i „użyj komentarza tłumiącego pylint”), ale mam własną sugestię.
Pozwólcie, że jeszcze raz zwrócę uwagę: niektóre klasy służą tylko do przechowywania danych.
Powyżej masz właściwości tylko do odczytu, co jest OK dla obiektu wartości (np. Jak te w Domain Driven Design), ale możesz także podać setery - w ten sposób twoja klasa będzie mogła wziąć odpowiedzialność za pola, które masz - na przykład do wykonania walidacji itp. (jeśli masz setery, możesz przypisać je używając ich w konstruktorze, tj. self.foo = foozamiast bezpośrednich self._foo = foo, ale ostrożnie, setery mogą założyć, że inne pola są już zainicjowane, a wtedy potrzebujesz niestandardowej walidacji w konstruktorze) .
W Pythonie 3.7 i nowszych, klasy danych stanowią dobre rozwiązanie, rozwiązując niektóre z brzydoty nazwanych krotek i są idealne dla obiektów wartości DDD.
hwjp
Zgadzam się i od 2020 roku jest to standardowa droga. Aby mieć mechanizm o szerokim zakresie wersji (2.7, 3.3+, jeśli dobrze pamiętam), możesz użyć attrsbiblioteki, która była tak naprawdę planem tworzenia dataclassesmodułu.
Tomasz Gandor
namedtuplesmają dziwną składnię do dziedziczenia ... wymagając od każdej klasy używającej jednej, aby wiedziała, że jest to nazwana krotka i używa __new__zamiast __init__. dataclassesnie ma tego ograniczenia
Erik Aronesty
4
Trudno jest, gdy szef oczekuje zasady pojedynczej odpowiedzialności, ale pylint mówi nie. Więc dodaj drugą metodę do swojej klasy, aby Twoja klasa naruszyła zasadę pojedynczej odpowiedzialności. To, jak daleko masz się podjąć, zasada pojedynczej odpowiedzialności jest w oczach patrzącego.
Moja poprawka,
Dodałem dodatkową metodę do mojej klasy, więc teraz robi dwie rzeczy.
def __str__(self):return self.__class__.__name__
Zastanawiam się tylko, czy muszę teraz podzielić moją klasę na 2 oddzielne pliki, a może również moduły.
problem rozwiązany, ale nie z moimi kolegami, którzy spędzają cały dzień dyskutując ze specyfikacją, zamiast zajmować się nią, jakby to było życie i śmierć.
Odpowiedzi:
Błąd zasadniczo mówi, że klasy nie są przeznaczone tylko do przechowywania danych, ponieważ traktujesz w zasadzie klasę jako słownik. Klasy powinny mieć co najmniej kilka metod do operowania na danych, które przechowują.
Jeśli Twoja klasa wygląda tak:
Rozważ użycie słownika lub
namedtuple
zamiast niego. Chociaż jeśli klasa wydaje się najlepszym wyborem, skorzystaj z niej. pylint nie zawsze wie, co jest najlepsze.Należy pamiętać, że
namedtuple
jest niezmienny, a wartości przypisane podczas tworzenia wystąpienia nie mogą być później modyfikowane.źródło
dict
lubnamedtuple
. Użyj klasy, jeśli chcesz dodać logikę do swojego obiektu (na przykład chcesz, aby coś się wydarzyło, gdy zostanie utworzony, potrzebujesz specjalnych rzeczy, gdy zostanie dodany, chcesz wykonać na nim pewne operacje, kontrolować sposób wyświetlane itp.)namedtuple
jest do bani - oprócz brzydkiej składni nie można jej łatwo udokumentować ani podać wartości domyślnych.namedtuple
żałowałem tej decyzji. Niespójne jest zezwalanie na dostęp zarówno nazwany, jak i indeksowany.Jeśli przedłużasz klasę, to moja sugestia jest taka, aby systematycznie wyłączać to ostrzeżenie i przejść dalej, np. W przypadku zadań Seler:
Nawet jeśli rozszerzasz tylko jedną funkcję, zdecydowanie potrzebujesz klasy, aby ta technika działała, a rozszerzanie jest zdecydowanie lepsze niż hakowanie klas zewnętrznych!
źródło
min-public-methods=0
w[BASIC]
sekcji pliku konfiguracyjnego. Pozwala to umieścić go w osobnej linii od wszystkich twoichdisable=
rzeczy (w[MESSAGE CONTROL]
), co, jak uważam, ułatwia dodawanie szczegółowych komentarzy na temat tego, dlaczego włączyłeś i wyłączyłeś rzeczy wraz ze zmianą konfiguracji.To kolejny przypadek
pylint
ślepych zasad.„Klasy nie służą do przechowywania danych” - to jest fałszywe stwierdzenie. Słowniki nie nadają się do wszystkiego. Element danych klasy jest znaczący, element słownika jest opcjonalny. Dowód: możesz zrobić,
dictionary.get('key', DEFAULT_VALUE)
aby zapobiecKeyError
, ale nie jest to proste__getattr__
domyślnego.EDYCJA - zalecane sposoby korzystania ze struktur
Muszę zaktualizować swoją odpowiedź. Teraz - jeśli potrzebujesz
struct
, masz dwie świetne opcje:a) Po prostu użyj
attrs
Oto biblioteka do tego:
https://www.attrs.org/en/stable/
Co zyskujesz dodatkowo: nie pisanie konstruktorów, wartości domyślne, walidacja,
__repr__
obiekty tylko do odczytu (do zastąpienianamedtuples
, nawet w Pythonie 2) i więcej.b) Użyj
dataclasses
(Py 3.7+)Idąc za komentarzem hwjp, polecam również
dataclasses
:https://docs.python.org/3/library/dataclasses.html
To jest prawie tak dobre, jak
attrs
i jest to standardowy mechanizm biblioteki („w zestawie baterie”), bez dodatkowych zależności, z wyjątkiem Pythona 3.7+.Reszta poprzedniej odpowiedzi
NamedTuple
nie jest świetny - szczególnie przed Pythonem 3typing.NamedTuple
: https://docs.python.org/3/library/typing.html#typing.NamedTuple - zdecydowanie powinieneś sprawdzićNamedTuple
wzorzec „klasa pochodna ”. Python 2 -namedtuples
stworzony z opisów łańcuchów - jest brzydki, zły i "programowanie wewnątrz literałów łańcuchowych" jest głupie.Zgadzam się z dwiema obecnymi odpowiedziami („rozważ użycie czegoś innego, ale pylint nie zawsze ma rację” - przyjęta i „użyj komentarza tłumiącego pylint”), ale mam własną sugestię.
Pozwólcie, że jeszcze raz zwrócę uwagę: niektóre klasy służą tylko do przechowywania danych.
Teraz opcja do rozważenia - użyj
property
-ies.Powyżej masz właściwości tylko do odczytu, co jest OK dla obiektu wartości (np. Jak te w Domain Driven Design), ale możesz także podać setery - w ten sposób twoja klasa będzie mogła wziąć odpowiedzialność za pola, które masz - na przykład do wykonania walidacji itp. (jeśli masz setery, możesz przypisać je używając ich w konstruktorze, tj.
self.foo = foo
zamiast bezpośrednichself._foo = foo
, ale ostrożnie, setery mogą założyć, że inne pola są już zainicjowane, a wtedy potrzebujesz niestandardowej walidacji w konstruktorze) .źródło
attrs
biblioteki, która była tak naprawdę planem tworzeniadataclasses
modułu.namedtuples
mają dziwną składnię do dziedziczenia ... wymagając od każdej klasy używającej jednej, aby wiedziała, że jest to nazwana krotka i używa__new__
zamiast__init__
.dataclasses
nie ma tego ograniczeniaTrudno jest, gdy szef oczekuje zasady pojedynczej odpowiedzialności, ale pylint mówi nie. Więc dodaj drugą metodę do swojej klasy, aby Twoja klasa naruszyła zasadę pojedynczej odpowiedzialności. To, jak daleko masz się podjąć, zasada pojedynczej odpowiedzialności jest w oczach patrzącego.
Moja poprawka,
Dodałem dodatkową metodę do mojej klasy, więc teraz robi dwie rzeczy.
Zastanawiam się tylko, czy muszę teraz podzielić moją klasę na 2 oddzielne pliki, a może również moduły.
problem rozwiązany, ale nie z moimi kolegami, którzy spędzają cały dzień dyskutując ze specyfikacją, zamiast zajmować się nią, jakby to było życie i śmierć.
źródło