15

I have some class

class A(object):
    def __init__(self, data):
        self._data = data

    def _equals(self, other):
        return self._data == other._data

Pycharm doesn't like that I access other._data because it is private.

"Access to protected member"

This doesn't make sense to me, because the access is made from within the class.

How do I write correct code here without warnings?

Gulzar
  • 23,452
  • 27
  • 113
  • 201
  • 3
    It's within `self`'s class, but even if it has the `_data` property there's no guarantee `other` is also an `A`. Have you read e.g. https://stackoverflow.com/q/44658367/3001761? – jonrsharpe Jun 04 '19 at 09:07
  • @jonrsharpe So what? the warning is about the private, not about "maybe not a correct type" – Gulzar Jun 04 '19 at 09:09
  • 5
    Are you aware that you can override the `==` operator in Python (and that the hook for doing so is `__eq__`, not `_equals`)? – user2357112 Jun 04 '19 at 09:09
  • 1
    It *doesn't* say *"maybe not a correct type"*, it says you're accessing a private member. Python is duck typed, from the interpreter's point of view it doesn't matter whether `other` is an `A` as long as it has the `_data` property. My point is that this may not be access within `other`'s class. – jonrsharpe Jun 04 '19 at 09:11
  • @user2357112 my problem is actually in one of my own functions, I just named it equals for the example. maybe it is confusing and should have just been named foo() – Gulzar Jun 04 '19 at 09:11
  • 1
    You should check they type... If you add an `if not isinstance(other, A): return false` you wont get the warning. Maybe simply adding the `, other: A` annotation is enough... – Giacomo Alzetta Jun 04 '19 at 09:12
  • @jonrsharpe which is why I expect it to assume _data exists for other, and I don't understand why that is relevant here. Will read the link you gave me, thanks! – Gulzar Jun 04 '19 at 09:12
  • @GiacomoAlzetta I don't understand what annotaion, could you elaborate please? – Gulzar Jun 04 '19 at 09:15
  • 1
    [Python3 supports type hints](https://docs.python.org/3/library/typing.html). PyCharm should be smart enough to use them to infer the types. – Giacomo Alzetta Jun 04 '19 at 09:16
  • @GiacomoAlzetta extremely cool! too bad this project is still on 2.7 Sure will use this when we migrate! – Gulzar Jun 04 '19 at 09:17
  • 1
    You can hint types in 2.7 with docstrings, PyCharm understands those too: https://www.jetbrains.com/help/pycharm/using-docstrings-to-specify-types.html – jonrsharpe Jun 04 '19 at 09:24

3 Answers3

22

If you really need it, like namedlist's ._asdict(), the answer is Can I get PyCharm to suppress a particular warning on a single line?:

class A(object):
    def __init__(self, data):
        self._data = data
    def _equals(self, other):
        # noinspection PyProtectedMember
        return self._data == other._data
Polv
  • 1,918
  • 1
  • 20
  • 31
  • 1
    This is not an answer, I wish it weren't as widely accepted – Gulzar Nov 07 '22 at 09:03
  • This is an answer in the strict sense, as it does suppress the warning in Pycharm as OP requested. It just took the poor approach of silencing a very legitimate warning instead of properly writing a more robust code. – MestreLion Jan 17 '23 at 06:01
4

Python 3.5+ answer (Type hints introduced):

from __future__ import annotations


class A(object):
    def __init__(self, data):
        self._data = data

    def _equals(self, other: A):
        return self._data == other._data

Using type hints as suggested by @Giacomo Alzetta, and allowing to type hint the class itself using from __future__ import annotations.

No need to hack PyCharm anymore or write ugly comments.


As also noted by @jonrsharpe, python 2 also supports type hints via docstrings.
I will not post this here as I support Python 2 losing support.

Gulzar
  • 23,452
  • 27
  • 113
  • 201
  • On the second thought, this would only work for `_equals` or the like, not generally. So, the solution you found would solve your problem only. (Not the title you wrote) – Polv Nov 24 '22 at 10:45
  • @Polv Please show code this would not work for, I am not following – Gulzar Nov 24 '22 at 12:20
  • not your code, but if I create `def _equivalent(self, other: B):` method, checking if different types are equivalent. – Polv Nov 24 '22 at 13:40
  • @Polv Still not following. If you define `def _equivalent(self, other: B):` just the same, it would work just the same – Gulzar Nov 24 '22 at 15:45
2

The comments in OP's question have a lot of great insight about the warning, proper fixes and best practices, most were unfortunately not mentioned by the current answers. So here it goes:

About the warning itself, OP says:

This doesn't make sense to me, because the access is made from within the class.

No, it is not. other can be any class, not necessarily A. So even if you only pass A instances as other, there's nothing in the code that indicates or enforces that.

Indication can be done with type hints, as explained by Gulzar's answer.

And there are some approaches for enforcing this:

  • Force other's type to be A (or a subclass): if not isinstance(other, A): return False. This is the arguably the preferred way, but it does defeat Python's duck-typing.
  • If instead of a regular method _equals() you're using one of the predefined "rich comparison" special methods such as __eq__(), __lt__(), __gt__(), you can return a special value to give a chance to other's class reflection method to handle the call: if not isinstance(other, A): return NotImplemented
  • If you want to preserve duck-typing to allow unknown classes, and you're not using a private attribute such as _data (otherwise you'll still get the warning), you could use a try-block to handle a missing attribute:
try:
    return self.data == other.data
except AttributeError:  # other has no .data
    return False
MestreLion
  • 12,698
  • 8
  • 66
  • 57
  • I think there is also `hasattr()` – Polv Jan 19 '23 at 02:11
  • @Polv: `hasattr()` also uses a try/except block behind the scenes, so it's usually better (and faster) to use the EAFP pattern instead of `if hasattr(): return ... else return False` – MestreLion Jan 19 '23 at 11:18