Forcing read-only Parameter / property in __init__ class constructor in Python

Question:

It might be dumb and repeat question just like Class-level read-only properties in Python, which is hard to understand and implement.
But is there any simple way to stop object user to modify class level defined read-only property (not method), just like other languages that simply use "Private" keyword to make it inaccessible?
For example in this simple code, I want to have "full_name" property to be set as read-only when user initiates object, and not able to change it once initiated by inner-method.

class Test:
    def __init__(self,fname:str,lname:str):
        self.first_name = fname
        self.last_name = lname
        ## expected to be a read only property "full_name"
        self.full_name = self.__private_fullname(fname,lname)
        
    def __private_fullname(self,name1,name2):
        return name1 + ' ' + name2
   
   
    
 # tester
name = Test('John','Watson')
print(name.full_name)   ## returns 'John Watson'
name.full_name ='someone else'   ## still user can change read-only property
print(f'Now full_name directly changed to "{name.full_name}" by object user')
Asked By: Leo Sam

||

Answers:

there is no way at all to define a private variable in python but you can simulate this with (@property) decorator for clean code purpose:
something like code below:

class Test:
    def __init__(self, fname: str, lname: str):
        self.first_name = fname
        self.last_name = lname
        ## expected to be a read only property "full_name"
        self.__full_name = fname + ' ' + lname
    @property
    def full_name(self):
        return self.__full_name


# tester
name = Test('John', 'Watson')
print(name.full_name)  ## returns 'John Watson'
# name.full_name = 'someone else'  ## still user can change read-only property
print(f'Now full_name directly changed to "{name.full_name}" by object user')

if you try to change full_name you get the error like this ->AttributeError: can’t set attribute

Answered By: Hossein Biniazian

The key is to use @property and add some restriction to the setter.
There is no concept of ‘private’ in Python.

class Test:
    def __init__(self, fname: str, lname: str):
        self.first_name = fname
        self.last_name = lname

    @property
    def full_name(self):
        return f'{self.first_name} {self.last_name}'

    @full_name.setter
    def full_name(self, value):
        print("Nope.")


person = Test(fname="asd", lname="zxc")
print(person.full_name)
person.full_name = "qwerty"  # Nope.
Answered By: Alex
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.