How to only allow the creation of valid objects in Python?

1
class Linha(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    @property
    def x(self):
        return self._x
    @x.setter
    def x(self, valor):
        if valor >= 0:
            self._x = valor
    @property
    def y(self):
        return self._y
    @y.setter
    def y(self, valor):
        if valor >= 0:
            self._y = valor

I've created this class that basically abstracts a row, realize that I made use of setters decorators in a way that does not allow value entry less than 0.

But when I try to create a Line instance if I pass one of the parameters as less than 0 it does so by creating the object but does not create this attribute.

Example:

linha = Linha(-1, 1)

If I do this it will create the object but it will not have the attribute x because it will not be set. If even worse if I pass the two negative attributes the object will be created but it will not have any attributes.

My question is: how do I avoid creating an invalid object? In other words, if one or more invalid parameters are passed, the object is not created.

    
asked by anonymous 12.11.2017 / 05:19

1 answer

2

As Jefferson commented on the question, you can throw an exception when the number you set is invalid, in this case less than zero. The exception will bypass the program flow by delegating the error handling logic to an upper layer, the program that creates the instance of the class.

class Linha(object):

    def __init__(self, x, y):
        self.x = x
        self.y = y

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, valor):
        if valor >= 0:
            self._x = valor
        else:
            raise ValueError("The x value must be a non-negative number")

    @property
    def y(self):
        return self._y

    @y.setter
    def y(self, valor):
        if valor >= 0:
            self._y = valor
        else:
            raise ValueError("The y value must be a non-negative number")

Another detail that may be valid to do in this situation, since within the methods you compare the value with an integer, is to ensure that this comparison is possible to make sure that the value is also numeric. That is, if I did obj.x = 'a' , I would give an error saying that the >= operator does not support a comparison between a string and an integer. This is a bad error message to deliver to your client (who uses the class). To work around this, you can convert the input value to integer (or float), before doing the comparison:

class Linha(object):

    def __init__(self, x, y):
        self.x = x
        self.y = y

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, valor):
        valor_inteiro = int(valor)
        if valor_inteiro >= 0:
            self._x = valor_inteiro
        else:
            raise ValueError("The x value must be a non-negative number")

    @property
    def y(self):
        return self._y

    @y.setter
    def y(self, valor):
        valor_inteiro = int(valor)
        if valor_inteiro >= 0:
            self._y = valor_inteiro
        else:
            raise ValueError("The y value must be a non-negative number")

So, if I try to create the Linha('a', 0) instance, the error message will be:

  

ValueError: invalid literal for int () with base 10: 'a'

Which is much clearer about what is wrong with the code, and even causes the method to always throw the same exception, ValueError , in both cases, which facilitates top-level handling.

try:
    x = input("Valor de x:")
    y = input("Valor de y:")
    linha = Linha(x, y)
except ValueError as error:
    print(error)
    
12.11.2017 / 12:58