Error "Maximum recursion Depth exceeded" when implementing Setter

Asked

Viewed 234 times

1

I’m learning python and wrote the following code:

class Produto:

    def __init__(self, nome, preco):
        self.nome = nome
        self.preco = preco

    def desconto(self, percentual):
        self.preco = self.preco - self.preco * percentual / 100

    @property
    def nome(self):
        return self.nome

    @nome.setter
    def nome(self, valor):
        self.nome = valor

    @property 
    def preco(self):
        return self.preco

    @preco.setter
    def preco(self, valor):
        self.preco = valor

p1 = Produto('Camiseta', 50)

p1.desconto(10)

However, when running, the following error is displayed:

Traceback (most recent call last):
  File "C:\Users\levil\PycharmProjects\pythonProject1\teste.py", line 27, in <module>
    p1 = Produto('Camiseta', 50)
  File "C:\Users\levil\PycharmProjects\pythonProject1\teste.py", line 4, in __init__
    self.nome = nome
  File "C:\Users\levil\PycharmProjects\pythonProject1\teste.py", line 16, in nome
    self.nome = valor
  File "C:\Users\levil\PycharmProjects\pythonProject1\teste.py", line 16, in nome
    self.nome = valor
  File "C:\Users\levil\PycharmProjects\pythonProject1\teste.py", line 16, in nome
    self.nome = valor
  [Previous line repeated 994 more times]
RecursionError: maximum recursion depth exceeded

Process finished with exit code 1

Could someone please explain to me what this is all about?

  • consider 1) edit the title to better reflect the content of the question and 2) accept one of the answers if they have answered your question.

2 answers

4


Answering your question the error message informs that one of the attributes of your class Produto is in loop auto updating recursively.

Paying attention to the error message:

File "C: Users levil Pycharmprojects pythonProject1 teste.py", line 27, in
p1 = Produto('Camiseta', 50)

  • Tells you to create a class instance Produto triggered an exception...

File "C: Users levil Pycharmprojects pythonProject1 teste.py", line 4, in init
self.nome = nome

  • Informs that within the constructor when performing the activity of assigning the argument nome for the attribute Produto.nome was triggered an exception...

File "C: Users levil Pycharmprojects pythonProject1 teste.py", line 16, in name
self.nome = valor

  • Informs that within the function intended to store the valor for the attribute Produto.nome an attempt was made to assign the argument valor for the attribute Produto.nome...

File "C: Users levil Pycharmprojects pythonProject1 teste.py", line 16, in name
self.nome = valor
File "C: Users levil Pycharmprojects pythonProject1 teste.py", line 16, in name
self.nome = valor
[Previous line repeated 994 more times]

Recursionerror: Maximum recursion Depth exceeded

  • Which in turn informs that again called the function intended to store the valor for the attribute Produto.nome where an attempt was made to assign the argument valor for the attribute Produto.nome and again called the function intended to store the valor for the attribute Produto.nome where an attempt was made to assign the argument valor for the attribute Produto.nome and again called the function intended to store the valor for the attribute Produto.nome where an attempt was made to assign the argument valor for the attribute Produto.nome and again called the function intended to store the valor for the attribute Produto.nome where an attempt was made to assign the argument valor to the property Produto.nome....
    ...this by 994 times until the maximum recursion depth was violated triggering an exception Recursionerror.

After analyzing the error message and re-analyzing the code you can conclude that it will even occur with the attribute Produto.preco after fixing the problem with Produto.nome.

To repair the code and the make it work is to cause the functions that assign values to the attributes Produto.nome and Produto.preco no longer invoke themselves and store their respective values in instance variables as you instituted access and modifier methods for attributes it is presumable that these instance variables are private variables.

