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.
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!
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.
I need to update my answer. Right now - if you need a struct
, you have two great options:
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.
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+.
NamedTuple
is not great - especially before Python 3's typing.NamedTuple
:
https://docs.python.org/3/library/typing.html#typing.NamedTuple
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).
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With