[Solved] What does Pylint’s “Too few public methods” message mean?

I’m running Pylint on some code, and receiving the error “Too few public methods (0/2)”. What does this message mean?

The Pylint documentation is not helpful:

Used when a class has too few public methods, so be sure it’s really worth it.

Enquirer: monsur

||

Solution #1:

The error basically says that classes aren’t meant to just store data, as you’re basically treating the class as a dictionary. Classes should have at least a few methods to operate on the data that they hold.

If your class looks like this:

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

Consider using a dictionary or a namedtuple instead. Although if a class seems like the best choice, use it. Pylint doesn’t always know what’s best.

Do note that namedtuple is immutable and the values assigned on instantiation cannot be modified later.

Respondent: Blender

Solution #2:

If you are extending a class, then my suggestion is to systematically disable this warning and move on, e.g., in the case of Celery tasks:

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

    ...             

Even if you are only extending a single function, you definitely need a class to make this technique function, and extending is definitely better than hacking on the third-party classes!

Respondent: sage

Solution #3:

This is another case of Pylint’s blind rules.

“Classes are not meant to store data” – this is a false statement. Dictionaries are not good for everything. A data member of a class is something meaningful, a dictionary item is something optional. Proof: you can do dictionary.get('key', DEFAULT_VALUE) to prevent a KeyError, but there is no simple __getattr__ with default.

Recommended ways for using structs

I need to update my answer. Right now – if you need a struct, you have two great options:

a) Just use attrs

These is a library for that:

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

What you get extra: not writing constructors, default values, validation, __repr__, read-only objects (to replace namedtuples, even in Python 2) and more.

b) Use dataclasses (Py 3.7+)

Following hwjp’s comment, I also recommend dataclasses:

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

This is almost as good as attrs, and is a standard library mechanism (“batteries included”), with no extra dependencies, except Python 3.7+.

The rest of the previous answer

NamedTuple is not great – especially before Python 3’s typing.NamedTuple:
https://docs.python.org/3/library/typing.html#typing.NamedTuple

  • you definitely should check out the “class derived from NamedTuple” pattern.
    Python 2 – namedtuples created from string descriptions – is ugly, bad and “programming inside string literals” stupid.

I agree with the two current answers (“consider using something else, but Pylint isn’t always right” – the accepted one, and “use Pylint suppressing comment”), but I have my own suggestion.

Let me point this out one more time: Some classes are meant just to store data.

Now the option to also consider – 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

Above you have read-only properties, which is OK for Value Object (e.g., like those in Domain Driven Design), but you can also provide setters – this way your class will be able to take responsibility for the fields which you have – for example to do some validation etc. (if you have setters, you can assign using them in the constructor, i.e., self.foo = foo instead of direct self._foo = foo, but careful, the setters may assume other fields to be initialized already, and then you need custom validation in the constructor).

Respondent: Tomasz Gandor

Solution #4:

It’s hard when your boss expects the single responsibility principle, but Pylint says no. So add a second method to your class so your class violates the single responsibility principle. How far you are meant to take the single responsibility principle is in the eye the beholder.

My fix

I added an extra method to my class, so it now does two things.

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

I’m just wondering if I need to split my class into two separate files now, and maybe modules as well.

The problem is solved, but not with my colleagues who spend all day arguing about the specification, rather than getting on with it, like it’s life and death.

Respondent: Sean Bradley

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Leave a Reply

Your email address will not be published.