Python conventions suggest to proceed the names of protected attributes with an underscore. As far as I understand protected attributes should only be used within a given class and its subclasses. Could you then provide me with some intuition on why pylint returns protected-access warnings when I try to use protected attributes inside alternative initializers, e.g.
class Test(object):
def __init__(self, name):
self.name = name
self._count = 0
self._bound = 1 # max value
@classmethod
def specific_state(cls, name, bound):
test = cls(name)
test._bound = bound
I do understand that in this specific case (presented in the example above) I work on an instance of an object but it is still inside the class definition thus it seems ok from my point of view. Is pylint a bit too rigorous in this matter or do I misunderstand sth?
In my opinion, pylint
is too vigorous on this one. I doubt that there would be too much disagreement on this. I can't speak for the pylint developers, but I would guess that this is more of a problem with the type inferencing than necessarily being considered their ideal behavior.
Different people will probably tell you different opinions on referencing members that lead with an underscore. My personal opinion is that
So this means that if I have class Foo
(with member _member
) defined in foo.py
and if I make a subclass of Foo
(lets call it Bar
) in bar.py
, I don't believe that Bar
should make any explicit reference to the _member
attribute. However, if you move Bar
into foo.py
, then it's Ok. In fact, class Baz
(also defined in foo.py
) should also be allowed to rely on the internals of Foo
-- but it better have a good reason for doing so.
Mostly, I'm not OK with the treatment of underscore prefixed members as "protected" (in the Java sense). I think they are meant to be treated as "implementation details". Protected works a lot better in Java because if you change the implementation (e.g. remove a protected member), the code will fail at compile time and let you know that the protected member your subclass relies on is no longer there. Python has no such safe-guards built in.
Because underscore prefixed names are "implementation details", if it's the same class (like the situation you describe in your question), then it's the same implementation and so (to me) allowing access to those members is a no-brainer. I'd # pylint: disable=protected-access
that without a second thought :-).
From pylint.checkers.classes.ClassChecker
in the _check_protected_attribute_access
method:
'''Given an attribute access node (set or get), check if attribute
access is legitimate. Call _check_first_attr with node before calling
this method. Valid cases are:
* self._attr in a method or cls._attr in a classmethod. Checked by
_check_first_attr.
* Klass._attr inside "Klass" class.
* Klass2._attr inside "Klass" class when Klass2 is a base class of
Klass.
'''
You can see that your case in not among the mentioned cases. So one could assume that the protected-access
warning is legit.
But:
from pylint
s code itself the exact same case occurred. (e.g. exceptions.py:338):
# pylint: disable=protected-access
exc = exc._proxied
So basically they disabled their on checker in that case.
in c++ the following code is valid:
class X
{
private:
int a;
protected:
int b;
public:
static void staticMethod(X& x)
{
x.a = 1;
x.b = 1;
}
};
So accessing protected/private members in static methods is valid.
I would say that pylint
is overly sensitiv in this case. You could use a comment to disable the checker there. The reason that the warning is produced is probably that it is kind of hard for a static checker to implement a more complex behavior.
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