Reputation: 191
Am I missing something, or this something like this not possible?
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
I'm getting this error message:
return self.__outer.__getVal()
AttributeError: Outer instance has no attribute '_Inner__getVal'
Upvotes: 2
Views: 3302
Reputation: 1122342
You are trying to apply Java techniques to Python classes. Don't. Python has no privacy model like Java does. All attributes on a class and its instances are always accessible, even when using __name
double-underscore names in a class (they are simply renamed to add a namespace).
As such, you don't need an inner class either, as there is no privileged access for such a class. You can just put that class outside Outer
and have the exact same access levels.
You run into your error because Python renames attributes with initial double-underscore names within a class context to avoid clashing with subclasses. These are called class private because the renaming adds the class names as a namespace; this applies both to their definition and use. See the Reserved classes of identifiers section of the reference documentation:
__*
Class-private names. Names in this category, when used within the context of a class definition, are re-written to use a mangled form to help avoid name clashes between “private” attributes of base and derived classes.
All names with double underscores in Outer
get renamed to _Outer
prefixed, so __getVal
is renamed to _Outer__getVal
. The same happens to any such names in Inner
, so your Inner.getVal()
method will be looking for a _Inner__getVal
attribute. Since Outer
has no _Inner__getVal
attribute, you get your error.
You could manually apply the same transformation to Inner.getVal()
to 'fix' this error:
def getVal(self):
return self.__outer._Outer__getVal()
But you are not using double-underscore names as intended anyway, so move to single underscores instead, and don't use a nested class:
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()
I renamed Inner
to _Inner
to document the type is an internal implementation detail.
While we are on the subject, there really is no need to use accessors either. In Python you can switch between property
objects and plain attributes at any time. There is no need to code defensively like you have to in Java, where switching between attributes and accessors carries a huge switching cost. In Python, don't use obj.getAttribute()
and obj.setAttribute(val)
methods. Just use obj.attribute
and obj.attribute = val
, and use property
if you need to do more work to produce or set the value. Switch to or away from property
objects at will during your development cycles.
As such, you can simplify the above further to:
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
Here outer.inner
produces a new _Inner()
instance as needed, and the Inner.val
property proxies to the stored self._outer
reference. The user of the instance never need know either attribute is handled by a property
object:
>>> outer = Outer(42)
>>> print outer.inner.val
42
Note that for property
to work properly in Python 2, you must use new-style classes; inherit from object
to do this; on old-style classes on property
getters are supported (meaning setting is not prevented either!). This is the default in Python 3.
Upvotes: 8
Reputation: 15310
The leading-double-underscore naming convention in Python is supported with "name mangling." This is implemented by inserting the name of the current class in the name, as you have seen.
What this means for you is that names of the form __getVal
can only be accessed from within the exact same class. If you have a nested class, it will be subject to different name mangling. Thus:
class Outer:
def foo(self):
print(self.__bar)
class Inner:
def foo2(self):
print(self.__bar)
In the two nested classes, the names will be mangled to _Outer__bar
and _Inner__bar
respectively.
This is not Java's notion of private. It's "lexical privacy" (akin to "lexical scope" ;-).
If you want Inner
to be able to access the Outer
value, you will have to provide a non-mangled API. Perhaps a single underscore: _getVal
, or perhaps a public method: getVal
.
Upvotes: 1