質問

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

役に立ちましたか?

解決

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.

他のヒント

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

I think the correct solution is to teach your students python's naming conventions (and make sure your code follows them). Options in autocomplete are in alphabetical order, and the '_' character comes later in the alphabet than 'a'->'Z', so if you follow the naming conventions your public API should be first in the autocomplete list. Following that standard is a lesson they're going to need to learn anyway, So I wouldn't try and hide it from them.

Now I can understand why you might want to hide Django's public API from them, but consider the other consequences of just hiding the base class API rather than actually abstracting it away: What if a student, not knowing that a method name is already used by a base-class (because it doesn't show up in autocomplete, or the documentation you've provided), decides to name one of their own methods that and winds up completely breaking everything because they overrode a method without realizing it?

Basically, you're trying to hide what's actually in the class from the students rather than teaching them the information they need to know to not do things they shouldn't. This almost always results in simply increasing the likelihood of things going wrong. It's also teaching them to be completely dependent on the features of their IDE; E.X. if it's not in autocomplete, it must not exist!

I think you need to look at the root causes of the concerns you have, and attempt to create lessons that address those concerns, rather than figure out hacks that work around them and potentially teach your students back programming practices. Learning how to read documentation properly is a really, REALLY important part of being a programer. Don't try and make them not have to learn that lesson. Hell, not learning that lesson is what results in 99% of questions (especially on Python for whatever reason) on Stack Exchange.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top