在python子类中创建一个私有方法

时间:2009-01-16 20:53:50

标签: python oop inheritance

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

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。 这可能吗?

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

13 个答案:

答案 0 :(得分:29)

与流行时尚相比,合理的理由来区分公共,私人和受保护的成员,无论您是在Python中工作还是在更传统的OOP环境中工作。很多时候,你需要在某种对象专业化水平上为特别冗长的任务开发辅助方法。不用说,你真的不希望任何子类继承这些方法,因为它们在专门的上下文中没有任何意义,甚至不应该是可见的;然而它们是可见的,并且它们削弱了标签完成,对象导航器和其他系统软件之类的功能,因为所有不同抽象级别的所有内容都变得扁平化并被抛在一起。请注意,这些编程辅助工具并非易事。如果你是一名学生,并且因为你正在学习如何而喜欢做一百万次同样的事情,那么它们是微不足道的。

Python历史上以这样的方式开发,由于意识形态的惯性和兼容性问题,实现公共/私人区分变得越来越困难。这是明确的事实。每个人都要改变他们一直在做的事情,这将是一件非常令人头疼的问题。因此,我们现在有一百万名Python粉丝,所有人都阅读了相同的一两篇原创文章,明确地确定公共/私人的区别是“unpythonic”。这些人因缺乏批判性思维或对广泛传播的常规做法的公平性,立即利用这一机会吸收了一系列可预测的应用程序 - De Defensione Serpentis - 我怀疑它不是来自于合理选择通过pythonis (pythonic方式),但忽略其他语言,他们或者选择不使用,不熟练使用,或者因为工作而无法使用。< / p>

正如有人已经说过的,你可以用Python做的最好的效果就是使用__(两个下划线)来预先设置方法名称。另一方面,实际上,唯一能实现的是在对象的__dict__中插入一个变形的属性名称。例如,假设您有以下类定义:

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

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

希望将来有一些标准化的私人方法实施,当人们意识到组织无法获得单个细胞复制DNA的方法时,有意识的头脑需要不断弄清楚如何修复其组织和内脏。

答案 1 :(得分:27)

在Python中无法真正做到这一点。相反,它是非语音的。

正如Guido所说,我们都是在这里同意成年人。

这是一个很好的summary of the philosophy behind everything in Python being public

答案 2 :(得分:12)

您可以使用单个或双下划线为方法和成员添加前缀。单个下划线暗示“请不要使用我,我应该只使用此类”,并且双下划线指示Python编译器使用类名来破坏方法/成员名称;只要类及其子类不具有相同的名称,方法/成员就可以被视为“私有”。

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

毕竟,即使是C ++私有也不是私有的。例如,想想老技巧:

#define private public
#include <module>

答案 3 :(得分:11)

Python作为源分发。私有方法的想法很有意义。

希望扩展B的程序员受到隐私问题的困扰,查看B的来源,将method的源代码复制并粘贴到子类{{1}中}}。

您通过“隐私”获得了什么?您可以期待的最好的方法是让您的潜在客户挫败复制和粘贴。

最糟糕的是,他们丢弃了你的包裹,因为他们无法延长它。

是的,所有开源都以这种或那种方式扩展。您无法预见代码的所有内容和用途。当代码作为源代码分发时,很难防止将来使用。

请参阅How do I protect Python code?


编辑关于“防止白痴”的代码。

首先,python在90%的时间内作为源分发。因此,任何下载,安装,然后拒绝阅读API指南并且无序调用方法的白痴仍然可以找出问题所在。

我们有三类白痴。

  • 拒绝阅读API指南(或浏览并忽略相关部分)并尽管文档无法调用方法的人。你可以试着把某些东西私有化,但它无济于事,因为它们会做出别的错误 - 并抱怨它。 [我不会说出名字,但我和那些似乎花费大量时间调用API不正确的人一起工作过。此外,你会在SO上看到这样的问题。]

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

  • 人们对API感到困惑,并以你能想象的方式调用方法(有些你不能。)你可以尝试私有化,但他们永远不会得到API。

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

  • 拒绝您的API并希望重写它以使其“白痴证明”的人。

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

    你可以让这些人参加一场“白痴”的升级军备竞赛。你把它们放在一起的所有东西都会拆开。

此时,隐私为您做了什么?有些人会拒绝理解它;有些人对此感到困惑;有些人想要解决它。

公众怎么样,让那些你称之为“白痴”的人从你的代码中学习?

