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?
CodePudding user response:
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.