我错过了什么,或者这样的事情不可能吗?
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'
答案 0 :(得分:5)
您正在尝试将Java技术应用于Python类。别。 Python没有像Java那样的隐私模型。 类及其实例上的所有属性始终可访问,即使在类中使用__name
双下划线名称(它们只是重命名为添加命名空间) )。
因此,您也不需要内部类,因为这样的类没有特权访问权限。您可以将该类放在 Outer
之外,并具有完全相同的访问级别。
您遇到了错误,因为Python 在类上下文中使用初始双下划线名称重命名属性,以免避免与子类冲突。这些被称为 class private ,因为重命名会将类名称添加为命名空间;这适用于他们的定义和使用。请参阅参考文档的Reserved classes of identifiers section:
__*
类私有名称。当在类定义的上下文中使用时,此类别中的名称将被重写以使用损坏的表单来帮助避免基类和派生类的“私有”属性之间的名称冲突。
Outer
中包含双下划线的所有名称都会重命名为_Outer
前缀,因此__getVal
会重命名为_Outer__getVal
。 Inner
中的任何此类名称都会发生相同的情况,因此您的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.attribute
和obj.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
。