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!