Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Python, can I hide a base class's members?

Tags:

python

pycharm

I am making a programming framework (based on Django) that is intended for students with limited programming experience. Students are supposed to inherit from my base classes (which themselves are inherited from Django models, forms, and views).

I am testing this out now with some students, and the problem is that when they write code in their IDE (most of them are using PyCharm), autocomplete gives them a ton of suggestions, since there are so many inherited methods and attributes, 90% of which are not relevant to them.

Is there some way to hide these inherited members? At the moment I am primarily thinking of how to hide them in auto-complete (in PyCharm and other IDEs). They can (and probably should) still work if called, but just not show up in places like auto-complete.

I tried setting __dict__, but that did not affect what showed up in autocomplete. Another idea I have is to use composition instead of inheritance, though I would have to think this through in more detail.

Edit: This framework is not being used in CS classes; rather, students will be using it to build apps for non-CS domain. So my priority is to keep it simple as possible, perhaps even if it's not a "pure" approach. (Nevertheless, I am considering those arguments as they do have merit.)

like image 919
RexE Avatar asked May 04 '14 14:05

RexE


People also ask

How a class member can be hidden in Python?

Data hiding in Python is performed using the __ double underscore before done prefix. This makes the class members non-public and isolated from the other classes.

What does hide () do Python?

The hide command is used to make objects invisible. If no flags are used, the objects specified, or the active objects if none are specified, will be made invisible.

What is __ base __ in Python?

Python provides a __bases__ attribute on each class that can be used to obtain a list of classes the given class inherits. The __bases__ property of the class contains a list of all the base classes that the given class inherits.

How do you make an attribute private in Python?

The official way to make a variable private in python is by adding two underscore e.g. self.


2 Answers

Disclaimer: the following method isn't very clean (but that's what you're asking for).

Now, the good news is that this method will never break your Python. In fact, it will only affect the IDE.

However, note that PyCharm may become smarter in the future, and get autocomplete to work anyway. I don't think this would be a priority for the Jetbrains team, though!


Here we go.

All you have to do is confuse PyCharm a bit. PyCharm won't be able to reliably follow imports performed using __import__, so we'll use that to import your django modules (if PyCharm doesn't which module you're importing, it can't know which class you are using and which methods to present in the autocomplete window!).

In the following example, we'll assume that:

  • _base.py is the django file that contains the class you want to extend (and whose methods you want to hide)
  • base.py is the file that contains the class you want to provide your students with (whose methods you want to show)
  • test.py is the file that your students will write

_base.py

Nothing in particular here

class DjangoBaseClass(object):
    def method_that_is_hidden(self):
        print "The hidden method is there!"

base.py

Note the import

base = __import__("_base")  # This is equivalent to `import base`. Python understands it, but PyCharm doesn't.

class YourBaseClass(base.DjangoBaseClass):  # PyCharm doesn't know what class this is! No autocomplete.
    def method_that_shows(self):
        print "The method that shows is there!"

Important note: in the case of a multi-level import, the syntax is a bit different. For example, to inherit from django.views.generic.base.View, do:

base = __import('django.views.generic.base', fromlist=['View',])

class MyView(base.View):
    # Your stuff

Note that if PyCharm gets smarter in the future, you'll have to add some more indirection (e.g. instead of __import__('base'), you could use __import__(''.join(['b', 'a', 's', 'e'])). This will get messy, though.

test.py

Nothing in particular here

from base import YourBaseClass

class StudentClass(YourBaseClass):
    pass

if __name__ == "__main__":
    c = StudentClass()
    c.method_that_is_hidden()
    c.method_that_shows()

When you run the test.py script, the methods both work:

$ python test.py
The hidden method is there!
The method that shows is there!

But when you use PyCharm, method_that_is_hidden does not show up in autocomplete:

the hidden method does not show


Note: I believe PyCharm now has features to follow call trees when you run tests, so if your students run tests using PyCharm, PyCharm may pick up the existence of those methods. I suppose they won't show in class definitions, but they may show if your students write c.. You should try it and see for yourself.

like image 84
Thomas Orozco Avatar answered Sep 21 '22 19:09

Thomas Orozco


I'd suggest you to use composition instead of inheritance. Then you design class's interface and decide which methods are available.

like image 40
wachme Avatar answered Sep 20 '22 19:09

wachme