关于oop:在Python子类中使方法私有化

Making a method private in a python subclass

是否可以在子类中使公共方法私有化?我不希望扩展这个类的其他类能够调用某些方法。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A:
    def __init__(self):
        #do something here

    def method(self):
        #some code here

class B(A):
    def __init__(self):
        A.__init__(self)
        #additional initialization goes here

    def method(self):
        #this overrides the method ( and possibly make it private here )

从这一点来看,我不希望任何从B扩展的类能够调用method。这有可能吗?

编辑:这样做的一个"逻辑"原因是我不希望用户以错误的顺序调用方法。


与此主题的流行方式相反,无论您是在Python中工作还是在更传统的OOP环境中工作,都有合理的理由区分公共、私有和受保护的成员。很多时候,您需要在对象专门化的某个级别上为一个特别冗长的任务开发辅助方法。不用说,您真的不希望这些方法被任何子类继承,因为它们在专门的上下文中没有意义,甚至不应该是可见的;但是它们是可见的,并且它们会降低诸如制表符完成、对象导航器和其他系统软件的效用,因为在abstra的所有不同级别上的所有内容行动被压扁并被扔在一起。请注意,这些编程辅助工具并不琐碎。如果你是一个学生,并且仅仅因为你在学习如何做就喜欢做上百万次同样的事情,那么它们是微不足道的。

历史上,由于思想惰性和兼容性问题,Python的开发方式使得实现公共/私有区别变得越来越困难。这是事实。如果每个人都改变他们所做的事情,那将是一个真正的头疼。因此,我们现在有上百万的Python粉丝,他们都读过同一篇或两篇原创文章,明确地决定了公共/私有的区别是"非对称的"。这些人,由于缺乏批判性的思想或对广泛传播的公平性,共同的做法,立即利用这个机会来增加一系列可预测的应用科学——防御蛇——我怀疑这不是由于对via pythonis(pythonic方式)的理性选择,而是由于忽视了其他语言,他们要么选择不。t使用,不熟练使用,或因工作不能使用。

正如有人所说,在python中,要产生类似于私有方法的效果,最好是在方法名前面加上__(两个下划线)。另一方面,实际上,这项工作唯一能完成的就是在对象的__dict__中插入一个变形的属性名。例如,假设您具有以下类定义:

1
2
3
class Dog(object):
    def __bark(self):
        print 'woof'

如果你运行dir(Dog()),你会看到一个奇怪的成员,叫做_Dog__bark。实际上,这种技巧存在的唯一原因是为了绕过我之前描述的问题:即,防止继承、重载和替换超级方法。

希望将来会有一些私人方法的标准化实施,当人们意识到组织不需要接触到单个细胞复制DNA的方法时,有意识的头脑需要不断地思考如何修复组织和内部器官。


在Python中无法真正做到这一点。这是相当不讲理的。

正如圭多所说,我们都是同意的成年人。

下面是关于Python公开背后的哲学的一个很好的总结。


可以使用单个或双下划线为方法和成员加前缀。一个下划线表示"请不要使用我,我应该只被这个类使用",一个双下划线指示python编译器使用类名来管理方法/成员名;只要类及其子类没有相同的名称,方法/成员就可以被视为"私有"。

但是,到目前为止,您的需求的解决方案是编写清晰的文档。如果不希望用户以错误的顺序调用方法,请在文档中这样说。

毕竟,即使C++的私有程序也不是私人的。例如,想想老把戏:

1
2
#define private public
#include <module>

我很惊讶没有人提到这一点,但是在方法名前面加上一个下划线是将其标记为"private"的正确方法。当然,这并不是真正的隐私,(如其他答案中所解释的那样),但你可以这样做。

1
2
def _i_am_private(self):
   """If you call me from a subclass you are a naughty person!"""


python作为源进行分发。私人方法的想法没有什么意义。

想要扩展B的程序员,由于隐私问题而感到沮丧,他查看B的源代码,复制并粘贴method的源代码到C的子类中。

你通过"隐私"得到了什么?你最好的希望就是让你的潜在客户无法复制和粘贴。

最坏的情况下,他们会丢弃你的包裹,因为他们无法扩展它。

是的,所有开放源码都是以某种方式扩展的。你不能预见你的代码将被应用到的每一件事和每一个用途。当代码作为源代码分发时,很难阻止将来的某些使用。

看看如何保护python代码?

编辑"防白痴"代码。

首先,90%的时候,python是作为源代码分发的。所以,任何下载、安装、然后拒绝阅读API指南并无序调用方法的白痴仍然可以从源代码中找出问题所在。

我们有三类白痴。

  • 拒绝阅读API指南(或忽略相关部分)和调用方法的人,尽管有文档记录。你可以试着把事情保密,但这不会有帮助,因为他们会做错事——然后抱怨。[我不会指名道姓,但我和那些似乎花费大量时间不恰当地调用API的人一起工作过。此外,您还会看到这样的问题。]

    您只能帮助他们使用可以剪切和粘贴的工作代码示例。

  • 那些被API弄糊涂了的人,以你能想象到的各种不同的方式调用这些方法(有些人你不能),你可以尝试使某些东西私有化,但他们永远不会得到API。

    您只能通过提供工作代码示例来帮助他们;即使这样,他们也会错误地剪切和粘贴它。

  • 那些拒绝你的API并想重写它使其成为"白痴证明"的人。

    您可以为他们提供一个可工作的代码示例,但他们不喜欢您的API,并坚持要重写它。他们会告诉你你的API是疯狂的,他们已经改进了。

    你可以让这些人参加一场不断升级的"防白痴"军备竞赛。你把所有的东西放在一起,它们就分开了。

