I am a Java developer and learning Python. I am using Python 3.10.2. I am trying to understand the read only variables in Python (Similar to final variable in Java), but I am bit confused about the way Python handles things. For example, below is my code.
class Plant:
def __init__(self, name: str):
self.__name = name
@property
def name(self):
return self.__name
table_rose = Plant("Table Rose")
print(f"Name of the plant - {table_rose.name}")
#trying to change the name.. Expecting Attribute error in the below line.
table_rose.__name = "Croutons"
print(f"Name of the plant - {table_rose.name}") #name didnt change..
print(f"Name of the plant - {table_rose.__name}") #It prinits Croutons.. Confusing how python handling things.
The above code prints all three print functions like below, but I was expecting AttributeError
My code output
Name of the plant - Table Rose
Name of the plant - Table Rose
Name of the plant - Croutons
Can anyone explain why I didnt get any error?
>Solution :
In general, Python doesn’t really do read-only variables like Java does. So when looking at Python code when you might expect a Java class to have a private field, don’t be surprised when everything is just an open variable and not wrapped up in a property decorator.
Anyways, onto your code 🙂
The cause for your confusion is something called name mangling. When you’re inside a class and create an attribute that starts with two underscores (e.g. self.__name), as a feature to allow you to avoid nameclashes when subclassing, Python inserts the class’ name in front (making it actually self._Plant__name under the hood).
But when outside your code, and you reference table_rose.__name, Python doesn’t do the automatic name mangling (as you’re outside the class), so it references just the plain .__name attribute, completely missing the one that you want (called ._Plant__name).
You can see this by using the dir() function. If you pass dir an object, it will return a list of all of that object’s attributes.
>>> dir(table_rose)
['_Plant__name', ... , '__name', ...]
This means that you’re creating a brand new .__name variable with the value 'Croutons', rather than changing the existing one.