prashant sharma
prashant sharma

Reputation: 135

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.

Upvotes: 1

Views: 5779

Answers (5)

Steven J Owens
Steven J Owens

Reputation: 848

Generally speaking you do not code getters and setters in python unless you specifically need them, right now. Just access the instance variables directly. If you decide you need getters/setters later, python supports adding them retroactively and having your new getters/setters intercept the direct accesses.

Most of the time you should just define the instance variables, and then in the code that needs to use those variables, just access them directly, i.e.:

p1 = Person("Sandeep",49)
print("%s is %d years old." % (p1._name, p1._age))
# prints "Sandeep is 49 years old."
p1._age = 50
print("Now %s is %d years old." % (p1._name, p1._age))
# prints "Now Sandeep is 50 years old."

You only add getter/setter methods later, when you actually have some need for them.

When you add the getter/setter methods later, you do not need to change any of the existing code that directly accesses the instance variables. Instead, use the @property decorator, shown in LeopardShark's answer above (https://stackoverflow.com/a/71080050/1813403).

The @property decorator does some magic code generation that makes your new getter/setter methods intercept existing code that tries to directly access the instance variables, and send the request through your getter/setter methods instead. So the existing code will keep working but will call your new getter/setter methods instead of just messing directly with the attribute.

Upvotes: 0

LeopardShark
LeopardShark

Reputation: 4466

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)

Upvotes: 2

prashant sharma
prashant sharma

Reputation: 135

Now I realise that I asked a silly question. The reason is obvious of using setters, creating functions for setting a value is a very convenient way to apply the constraints on it. In the example above, the constraint is that the age of the person should be positive and less than 120. Implementation of such constraints is not possible without setters.

Upvotes: 0

Advay168
Advay168

Reputation: 627

You need to tell python how to associate the getter and setter with the actual variable name. To do this you can use the builtin property function like so:

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
    name = property(get_name)
    age = property(get_age, set_age)

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

Then instead of referring to _name and _age use name and age

Upvotes: 2

GhostCat
GhostCat

Reputation: 140641

what I understand is that they are used so that no one could change the object's attributes directly.

Your understanding is wrong. There is no magic rule that tells the python interpreter oh, this class has a setter, so direct access to the fields aren't allowed any more.

In other words: this is merely "works by convention". When you see a python class that has setter methods, then you know you should not access the fields directly, although you still can do that.

And note: python is mostly rather lenient on such things. A lot of things are "by convention" only. It is just "the nature" of python: you can do a lot of things that other languages, especially statically typed ones like Java do not allow.

Upvotes: 0

Related Questions