pythonoopgetter-setterencapsulationprivate-members

In Python ,why does it end up in recursion while accessing private attribute from getter() ,without using double underscore in getter()


class Person():

    def __init__(self,name:str,height :float) -> None:
        #initialize
        self.__name=name
        self.height=height
    
    @property
    def name(self) -> str:
        print("Inside Getter")
        return self.__name
    
    @name.setter
    def name(self, value):
        print("New name being set")
        self.name = value

    def __repr__(self) -> str:
        return f"({self.__class__.__name__}:{self.name},{self.height})"

p=Person("Sam",180.00)
print(p)

1.In the above code ,when I access the name without using double underscore inside repr() method, it calls getter and then calls repr(). I don't understand why getter gets called here. (Code works as expected when I use double underscore inside repr() too, only repr() gets executed )

Output for the above code is:

Inside Getter

(Person:Sam,180.0)

-----------------------------------------------------------------------------------------------------------------------------

2.Slightly modifying the above code when I also remove the double underscore from the getter()(now both getter() and repr() don't have a double underscore)

 def name(self) -> str:
        print("Inside Getter")
        return self.name

I end up with an recursion error. How did recursion take place here?

in name print("Inside Getter") RecursionError: maximum recursion depth exceeded while calling a Python object


Solution

  • Regarding the recursion, when you access self.name you are actually calling the property

    @property
    def name(self) -> str:
        print("Inside Getter")
        return self.__name
    

    so if you try to access self.name within the property, you'll have set up a recursive loop.

    Typically, you'd use the self.__name attribute to store the data that you want to obfuscate behind getters and setters, so your class would look like this:

    class Person():
    
        def __init__(self, name:str, height:float) -> None:
            self.name = name
            self.height = height
        
        @property
        def name(self) -> str:
            return self.__name # Get self.__name
        
        @name.setter
        def name(self, value):
            self.__name = value # Set self.__name
    
        def __repr__(self) -> str:
            return f"({self.__class__.__name__}:{self.name},{self.height})"