Python:从内部类访问外部私有函数

时间:2016-04-13 14:09:33

标签: python function class inheritance private

我错过了什么,或者这样的事情不可能吗?

class Outer:
    def __init__(self, val):
        self.__val = val

    def __getVal(self):
        return self.__val

    def getInner(self):
        return self.Inner(self)

    class Inner:
        def __init__(self, outer):
            self.__outer = outer            

        def getVal(self):
            return self.__outer.__getVal()


foo = Outer('foo')
inner = foo.getInner()
val = inner.getVal()    
print val

我收到此错误消息:

    return self.__outer.__getVal()
AttributeError: Outer instance has no attribute '_Inner__getVal'

2 个答案:

答案 0 :(得分:5)

您正在尝试将Java技术应用于Python类。别。 Python没有像Java那样的隐私模型。 类及其实例上的所有属性始终可访问,即使在类中使用__name双下划线名称(它们只是重命名为添加命名空间) )。

因此,您也不需要内部类,因为这样的类没有特权访问权限。您可以将该类放在 Outer之外,并具有完全相同的访问级别。

您遇到了错误,因为Python 在类上下文中使用初始双下划线名称重命名属性,以免避免与子类冲突。这些被称为 class private ,因为重命名会将类名称添加为命名空间;这适用于他们的定义和使用。请参阅参考文档的Reserved classes of identifiers section

  

__*
  类私有名称。当在类定义的上下文中使用时,此类别中的名称将被重写以使用损坏的表单来帮助避免基类和派生类的“私有”属性之间的名称冲突。

Outer中包含双下划线的所有名称都会重命名为_Outer前缀,因此__getVal会重命名为_Outer__getValInner中的任何此类名称都会发生相同的情况,因此您的Inner.getVal()方法会查找_Inner__getVal属性。由于Outer没有_Inner__getVal属性,因此您会收到错误。

您可以手动将相同的转化应用于Inner.getVal()以“修复”此错误:

def getVal(self):
    return self.__outer._Outer__getVal()

但是你并没有按照预期使用双下划线名称,所以转而使用单个下划线,不要使用嵌套类:

class Outer:
    def __init__(self, val):
        self._val = val

    def _getVal(self):
        return self._val

    def getInner(self):
        return _Inner(self)

class _Inner:
    def __init__(self, outer):
        self._outer = outer            

    def getVal(self):
        return self._outer._getVal()

我将Inner重命名为_Inner以记录该类型是内部实现细节。

虽然我们在讨论这个问题,但实际上也没有必要使用访问器。在Python中,您可以随时在property objects和普通属性之间切换。没有必要在Java中进行防御性编码,在Java中,属性和访问器之间的切换会带来巨大的转换成本。在Python中,使用obj.getAttribute()obj.setAttribute(val)方法。只需使用obj.attributeobj.attribute = val,如果您需要做更多工作来生成或设置值,请使用property。在开发周期中随意切换到或远离property个对象。

因此,您可以进一步简化上述内容:

class Outer(object):
    def __init__(self, val):
        self._val = val

    @property
    def inner(self):
        return _Inner(self)

class _Inner(object):
    def __init__(self, outer):
        self._outer = outer            

    @property
    def val(self):
        return self._outer._val

此处outer.inner根据需要生成新的_Inner()实例,Inner.val属性代理存储的self._outer引用。实例的用户永远不需要知道任何属性都由property对象处理:

>>> outer = Outer(42)
>>> print outer.inner.val
42

请注意,要使property在Python 2中正常运行,您必须使用新式类;继承自object来做到这一点;支持property getter上的旧式类(意味着设置也不会被阻止!)。这是Python 3中的默认设置。

答案 1 :(得分:1)

Python中的前导双下划线命名约定支持“名称修改”。这可以通过在名称中插入当前类的名称来实现,如您所见。

这对您来说意味着__getVal形式的名称只能 才能从完全相同的类中访问。如果你有一个嵌套类,它将受到不同的名称修改。因此:

class Outer:
    def foo(self):
        print(self.__bar)

    class Inner:
        def foo2(self):
            print(self.__bar)

在两个嵌套类中,名称将分别损坏为_Outer__bar_Inner__bar

这不是Java的私有概念。这是“词汇隐私”(类似于“词汇范围”;-)。

如果您希望Inner能够访问Outer值,则必须提供非损坏的API。也许只有一个下划线:_getVal,或者可能是公共方法:getVal