This is by design of the "duck-typing" philosophy, adapted by python: "Give it a shot, if it works, great! If it does not work, well...exception will handle that" (more on this duck-typing another time). But, by convention, python does provide a fake level of protection for private methods. Give this code a run and try to understand:
class Foo(object):
def __init__(self):
super(Foo, self).__init__()
self._one = "this is from Foo"
self.__two = "this is from Foo too"
def _getone(self):
print self._one
def __gettwo(self):
print self.__two
def gettwo(self):
self.__gettwo()
if __name__ == '__main__':
f = Foo()
f._getone()
try:
f.__gettwo()
except:
traceback.print_exc()
f.gettwo()
f._getone() will call _getone() method, passing the first argument as f -- an instance of Foo object (the self argument). Everything works as expected, we saw the string "this is from Foo"
f.__gettwo()....raise an exception. There is no __gettwo() method. What the heck? We just defined it!
f.gettwo() will call self.__gettwo() internally, and this time, it works. Why?
Look look: When you dic(f), here is what we get:
['_Foo__gettwo', '_Foo__two', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_getone', '_one', 'gettwo']
Foo class does not have a __gettwo() method, but a _Foo__gettwo() instead! YAY, now we know why there is not a __gettwo() method. Python, by convention, mingle the class name with the method name IF the name starts with double underscore. If you try to call self.__method_name(), Python is smart enough to replace __method_name with _Classname__method_name(), and works as expected. Any external calls to __method_name() will fail since the Class itself does not provide such a method. Same thing for __instance. This provides a "fake" protection from direct manipulation externally.
This also affects inheritance. Since __method_name never exists, you can not try to overwrite with a sub class without using its parents' name. Same goes for attributes:
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self._one = "this is from Bar"
self.__two = "this is from Bar too"
def _getone(self):
print "Bar:", self._one
def __gettwo(self):
print "Bar:", self.__two
if __name__ == '__main__':
b = Bar()
b._getone()
try:
b.__gettwo()
except:
traceback.print_exc()
b.gettwo()
b._getone() will work as expected, while b.gettwo() will print _Foo__two instead.
This cost me an hour of debug time. Hopefully you don't have to do the same.
No comments:
Post a Comment