在这一点上,隐私权为您做了什么?有些人会拒绝理解它;有些人对此感到困惑;有些人想围绕它工作。

公众呢,让你称之为"白痴"的人从你的代码中学习?


"一切都必须公开"的支持者认为作者试图向用户隐藏一个有用的API。这家伙不想违反毫无疑问的Python法则。他希望使用一些方法来定义一个有用的API,并且希望使用其他方法来组织该API的实现。如果两者之间没有分离,并不意味着作者不是白痴。这意味着作者太懒了,无法真正定义API。

在python中,不将属性或方法标记为私有,它们可以前面加上_作为弱的"内部使用"指示器,或者加上__作为稍强的指示器。在一个模块中,名称可以以同样的方式前缀为_,您也可以在名为__all__的变量中放入构成模块公共API的字符串序列。

愚蠢的一致性是小头脑中的妖精。


这可能是一个合理的近似值。"拯救"的词汇范围界定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python

class Foo(object):
    def __init__(self, name):
        self.name = name
        self.bar()

    def bar(self):
        def baz():
            print"I'm private"
            print self.name

        def quux():
            baz()

        self.quux = quux


if __name__ =="__main__":
    f = Foo("f")
    f.quux()

    g = Foo("g")
    g.quux()

    f.quux()

印刷品:

1
2
3
4
5
6
I'm private
f
I'
m private
g
I'm private
f

将继承的方法从public更改为private会中断继承。具体来说,它打破了IS-A关系。

想象一下,一个餐馆的班级有一个开门的公共方法(也就是说,现在是正餐时间,我们想把前门的标志从关上翻到开上)。现在我们想要一个餐饮类,它将共享餐厅的许多实施细节(它们都需要厨师、厨房、菜肴、食品供应商,甚至可能是服务员),但没有餐饮区、前门或开门方法。从餐馆继承遗产是错误的!似乎你所要做的就是将"开门"方法改为"私人",这样就没有人可以使用它,但是"任何餐饮对象都是餐厅"是错误的,因为餐厅的公共界面的一部分是"开门"。最好通过添加一个新的基类来重构餐厅,然后餐厅和餐饮都从中派生出来。

具有受保护或私有继承概念的语言只支持这种继承实现的思想,但Python不是其中之一。(在这些语言中也没有用处,除非很少)通常当寻求非公共继承时,包容(又称"has-a relationship")是更好的路径,甚至可以使属性受保护或私有。

python的拼写用一个前导下划线保护,private用双前导下划线保护,修改了triptych答案中提到的"同意成人"哲学。类的"危险"属性和方法,例如可能导致数据丢失的属性和方法,应该是非公共的(无论是使它们受保护还是私有的,都会受到其他因素的影响),公共方法用于提供更简单、更安全的接口。


要使一个方法成为私有的,您可以这样命名一个方法:

1
2
3
4
5
6
7
class Foo:
    def __blah():
        pass

class Bar(Foo):
    def callBlah():
        self.__blah() # will throw an exception

但是子类仍然可以通过内省找到你的方法,如果他们真的想要的话。

但是python(经过深思熟虑的设计和选择)没有私有成员的概念。


1
2
3
4
5
6
class MyClass(object):
    def _prtectedfunc(self):
        pass # with one undersocre

    def __privatefunc(self):
        pass # with two undersocre

http://linuxwell.com/2011/07/21/private-protected-and-public-in-python/


即使在同意的成年人中,也有理由不想将方法公开给派生类。问题是,我是否可以有一个类似parent>child的层次结构,其中用户可以从parent或child派生,但不能公开任何私有或受保护的方法。在下面的示例中,子级显式重载父级。uu private而不公开它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Parent(object):
    def __private(self):
        print 'Parent'

    def go(self):
        self.__private()

class Child(Parent):
    def __init__(self):
        Parent.__init__(self)
        self._Parent__private = self.__private

    def __private(self):
        print 'Child'


这个问题有很多好的答案,但从实践的角度来看,这里有一些固执己见的答案:

您确实需要避免公共API、IntelliSense、Docs、import *等对私有成员的损坏,因此请始终将或附加到这些成员。

大问题是:应该是uuu还是uuu?纯粹主义OOP的观点是始终将私有成员和受保护方法附加在一起。_uuuu会造成名称的混乱,使得很难使用setattrib、测试框架以及降低可读性。因此,我个人的观点是尽量避免使用私人会员。

但是,我们希望派生类使用但不作为公共API的受保护成员呢?我在这里的偏好是完全不使用下划线。这允许这些成员进入公共文档。然后,我们依靠docstring告诉用户,这些是这样的受保护成员:

1
2
3
4
5
6
7
8
9
10
class Class1:
  def _private1():
    pass

  def protected1():
   """(for derived classes) Does XYZ."""
    pass

  def public1()
    pass

这个问题有很多好的答案,但从实践的角度来看,这里有一些固执己见的答案:

您确实需要从公共API中隐藏(也称为文档)低级变量和方法。因此,一定要把uuo或uuu附在这些成员身上。更大的问题是:应该是uuu还是uuu?纯粹主义OOP的观点是始终将私有成员和"受保护"方法(即可访问、可从派生类重写)附加在一起。_uuu确保派生类不能重写它。另一方面,它也使得使用诸如setattr、可测试性和可读性之类的东西变得困难。

我个人的观点(以及谷歌的风格指南)是尽量避免使用私人会员。

但受保护的成员呢?我们如何告诉图书馆用户他们必须/应该重写什么?这里,我不喜欢为受保护的成员使用下划线,而是依赖于docstring注释和基类中可选的@abstractmethod。这里的一个优点是受保护的成员不会从各种文档生成器中删除。