在Python中,我可以隐藏基类的成员吗?

5 投票
3 回答
1997 浏览
提问于 2025-04-18 05:19

我正在制作一个编程框架(基于Django),主要是为了那些编程经验有限的学生。学生们需要从我的基础类继承,而这些基础类本身又是从Django的模型、表单和视图继承而来的。

现在我正在和一些学生一起测试这个框架,问题是当他们在自己的IDE(大多数人使用的是PyCharm)中写代码时,自动补全功能给他们提供了很多建议,因为有太多继承的方法和属性,其中90%对他们来说都是不相关的。

有没有办法隐藏这些继承的成员呢?目前我主要在考虑如何在自动补全中隐藏它们(在PyCharm和其他IDE中)。这些方法(可能应该)仍然可以被调用,但就是不希望它们出现在自动补全的列表里。

我尝试过设置__dict__,但这并没有影响自动补全中显示的内容。另一个我想到的办法是使用组合而不是继承,不过我需要更详细地考虑这个方案。

编辑:这个框架并不是在计算机科学课程中使用的;相反,学生们会用它来为非计算机科学领域构建应用。所以我的首要任务是尽可能保持简单,甚至可以不追求“纯粹”的方法。(不过,我也在考虑这些观点,因为它们确实有道理。)

3 个回答

-2

我觉得正确的做法是教你的学生Python的命名规范(并确保你的代码遵循这些规范)。在自动补全的选项中,内容是按字母顺序排列的,而'_'这个字符在字母表中排在'a'->'Z'之后,所以如果你遵循命名规范,你的公共API应该会排在自动补全列表的最前面。遵循这个标准是他们必须学习的内容,所以我觉得不需要刻意隐瞒。

我能理解你为什么想要把Django的公共API隐藏起来,但想想如果只是隐藏基类的API而不是实际抽象掉它,会有什么后果:如果一个学生不知道某个方法名已经被基类使用了(因为它在自动补全中没有出现,或者你提供的文档没有说明),他可能会给自己的方法起同样的名字,结果完全搞砸了,因为他在不知情的情况下覆盖了一个方法。

基本上,你是在试图把类里面的内容隐藏起来,而不是教他们需要知道的信息,以避免做不该做的事情。这几乎总是会导致问题的可能性增加。而且这也在教他们完全依赖于他们的IDE的功能;比如说,如果在自动补全中没有出现,那就说明它不存在!

我觉得你需要看看你担心的根本原因,并尝试设计课程来解决这些问题,而不是想出一些变通的方法,这样可能会教给学生错误的编程习惯。学会正确阅读文档是成为程序员非常、非常重要的一部分。不要试图让他们不去学习这个教训。实际上,不学习这个教训是导致99%问题(尤其是在Python相关问题上)的原因之一,这在Stack Exchange上尤其明显。

2

我建议你使用组合而不是继承。这样你可以设计类的接口,决定哪些方法是可以使用的。

8

免责声明:下面的方法不是很干净(但这正是你想要的)。

好消息是,这个方法不会破坏你的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.,可能会显示出来。你可以试试看,亲自体验一下。

撰写回答