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:

class User(object):
    def __init__(self, name: str, login: str, password: str, email: str):
        self.name = name
        self.login = login
        self.password = password
        self.email = email
        self.__is_modified__ = False

    def is_modified(self):
        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:

    @property
    def name(self):
        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:

    @name.setter
    def name(self, value):
        if value is None:
            raise ValueError("Name cannot be none!")
        self.__is_modified__ = True
        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:

class User(object):
    def __init__(self, name: str, login: str, password: str, email: str):
        self.name = name
        self.login = login
        self.password = password
        self.email = email
        self.__is_modified__ = False

    def is_modified(self):
        return self.__is_modified__

    @property
    def name(self):
        return self.__name

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

    @property
    def login(self):
        return self.__login

    @login.setter
    def login(self, value):
        if value is None:
            raise ValueError("Login cannot be none!")
        self.__is_modified__ = True
        self.__login = value

    @property
    def password(self):
        return self.__password

    @password.setter
    def password(self, value):
        if value is None:
            raise ValueError("Password cannot be none!")
        self.__is_modified__ = True
        self.__password = value

    @property
    def email(self):
        return self.__email

    @email.setter
    def email(self, value):
        if value is None:
            raise ValueError("Email cannot be none!")
        self.__is_modified__ = True
        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:

if __name__ == '__main__':
    user = User(name="John User", login="j.user", email="j.user@gmail.com", password="p4ssw0rd!")

    print(f"Created an User. Is it modified? {user.is_modified()}")

    user.name = "Updated name"

    print(f"Changed an User. Is it modified? {user.is_modified()}")

Exemplo de saída:

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

Coloquei este exemplo no meu Github.

Espero ter ajudado!