Home > Software design >  Python3 - Modifying Read-Only property - Not throwing AttributeError
Python3 - Modifying Read-Only property - Not throwing AttributeError

Time:02-19

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.

  • Related