1
from typing import List, Tuple, Dict, Union

class CatalogueException(Exception):
    pass

class StockException(Exception):
    pass

class Product():
    def __init__(self,_code,_name,_price):
        self._code=_code
        self._name=_name
        self._price=_price

    def __hash__(self):
        return hash(self._code)
    def __str__(self):
        return 'Code: {}\nName: {}\nPrice: {}'.format(self._code,self._name,self._price)
class Catalogue():
    catalogo=[]

    def add_product(self,p):
        catalogo=[]
        if p in catalogo:
            print("el producto")
        catalogo.append(p)

    def remove_product(self,p):
        catalogo.remove(p)

    def __str__(self):
        return('{}'.format(catalogo))

catalogo=Catalogue()
new_spanner = Product("S78", "Adjustable spanner", 35.99)
new_hammer = Product("A45", 'Bush hammer', 15.5)
catalogo.add_product(new_spanner)
catalogo.add_product(new_hammer)
print(catalogo)

Lo que se intenta es que imprima los dos productos añadidos al catalogo y quede de la manera:

Code: A45 
Name: Bush hammer 
Price: 15.5 
Code: S78 
Name: Adjustable spanner 
Price: 35.99

Pero al ejecutar sale el error:

:RecursionError: maximum recursion depth exceeded

He probado a reescribir la funcion str de diferentes maneras pero me sale el mismo error

fedorqui
  • 15,850
  • 17
  • 58
  • 112
  • Sería interesante que leyeras [¿Qué debo hacer cuando alguien contesta mi pregunta?](https://es.stackoverflow.com/help/someone-answers) y marcaras como aceptada una respuesta si te ayudó a resolver el problema. En particular, [la respuesta de FJSevilla](https://es.stackoverflow.com/a/355563/83) es muy completa y bien merece esa marca – fedorqui May 15 '20 at 07:13
  • 1
    Habia votado pero desde luego que se merece la marca. – Diego Gonzalez May 15 '20 at 08:23

3 Answers3

2

Estás instanciando incorrectamente el objeto: cuando defines una clase Clase, lo que tienes es un objeto. A partir de allí, trabajas con el objeto asignándole valores, etc. Esa definición se hace cuando defines objeto = Clase() y se define en __init__(). Sin embargo, tú estás definiendo variables de clase.

Por ello, deberías decir algo así como:

class Catalogue():
    def __init__(self):
        self.catalogo = []

Y luego trabajar con self.catalogo todo el rato:

    def add_product(self, p):
        if p in self.catalogo:
            print("el producto")
        self.catalogo.append(p)

    def remove_product(self, p):
        self.catalogo.remove(p)

Finalmente, para mostrar los valores está bien que uses __str__(), pero hay que darle alguna vuelta:

def __str__(self):
    return '\n'.join(['{}'.format(cat) for cat in self.catalogo])
fedorqui
  • 15,850
  • 17
  • 58
  • 112
2

El problema está en la línea

return('{}'.format(catalogo))

pero viene de antes, implementas mal la clase.

Declaras un atributo de clase que luego no usas

class Catalogue():
    catalogo=[]
    ^^^^^^^^^^

para empezar no debe ser un atributo de clase sino de instancia, dado que cada objeto catálogo tendrá sus productos y no todos los catálogos los mismos productos (atributo de clase).

Además de ello, jamás llegas a usarlo realmente, ésto se debe a que no lo referencias desde los métodos con instancia.catalogo (self.catalogo) o Catalogue.catalogo (cls.catalogo).

Mírate ésta pregunta para entender la diferencia entre ambos tipos de atributos:

En los métodos de instancia usas realmente la variable global catalogo

class Catalogue():

    def remove_product(self,p):
        catalogo.remove(p)
        #^^^^^^^
        #  esto

    def __str__(self):
        return('{}'.format(catalogo))
                          #^^^^^^^^
                          # y ésto
catalogo=Catalogue()
#^^^^^^^^
# son ésto

ésta es la razón de que termines con una recursión infinita:

  • print(producto) llama a producto.__str__.
  • Cuándo se evalúa return('{}'.format(catalogo)) str.format llama a producto.__str__ de nuevo.
  • Y así hasta el infinito y más allá, bueno hasta que llegas a 1000 llamadas recursivas que es el límite por defecto que tiene Python para proteger al propio intérprete de un desbordamiento de pila.

En el método add_product realmente no haces nada

Sí haces realmente, pero no hay efectos fuera del mismo. Aunque catalogo es una variable global como se ha explicado, al hacer catalogo = [] (reasignación) se crea automáticamente una variable local al método que solapa la global, luego añades el producto a la lista, pero como buena variable local, en cuanto el método retorna el GC manda a la lista a mejor vida. Esa lista jamás podrá ser accedida desde fuera del método.

En definitiva, debes usar un atributo de instancia y no de clase, el código podría quedar así, con anotaciones de tipo incluidas:

from typing import List, Union


class CatalogueException(Exception):
    pass


class StockException(Exception):
    pass


class Product:
    def __init__(self, code: str, name: str, price: Union[float, int]):
        self._code: str = code
        self._name: str = name
        self._price: Union[float, int] = price

    def __hash__(self) -> int:
        return hash(self._code)

    def __str__(self) -> str:
        return f'Code: {self._code}\nName: {self._name}\nPrice: {self._price}'


class Catalogue:

    def __init__(self) -> None:
        self._products: List[Product] = []

    def add_product(self, product: Product) -> None:
        self._products.append(product)

    def remove_product(self, product: Product) -> None:
        self._products.remove(product)

    def __str__(self) -> str:
        prods = "\n\n".join(str(prod) for prod in self._products)
        return f"{'-' * 34} CATÁLOGO {'-' * 34}\n{prods}\n{'-' * 78}"


if __name__ == "__main__":
    catalogo = Catalogue()
    new_spanner = Product("S78", "Adjustable spanner", 35.99)
    new_hammer = Product("A45", 'Bush hammer', 15.5)
    catalogo.add_product(new_spanner)
    catalogo.add_product(new_hammer)
    print(catalogo)

Es recomendable que no uses catalogo como nombre del atributo de la clase Catalogo, es confuso. products es un mejor nombre para la lista.

El uso de _nombre es una convención para marcar atributos/métodos "privados". Es correcto su uso, pero no tiene sentido en el nombre de los argumentos en este caso.

FJSevilla
  • 55,603
  • 7
  • 35
  • 58
0

Has llegado al limite de la recursividad por eso te lanza ese error. Para solventarlo puedes hacer esto:

import sys
sys.setrecursionlimit(30000) # 10000 es un ejemplo puedes variar el numero incrementando o decrementando

Pero es mas recomendable que optimices el código ya que esto puede afectar a la velocidad del mismo

Enrique Asensio
  • 486
  • 2
  • 9