Search code examples
pythongetter-setter

How do getters and setters work in Python?


I am learning getters and setters , what I understand is that they are used so that no one could change the object's attributes directly. In the example

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age

    def get_age(self):
        return self._age

    def set_age(self, new_age):
        if isinstance(new_age, int) & new_age>0 & new_age<120:
            self._age = new_age

    def get_name(self):
        return self._name
    
    def __str__(self):
        return 'Person[' + self._name + '] is ' + str(self._age)
    
p1 = Person("Sandeep", 49)

I created an object p1 where I set the age 49. As I have made a set_age function so I expect we can change the age of p1 through set_age only, not through the routine way. But it is not happening, I am able to change the age of p1 through , for example, p1._age = 35 as well. Then, what is the advantage to make set_age function, if I am still able to access the attributes directly?

I think, I am missing something, please help.


Solution

  • The reason to use a getter and setter, is if you want to do something more complex than just set and attribute with foo.bar. In your case, set_age has an

    isinstance(new_age, int) & new_age>0 & new_age<120
    

    check, which is not possible to do with a raw attribute. (Side-note: you should use and instead of &.)

    Yes, someone can still do p1._age = -1, and then their code won't work, but why would they? It just makes their code not work.

    Your get_name function is less useful than the age one. It basically makes name read-only, which might or might not be useful.

    When creating setters and getters in Python, it is usual to use the @property decorator. This means the functions can be called as if they were attributes, so instead of p1.get_name() you can just do p1.name. Similarly p1.set_age(3) becomes p1.age = 3.

    You probably want to use the age setter in __init__, because then the age of the Person is validated when it is created.

    Here is a version that makes these changes (and a couple of other readability improvements).

    class Person:
        def __init__(self, name, age):
            self._name = name
            self.age = age
    
        @property
        def age(self):
            return self._age
    
        @age.setter
        def age(self, new_age):
            if isinstance(new_age, int) and 0 < new_age < 120:
                self._age = new_age
        
        @property
        def name(self):
            return self._name
    
        def __str__(self):
            return f"Person[{self.name}] is {self.age}"
        
    p1 = Person("Sandeep", 49)