在Python中,我可以隐藏基类的成员吗?
我正在制作一个编程框架(基于Django),主要是为了那些编程经验有限的学生。学生们需要从我的基础类继承,而这些基础类本身又是从Django的模型、表单和视图继承而来的。
现在我正在和一些学生一起测试这个框架,问题是当他们在自己的IDE(大多数人使用的是PyCharm)中写代码时,自动补全功能给他们提供了很多建议,因为有太多继承的方法和属性,其中90%对他们来说都是不相关的。
有没有办法隐藏这些继承的成员呢?目前我主要在考虑如何在自动补全中隐藏它们(在PyCharm和其他IDE中)。这些方法(可能应该)仍然可以被调用,但就是不希望它们出现在自动补全的列表里。
我尝试过设置__dict__
,但这并没有影响自动补全中显示的内容。另一个我想到的办法是使用组合而不是继承,不过我需要更详细地考虑这个方案。
编辑:这个框架并不是在计算机科学课程中使用的;相反,学生们会用它来为非计算机科学领域构建应用。所以我的首要任务是尽可能保持简单,甚至可以不追求“纯粹”的方法。(不过,我也在考虑这些观点,因为它们确实有道理。)
3 个回答
我觉得正确的做法是教你的学生Python的命名规范(并确保你的代码遵循这些规范)。在自动补全的选项中,内容是按字母顺序排列的,而'_'
这个字符在字母表中排在'a'->'Z'
之后,所以如果你遵循命名规范,你的公共API应该会排在自动补全列表的最前面。遵循这个标准是他们必须学习的内容,所以我觉得不需要刻意隐瞒。
我能理解你为什么想要把Django的公共API隐藏起来,但想想如果只是隐藏基类的API而不是实际抽象掉它,会有什么后果:如果一个学生不知道某个方法名已经被基类使用了(因为它在自动补全中没有出现,或者你提供的文档没有说明),他可能会给自己的方法起同样的名字,结果完全搞砸了,因为他在不知情的情况下覆盖了一个方法。
基本上,你是在试图把类里面的内容隐藏起来,而不是教他们需要知道的信息,以避免做不该做的事情。这几乎总是会导致问题的可能性增加。而且这也在教他们完全依赖于他们的IDE的功能;比如说,如果在自动补全中没有出现,那就说明它不存在!
我觉得你需要看看你担心的根本原因,并尝试设计课程来解决这些问题,而不是想出一些变通的方法,这样可能会教给学生错误的编程习惯。学会正确阅读文档是成为程序员非常、非常重要的一部分。不要试图让他们不去学习这个教训。实际上,不学习这个教训是导致99%问题(尤其是在Python相关问题上)的原因之一,这在Stack Exchange上尤其明显。
我建议你使用组合而不是继承。这样你可以设计类的接口,决定哪些方法是可以使用的。
免责声明:下面的方法不是很干净(但这正是你想要的)。
好消息是,这个方法不会破坏你的Python环境。实际上,它只会影响IDE(集成开发环境)。
不过,请注意,未来PyCharm可能会变得更聪明,自动补全功能可能会正常工作。不过,我觉得这对Jetbrains团队来说并不是优先考虑的事情!
接下来,我们开始吧。
你只需要让PyCharm有点困惑。PyCharm无法可靠地跟踪使用__import__
进行的导入,所以我们将利用这一点来导入你的django模块(如果PyCharm不知道你在导入哪个模块,它就无法知道你在使用哪个类,也就无法在自动补全窗口中展示哪些方法!)。
在下面的例子中,我们假设:
_base.py
是包含你想扩展的类(以及你想隐藏的方法)的django文件base.py
是包含你想提供给学生的类(以及你想展示的方法)的文件test.py
是学生将要编写的文件
_base.py
这里没有特别的内容
class DjangoBaseClass(object):
def method_that_is_hidden(self):
print "The hidden method is there!"
base.py
注意导入部分
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!"
重要提示:在多级导入的情况下,语法会稍微不同。例如,要从django.views.generic.base.View
继承,可以这样做:
base = __import('django.views.generic.base', fromlist=['View',])
class MyView(base.View):
# Your stuff
请注意,如果未来PyCharm变得更聪明,你可能需要增加一些间接性(例如,不用__import__('base')
,可以用__import__(''.join(['b', 'a', 's', 'e']))
。不过这样会变得很麻烦。
test.py
这里没有特别的内容
from base import YourBaseClass
class StudentClass(YourBaseClass):
pass
if __name__ == "__main__":
c = StudentClass()
c.method_that_is_hidden()
c.method_that_shows()
当你运行test.py
脚本时,这两个方法都能正常工作:
$ python test.py
The hidden method is there!
The method that shows is there!
但是当你使用PyCharm时,method_that_is_hidden
不会出现在自动补全中:
注意:我相信PyCharm现在有跟踪调用树的功能,当你运行测试时,如果你的学生使用PyCharm进行测试,PyCharm可能会发现这些方法的存在。我想它们不会出现在类定义中,但如果你的学生写c.
,可能会显示出来。你可以试试看,亲自体验一下。