If you have not already done so, go back and read Part 1 and Part 2 of this series before proceeding.
So, we left off with a tie ball game. I gave Python a point for its approach to polymorphism but it lost a point for its sloppy inheritance support. So, it all comes down to encapsulation. Does Python provide good protection for things like private variables and err on the side of immutability?
We all know what encapsulation looks like in something like Java or C#. You create a class, declare some variables to be private, and we can be reasonably assured that the internals of our class stay internal to the class. Intellisense will never try to auto-complete for a private variable of class A while referencing the code in some other place.
In Javascript we can use closures to provide encapsulation. Variables declared in the closed scope are accessible to the object being created but are not accessible from outside. For example:
let closure = function(name){ let _name = name; return { sayHello: ()=> console.log("Hello, my name is " + _name) }; }("json"); closure.sayHello(); // "Hello, my name is json" console.log(closure._name); // undefined
The variable _name is effectively private.
So let’s talk about Python. Surely Python, a language which makes the claim of being object-oriented must make it easier than Javascript to create private variables. Right? Sadly, the answer is a big fat no.
class Person: name = None def __init__(self, name): self.name = name def say_hello(): print("Hello, my name is " + self.name) p = Person("json") p.say_hello() # Hello, my name is json print(p.name) # json
Wat?! Unlike Java or C#, there is no private or protected modifier to allow us to easily hide the class internals. Instead, Python suggests following the convention that variables prefixed with a single underscore are protected and variables prefixed with two underscores are private.
class Person: _name = None # "protected" __name = None # "private" def __init__(self, name): self._name = name self.__name = name def say_hello(): print("Hello, my name is " + self._name) def say_private_hello(): print("Hello, my name is " + self.__name) p = Person("json") p.say_hello() # "Hello, my name is json" p.say_hello() # "Hello, my name is json" print(p._name) # json print(p.__name) # AttributeError: 'Person' object has no attribute '__name'
Ok, thats a little better. Our supposedly protected variable is still publicly available though. But at least we can’t make any changes to the private variable anymore. Oh wait… what’s that? Oh you’ve got the be kidding me. W.T.F is THIS:
print(p.__Person_name) # json
Intellisense told me that there is a variable named __Person_name. I certainly didn’t create that… did I?! Turns out, this is what Python does to its “private” variables. It just munges the variable name and adds the class name as a prefix. :facepalm: So in fact, there is no such thing as a private variable in Python. In fact, even the IDEs designed FOR Python like PyCharm expose these variables in intellisense. You’d think that an IDE for the language would at least respect the wishes of the double underscore convention and not expose the variables to the world. But, it does.
You might argue that you can access Java private variables using reflection. Sure, you can. But the language makes you jump through some hoops to do so and the IDEs certainly don’t point you in the right direction.
To put it bluntly, encapsulation in Python plain out stinks. I didn’t even get to mention how in Python, the default for passing parameters to functions is to pass by reference, not value. So, immutability isn’t exactly easy to achieve either unless your class manually copies the values of the arguments it receives. But, why go through all that trouble when some knucklehead can just __ClassName_private_var=evil_object whenever he wants to anyway, right?