I have two classes that loosely take the form below:
class Foo:
def __init__(self, foo):
self.__foo = foo
class Bar(Foo):
def bar(self):
print self.__foo
When I try to invoke the bar method on an instance of Bar, it fails.
b = Bar('foobar')
b.bar()
Result:
Traceback (most recent call last):
File "foobar.py", line 14, in
b.bar()
File "foobar.py", line 10, in bar
print self.__foo
AttributeError: Bar instance has no attribute '_Bar__foo'
My understanding is that this code should work based on two other questions, why doesn't it?
解决方案
Simple. __foo contains 2 underscores in the beginning, so it's assumed to be class-private method and it's transformed into _Classname__method.
When you request access to the attribute named as such on Bar object it asks Bar class if it has this method (not Foo class), so self.__foo is always the same as self._Bar__foo.
When an identifier that textually occurs in a class definition begins
with two or more underscore characters and does not end in two or more
underscores, it is considered a private name of that class. Private
names are transformed to a longer form before code is generated for
them. The transformation inserts the class name, with leading
underscores removed and a single underscore inserted, in front of the
name. For example, the identifier __spam occurring in a class named
Ham will be transformed to _Ham__spam.
If you modify your code slightly
class Foo:
def __init__(self, foo):
self.__foo = foo
assert hasattr(self, '_Foo__foo'), 'Attribute has been just created'
class Bar(Foo):
def bar(self):
assert hasattr(self, '_Foo__foo'), 'No errors, thanks to inheritance'
assert statements will not cause any AssertionErrors.
Add __getattribute__ method to Bar class to capture all requests to Bar objects:
class Bar(Foo):
def bar(self):
print('Accessing __foo from bar')
print(self.__foo)
def __getattribute__(self, name):
print('Requested', name)
return super().__getattribute__(name)
b = Bar('foobar')
b.bar()
There will be 3 lines (apart from AttributeError) in the output:
Requested bar
Accessing __foo from bar
Requested _Bar__foo # AttributeError follows
As you can see, if attribute you are requesting has 2 leading underscores, Python is renaming it on the fly.