2
respostas

[Dúvida] Comportamento estranho do Setter

Escrevi o seguinte código para limitar os tipos de moto que poderiam ser inseridos a 2:

from ex.vehicle.modules.vehicle import Vehicle


class Bike(Vehicle):
    def __init__(self, model, brand, bike_type):
        super().__init__(model, brand)
        self._bike_type = bike_type

    def __str__(self):
        return ( 
            f"{super().__str__()}" \
            f"Bike type: {self.bike_type}\n"
        )

    @property
    def bike_type(self):
        return self._bike_type
    
    @bike_type.setter
    def bike_type(self, type):
        if type.lower() == "sporty" or type.lower() == "casual":
            self._bike_type = type
        else:
            print("The type of bike can be just 'casual' or 'sporty'.")

.

Problema: por alguma motivo, quando eu crio uma instância da classe, tal como:

bike = Bike(
    model="Hornet 2000",
    brand="Honda",
    bike_type="anything",
)

O parâmetro "bike_type" é aceito (mesmo que não seja permitido pelo meu setter). Aparentemente, isso ocorre pois quando estamos criando um objeto, o mesmo atribui os valores diretamente aos atributos sem antes passar pelo setter.

.

Não consigo entender por que isso foi criado dessa forma. Gostaria de saber como contornar esse comportamento estranho do Python ou se, na verdade, estou cometendo algum erro na minha escrita.

2 respostas

Oi Mikael!

O que você está observando é um comportamento comum em Python devido à forma como o método __init__ funciona. Quando você cria uma instância da classe Bike, o método __init__ é chamado, e ele atribui diretamente o valor do parâmetro bike_type ao atributo _bike_type sem passar pelo setter. Isso acontece porque, dentro do __init__, você está acessando diretamente o atributo privado _bike_type.

Para contornar esse comportamento e garantir que o valor passe pelo setter, você pode modificar o __init__ para usar o setter em vez de atribuir diretamente ao atributo. Veja como você pode fazer isso:

class Bike(Vehicle):
    def __init__(self, model, brand, bike_type):
        super().__init__(model, brand)
        self.bike_type = bike_type  # Use o setter aqui

    def __str__(self):
        return (
            f"{super().__str__()}"
            f"Bike type: {self.bike_type}\n"
        )

    @property
    def bike_type(self):
        return self._bike_type
    
    @bike_type.setter
    def bike_type(self, type):
        if type.lower() == "sporty" or type.lower() == "casual":
            self._bike_type = type
        else:
            print("The type of bike can be just 'casual' or 'sporty'.")

Ao usar self.bike_type = bike_type no __init__, você está chamando o setter, que então verifica se o tipo de bike é válido antes de atribuir o valor ao atributo privado _bike_type.

Espero ter ajudado e bons estudos!

Isso parece que irá gerar um RecursionError caso dê certo: o que não dará, pois o atributo ao qual o getter e setter estão se referindo não existe. E mais, agora o atributo está como se fosse public ao invés de protected.

Por acaso gerou essa resposta com algum recurso de IA? Está bastante incompleto, terei de refazer a pergunta no fórum.