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