Criando getters e setters no modo “pythonico”. (#dev #python #oop)

Criando getters e setters no modo “pythonico”. (#dev #python #oop)

Overview

Se você já se perguntou sobre como melhorar o controle de suas classes em Python, este post é para você! Aqui, vamos mergulhar em um exemplo divertido, mas altamente informativo, sobre como implementar getters e setters. Com uma abordagem passo a passo, exploraremos a criação de uma classe 'User' para demonstrar como podemos administrar o acesso e as modificações de suas propriedades. Preparado para aprender e, quem sabe, modificar alguns dos seus próprios códigos após isso? Vem comigo!

Neste post mostro um exemplo de como criar getters e setters de forma que você possa criar fluxos mais complexos. O exemplo que utilizo vai ser uma classe que te informa se ela foi modificada ou nao desde que foi criada.

A classe que vou utilizar chama-se User e possui as seguintes propriedades: Name, Login, Password, Email e uma "propriedade privada" chamada "is_modified".

Veja o código:

 1class User(object):
 2    def __init__(self, name: str, login: str, password: str, email: str):
 3        self.name = name
 4        self.login = login
 5        self.password = password
 6        self.email = email
 7        self.__is_modified__ = False
 8
 9    def is_modified(self):
10        return self.__is_modified__

Bom, esta é uma classe normal. Nada demais nela. O que vamos fazer agora é adicionar 2 métodos para cada uma das propriedades (exceto a __is_modified__, ela nao deve ser acessada de fora da classe).

Começando pela propriedade name, vamos fazer o getter:

1    @property
2    def name(self):
3        return self.__name

O getter é composto pelo decorator @property (nativo do python, nao precisa instalar pacotes extras), o método com o mesmo nome da propriedade que queremos criar o getter e o setter. (no caso, name). Este método apenas retorna uma propriedade chamada __name. Todavia, este nome é arbitrário, você pode colocar o nome que desejar, mas repetir o nome da propriedade, incluindo os indicadores de que ela é privada, costuma a ser uma boa ideia.

Agora vamos fazer o setter da propriedade name:

1    @name.setter
2    def name(self, value):
3        if value is None:
4            raise ValueError("Name cannot be none!")
5        self.__is_modified__ = True
6        self.__name = value

O decorator do setter é o nome do método getter, chamando o setter (@name.setter). Este método recebe um argumento, que é o novo valor da propriedade.

Você nao precisa, mas eu coloquei uma validação ali. O nome não pode ser nulo. Então, se a pessoa tentar passar uma string nula como o nome, será lançada uma exception. Também mudei o valor da propriedade __is_modified__ para True e depois fiz o que o setter deveria fazer: definir o valor da propriedade Name.

Assim como no setter, a propriedade interna nao precisa se chamar __name, mas elas tem que ter o mesmo nome entre si, ou seja, o que você usar no setter, tem que usar no getter.

A apliquei a mesma lógica as outras propriedades da classe:

 1class User(object):
 2    def __init__(self, name: str, login: str, password: str, email: str):
 3        self.name = name
 4        self.login = login
 5        self.password = password
 6        self.email = email
 7        self.__is_modified__ = False
 8
 9    def is_modified(self):
10        return self.__is_modified__
11
12    @property
13    def name(self):
14        return self.__name
15
16    @name.setter
17    def name(self, value):
18        if value is None:
19            raise ValueError("Name cannot be none!")
20        self.__is_modified__ = True
21        self.__name = value
22
23    @property
24    def login(self):
25        return self.__login
26
27    @login.setter
28    def login(self, value):
29        if value is None:
30            raise ValueError("Login cannot be none!")
31        self.__is_modified__ = True
32        self.__login = value
33
34    @property
35    def password(self):
36        return self.__password
37
38    @password.setter
39    def password(self, value):
40        if value is None:
41            raise ValueError("Password cannot be none!")
42        self.__is_modified__ = True
43        self.__password = value
44
45    @property
46    def email(self):
47        return self.__email
48
49    @email.setter
50    def email(self, value):
51        if value is None:
52            raise ValueError("Email cannot be none!")
53        self.__is_modified__ = True
54        self.__email = value

Obviamente, a classe ficou bem maior, mas agora temos o controle sobre o que é devolvido e definido em cada propriedade da classe. No caso, garanti que nenhuma das propriedades dela serão nulas.

Agora para testar o método que detecta se o objeto foi modificado:

1if __name__ == '__main__':
2    user = User(name="John User", login="j.user", email="[email protected]", password="p4ssw0rd!")
3
4    print(f"Created an User. Is it modified? {user.is_modified()}")
5
6    user.name = "Updated name"
7
8    print(f"Changed an User. Is it modified? {user.is_modified()}")

Exemplo de saída:

1Created an User. Is it modified? False
2Changed an User. Is it modified? True

Coloquei este exemplo no meu Github.

Espero ter ajudado!