class Produto:

    def __init__(self, nome, preco):
        #Atribui os argumentos aos seus respectivos atributos.
        self.nome = nome              
        self.preco = preco          

    def desconto(self, percentual):
        self.preco = self.preco - self.preco * percentual / 100

    @property
    def nome(self):
        return self._nome            #Retorna o valor da variável privada _nome

    @nome.setter
    def nome(self, valor):
        self._nome = valor           #Atribui valor a variável privada _nome

    @property 
    def preco(self):
        return self._preco           #Retorna o valor da variável privada _preco

    @preco.setter
    def preco(self, valor):
        self._preco = valor           #Atribui valor a variável privada _preco

p1 = Produto('Camiseta', 50)

p1.desconto(10)

print(p1.preco)                       #imprime 45.0

How to make it work is not the same as being right, its two attributes Produto.nome and Produto.preco are wordy because the implemented form are only public variables endowed with an intrinsic overhead, the implementation of methods accessors and modifiers that do the same as a public variable would. Maybe the method Produto.desconto() is a candidate to become an attribute because it can be interesting to know the percentage of discount incident in a Produto and when reset the discount would automatically modify the price:

class Produto:

    def __init__(self, nome, preco, desconto=0):
        #Atribui os argumentos a suas respectivas variáveis públicas.
        self.nome = nome                 
        self.preco = preco
        self.desconto = desconto                    

    #Acessor do atributo desconto.
    @property
    def desconto(self):
        return self._desconto             #Retorna o valor da variável privada _desconto.
        
    #Modificador do atributo desconto.
    @desconto.setter
    def desconto(self, percentual): 
        self._desconto = percentual                              #Atribui percentual a variável privada _desconto
        self.preco = self.preco - self.preco * percentual / 100  #Recalcula o preço com o novo percentual
        

    
p1 = Produto('Camiseta', 50)

p1.desconto = 10                 

print(p1.preco)                   #imprime 45.0

Another possible approach is to have Produto.nome and Produto.desconto as public variables and Produto.preco with a read-only attribute:

class Produto:

    def __init__(self, nome, preco, desconto=0):
        #Atribui os argumentos a suas respectivas variáveis públicas .
        self.nome = nome   
        self.desconto = desconto   
        #Atribui os argumentos a suas respectivas variáveis privadas.
        self._preco = preco                 

    #Define o atributo readonly com valor calculado na consulta
    @property
    def preco(self):
        return self._preco - self._preco * self.desconto / 100

    
    
p1 = Produto('Camiseta', 50)

p1.desconto = 10                 

print(p1.preco)                   #imprime 45.0

Only by analyzing the project requirements can one decide how best to implement the class Produto. For example it may have a requirement where it specifies that once created the product its name cannot be changed, or else a requirement implying that when changing the product name automatically a database is updated.

  • Aaah, got it, thank you so much for your time ;)

3

Your code is looping, this is because its internal property nome is also a setter.

So when do you do an assignment on self.nome, as is done in the class builder, he calls the @nome.setter, which in turn again makes an assignment on nome and called again the @nome.setter entering an infinite loop until generating exception.

To better understand, run the example in the link below, it has some prints for you to better view the situation:

https://repl.it/@Dadinel/ScornfulWarlikeInterpreter


To correct the situation, it is common to treat the internal properties of the Python class starting with underline, example:

class Produto:

    def __init__(self, nome, preco):
        self.nome = nome
        self.preco = preco

    def desconto(self, percentual):
        self.preco = self.preco - self.preco * percentual / 100

    @property
    def nome(self):
        return self._nome

    @nome.setter
    def nome(self, valor):
        self._nome = valor

    @property 
    def preco(self):
        return self._preco

    @preco.setter
    def preco(self, valor):
        self._preco = valor

p1 = Produto('Camiseta', 50)

print(p1.preco)

p1.desconto(10)

print(p1.nome)
print(p1.preco)

See online: https://repl.it/@Dadinel/UnawareGiddyDriver


Take a look at this question and answer about getters and setters in Python:

Pythonic way of defining setters and getters

  • Understood, thank you very much for your time... I will see the link ;)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.