Що означає повідомлення пілінта "Занадто мало публічних методів"


110

Я виконую пілінт на якомусь коді та отримую помилку "Занадто мало публічних методів (0/2)". Що означає це повідомлення? Документи pylint не корисні:

Використовується, коли в класі є занадто мало публічних методів, тому будьте впевнені, що це дійсно того варто.


1
Як виглядає ваш клас? Чи робить у класі щось інше, ніж зберігати дані?
Блендер

1
Усього класу є зберігання даних.
монсур

2
Ну, тут є ваша проблема. Класи не призначені для зберігання даних. Ось для чого створені структури даних, такі як словники та списки.
Блендер

Цікаво, дякую! Повідомлення про помилку пілінта може бути більш корисним. У будь-якому випадку, сміливо перетворять ваш коментар у відповідь, і я схвалюю.
монсур

6
Але де визначення "мало"? Я отримав рівно один метод. Ось чому клас існує. Як пілінт визначає "мало"? Більше 2? Чому?
Зордід

Відповіді:


124

Помилка в основному говорить , що класи не призначені тільки зберігати дані, так як ви в основному лікування класу як словник. У класах повинно бути принаймні кілька методів роботи з даними, які вони зберігають.

Якщо ваш клас виглядає так:

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

Подумайте про використання словника чи namedtupleзамість цього. Хоча якщо клас здається найкращим вибором, використовуйте його. pylint не завжди знає, що найкраще.

Зверніть увагу, що namedtupleце незмінне, і значення, присвоєні інстанції, не можна змінити пізніше.


72
+1 для "pylint не знає, що найкраще" - використовуйте власне судження, але, як правило, якщо вам потрібно "структура", використовуйте dictабо namedtuple. Використовуйте клас, коли ви хочете додати певну логіку до свого об’єкта (наприклад, ви хочете, щоб речі траплялися, коли він створений, вам потрібні деякі особливі речі, коли він додається, ви хочете o виконувати деякі операції над ним, контролювати, як його відображається тощо)
Бурхан Халід

Дякуємо за детальні відповіді! Мій випадок використання аналогічний тому, що згадував Бурхан, я роблю деяку обробку даних, коли вони створені.
монсур

6
Ця помилка не має сенсу, якщо у вас є мета (метаклас) всередині вашого визначення класу.
alexander_ch

11
namedtupleсмокче - окрім того, що у вас є некрасивий синтаксис, ви не можете його задокументувати або легко надати значення за замовчуванням.
rr-

6
Кожен раз, коли я використовував namedtuple, шкодував про рішення. Це непослідовно, щоб дозволити як названий доступ, так і індексовані атрибути доступу.
theorifice

39

Якщо ви розширюєте клас, то моя пропозиція полягає в систематичному відключенні цього попередження і продовжуйте роботу, наприклад, у випадку завдань із селери:

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

    ...             

Навіть якщо ви розширюєте лише одну функцію, вам обов'язково потрібен клас, щоб зробити цю техніку функцією, а розширення, безумовно, краще, ніж злом на сторонні класи!


Маючи цей діапазон, попереднє введення даних тепер дає мені: Неправильне значення опції 'занадто мало-публічний метод' (погана опція-значення)
Меркурій

Чи включили ви 's' методи? У вашому повідомленні про неправильну опцію немає його.
шавлія

4
Можливо, кращий спосіб відключити це - встановити min-public-methods=0в [BASIC]розділі конфігураційного файлу. Це дозволяє вам розмістити його в окремому рядку від усіх ваших disable=речей (в [MESSAGE CONTROL]), які я знаходжу, полегшує додавання детальних коментарів про те, чому ви включили та вимкнули речі разом із зміною конфігурації.
cjs

15

Це ще один випадок pylint сліпих правил.

"Класи не призначені для зберігання даних" - це помилкове твердження. Словники корисні не для всього. Член даних класу - щось значуще, елемент словника - щось необов’язкове. Доказ: ви можете зробити, dictionary.get('key', DEFAULT_VALUE)щоб запобігти KeyError, але немає простого__getattr__ за замовчуванням.

EDIT - рекомендовані способи використання конструкцій

Мені потрібно оновити свою відповідь. Прямо зараз - якщо вам потрібноstruct , у вас є два чудових варіанти:

а) Просто використовуйте attrs

Це бібліотека для цього:

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

Що ви отримуєте додатково: не написання конструкторів, значень за замовчуванням, валідація, __repr__об'єкти лише для читання (для заміни namedtuplesнавіть у Python 2) та багато іншого.

б) Використовувати dataclasses(Py 3.7+)

Після коментаря hwjp я також рекомендую dataclasses:

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

Це майже так само добре attrs, і є стандартним механізмом бібліотеки ("батареї включені"), без додаткових залежностей, крім Python 3.7+.

Решта попередньої відповіді

NamedTupleне чудово - особливо перед python 3's typing.NamedTuple: https://docs.python.org/3/library/typing.html#typing.NamedTuple - вам обов'язково слід перевірити NamedTupleшаблон "класу, похідного від ". Пітон 2 -namedtuples створений з опису рядків - некрасивий, поганий і "програмування всередині літеральних рядків" дурний.

Я погоджуюся з двома поточними відповідями ("розглянути можливість використання чогось іншого, але pylint не завжди є правильним" - прийнятим, і "використовувати коментар, що пригнічує пілінт"), але у мене є своя пропозиція.

Дозвольте це ще раз зазначити: деякі класи призначені просто для зберігання даних.

Тепер варіант також розглянути - використання 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

Вище маєте властивості лише для читання, що є нормальним для об'єкта цінності (наприклад, таких, як у дизайні, керованому доменом), але ви також можете надати сетерів - таким чином ваш клас зможе взяти на себе відповідальність за поля, які у вас є - наприклад зробити деяку перевірку і т. д. (якщо у вас є сеттери, ви можете призначити їх використання в конструкторі, тобто self.foo = fooзамість прямого self._foo = foo, але обережного, вони можуть передбачити ініціалізацію інших полів, і тоді вам потрібна спеціальна перевірка в конструкторі) .


2
У Python 3.7 та вище, класи даних служать хорошим рішенням, вирішуючи деякі неподобства названих пар, і вони ідеально підходять для об'єктів значення DDD.
hwjp

Я згоден, і з 2020 року це буде стандартний шлях. Щоб мати механізм широкого діапазону версій (2,7, 3,3+, якщо я пам'ятаю), ви можете використовувати attrsбібліотеку, яка насправді була основою для створення dataclassesмодуля.
Томаш

namedtuplesмають дивний синтаксис для успадкування ... вимагаючи, щоб кожен клас, що використовує один, знав, що це іменований кортеж і використовувати __new__замість __init__. dataclassesне майте цього обмеження
Ерік Аронесті

4

Важко, коли твій начальник очікує єдиного принципу відповідальності, але пілінт говорить "ні". Тому додайте до свого класу другий метод, щоб ваш клас порушував принцип єдиної відповідальності. Наскільки далеко ви маєте на меті взяти на себе принцип єдиної відповідальності - це очей, хто дивиться.

Моє виправлення,

Я додав додатковий метод у свій клас, тож тепер він робить 2 речі.

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

Мені просто цікаво, чи потрібно мені зараз розділити свій клас на 2 окремі файли, а також, можливо, і модулі.

Проблема вирішена, але не з моїми колегами, які цілий день проводять сперечатися з приводу, а не з цим, як це життя і смерть.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.