答案 4 :(得分:11)

我很惊讶没有人提到这一点,但在方法名称前加上一个下划线是将其标记为“私有”的正确方法。当然,它不是真正的私人(正如其他答案中所解释的那样),但是你去了。

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

答案 5 :(得分:7)

这可能是一个公平的近似值。词汇范围确定“救援”:

#!/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()

打印:

I'm private
f
I'm private
g
I'm private
f

答案 6 :(得分:7)

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

在Python中,不是将属性或方法标记为私有,它们可能以_为前缀作为弱“内部使用”指示符,或者以__为前缀稍微强一些。在模块中,名称可能以相同的方式以_为前缀,您也可以在名为__all__的变量中放置构成模块公共API的字符串序列。

愚蠢的一致性是小脑袋的大人物。

答案 7 :(得分:6)

将继承的方法从公共更改为私有中断继承。具体来说,它打破了is-a关系。

想象一下餐厅课程采用开门式公共方法(即晚餐时间,我们希望将前门标志从关闭翻转到打开状态)。现在我们想要一个餐饮课,它将分享餐厅的许多实施细节(他们都需要厨师,厨房,餐具,食品供应商,甚至可能是服务员),但没有用餐区,前门或开门方法。从餐厅继承是一个错误!看起来您需要做的就是将开门方法改为私有,这样就没有人可以使用它了,但是“任何餐饮对象 - 餐厅”都是假的,因为餐厅公共界面的一部分是敞开的。最好通过添加一个新的基类来重构餐厅,然后餐厅和餐饮都来自它。

具有受保护或私有继承概念的语言仅支持实现继承的这种思想,但Python不是其中之一。 (除了很少,这些语言也没用。)通常在寻求非公共继承时,包含(又称“有关系”)是更好的路径,你甚至可以使属性受到保护或私有。

Python法术受到单一前导下划线和私人双重下划线的保护,修改了Triptych答案中提到的“同意成人”哲学。您班级的“危险”属性和方法,例如:可能导致数据丢失的,应该是非公开的(无论是使其受到保护还是私有受其他因素影响),公共方法用于提供更简单,更安全的界面。

答案 8 :(得分:5)

要使方法排序为私有,您可以命名 - 修改类似的方法:

class Foo:
    def __blah():
        pass

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

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

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

答案 9 :(得分:1)

即使在同意的成年人中,也有理由不想将方法暴露给派生类。问题是,我可以拥有像Parent这样的层次结构&gt;子级,用户可以从父级或子级派生但不暴露任何私有或受保护的方法。在下面的示例中,Child显式重载Parent .__ private而不公开它。

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'

答案 10 :(得分:1)

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/

答案 11 :(得分:0)

此主题中有很多好的答案,但是从实用的角度来看,这是一些自以为是的答案:

您确实需要从公共API隐藏(也称为文档)低级变量和方法。因此,请始终在这些成员上附加_或__。更大的问题:应该是_还是__?纯粹的OOP观点是始终将__附加到私有成员上,并将_附加到“受保护的”方法上(即,可访问的,可从派生类覆盖的方法)。 __确保派生类无法覆盖它。另一方面,它也使得很难使用setattr,可测试性和可读性之类的东西。

我个人的观点(以及Google's style guide)是尽可能避免使用__,并为私人成员使用_。

但是受保护成员呢?我们如何告诉图书馆用户他们必须/应该重写什么?在这里,我不希望对受保护成员使用下划线,而应依靠文档字符串注释以及基类中的@abstractmethod(可选)。这里的一个优点是受保护的成员不会从各种文档生成器中删除。

答案 12 :(得分:0)

此主题中有很多好的答案,但是从实用的角度来看,这是一些自以为是的答案:

要做需要避免来自公共API,智能感知,文档,import *等的私人成员。因此,请始终在这些成员上附加_或__。

一个大问题:应该是_还是__?纯粹的OOP观点是始终将__附加到私有成员上,并将_附加到受保护的方法上。 __确实进行了名称修饰,并使其难以使用诸如setattrib,测试框架之类的内容,并降低了可读性。因此,我个人的观点是尽可能避免使用__,并将_用于私有成员。

但是我们希望派生类使用但不用作公共API的受保护成员呢?我的偏好是完全不使用下划线。这使这些成员可以获取公共文档。然后,我们依靠docstring告诉用户这些是受保护的成员,如下所示:

class Class1:
  def _private1():
    pass

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

  def public1()
    pass