What's the Pythonic Way to Use Getters and Setters

How do getters and setters work in Python?

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)

Using @property versus getters and setters

Prefer properties. It's what they're there for.

The reason is that all attributes are public in Python. Starting names with an underscore or two is just a warning that the given attribute is an implementation detail that may not stay the same in future versions of the code. It doesn't prevent you from actually getting or setting that attribute. Therefore, standard attribute access is the normal, Pythonic way of, well, accessing attributes.

The advantage of properties is that they are syntactically identical to attribute access, so you can change from one to another without any changes to client code. You could even have one version of a class that uses properties (say, for code-by-contract or debugging) and one that doesn't for production, without changing the code that uses it. At the same time, you don't have to write getters and setters for everything just in case you might need to better control access later.

when to use getter and setter with property?

You should generally prefer to use "protected" variables (such as those starting with _) with properties (not separate functions that users need to call, that's just clunky), as it confers some advantages. This encapsulation is very handy as it:

  • lets you control the internal data completely, such as preventing people entering ages like -42 (which they will do if they can); and
  • lets you change the underlying implementation in any manner you want, without affecting clients.

For example on that last point, you may want to maintain a separate structure of all names and simply store references to those names in your Person class. This can allow you to store many more names, as the surname "Von Grimmelshausen" would be stored once (in the separate structure) and as much smaller indexes in all the Person objects that use it.

You can then totally change the naive getter from:

@property
def surname(self):
return self._surname

to:

@property
def surname(self):
return self._surname_db[self._surname_index]

without any changes to clients.

Python Setter and Getter Naming

If you did this:

@color.setter
def color(self, x):
self.color = x

Then this setter would call itself repeatedly. There's no difference between obj1.color = 'blue' and self.color = x, they both do the same thing, which is to invoke the setter def color. So with the setter invoking itself recursively, you have an endless recursive loop which will eventually crash your program.

For that purpose you need to actually store the value on some other attribute. Using "_color" for its name is just the rather obvious solution.

Note that using this kind of setter/getter in Python is frowned upon, if your setter/getter doesn't do anything. You can remove them entirely and just plainly set self.color = 'blue' for exactly the same effect as your setter/getter pair currently has. You should only use setters/getters if they do some additional processing. Since the syntax of a plain attribute and a setter/getter are identical, you can even safely transition to using setters/getters later on if you need to (in contrast to, say, Java, where a setter/getter is foo.setBar(...), which is not identical to foo.bar = ... and cannot be transparently substituted later).

OOP: getter/setter methods

In python do not use getter/setter methods. Instead just access the attribute itself, or, if you need code to be run every time the attribute is accessed or set, use properties.

What is the simplest way to define setter and getter in Python

If the setter and getter do nothing else than accessing an underlying real attribute, then the simplest way of implementing them is not to write setters and getters at all. This is the standard behaviour, and there is no point in writing functions recreating the behaviour the attribute has anyway.

You don't need getters and setters to ensure encapsulation in the case your access logic changes to something different than the standard access mechanics later, since introducing a property won't break your interface.

Python Is Not Java. (And not C# either, for that matter.)

What is the proper way to write getter and setter in Python?

Both the getter and setter need to have the same name:

class ParamDefinition:
def __init__(self, type_name):
self.__type_name = type_name

@property
def type_name(self):
return self.__type_name

@type_name.setter
def type_name(self, type_name):
self.__type_name = type_name

def printf(self):
print(self.__type_name)

def test():
compDef = ParamDefinition(None)

compDef.type_name = "int"
compDef.printf()


if __name__ == "__main__":
test()

Output:

int

EDIT:

While this isn't technically part of the question I thought I should add some more thoughts. You say you are from a C# background, and in C# it's fairly normal to make every field a property by default since you can add extra logic later without breaking the interface, and allows reflection.

These advantages don't really exist with Python and as you can see the boilerplate needed to turn a field into a property is much more significant than in C#. Therefore I'd recommend not using properties in Python until it's really what you need.



Related Topics



Leave a reply



Submit