2

Quisiera entender por que cuando ejecuto mi codigo me arroja la siguiente salida en consola:

8

9

20

None

Estuve haciendo debugg pero no encuentro la razón de porque imprime None arroja None al terminar de recorrer el for dentro de la función area() en la clase pruebarectangulo.

class Rectangulo:
        def __init__(self, base, altura):
            self.base = base
            self.altura = altura
    
    
    
    class pruebaRectangulo:
        rectangulos = []
    
        def __init__(self, rectangulos=[]):
          self.rectangulos = rectangulos
    
        def agregar(self, r):
           self.rectangulos.append(r)
    
        def area(self):
            for r in self.rectangulos:
                salida = r.base*r.altura
                if(salida == None):
                    break
                print(salida)
    
    r = Rectangulo(2, 4)
    r2 = pruebaRectangulo([r])
    r2.agregar(Rectangulo(3, 3))
    r2.agregar(Rectangulo(4, 5))
    print(r2.area())

1 Answers1

1

None viene de la línea print(r2.area()). Imprimes la salida del método de instancia area, pero dicho método retorna None siempre. No tiene ningún estamento return explícito. Cuando se ejecuta un return de forma explícita todo objeto llamable en Python retorna None por defecto.

Si quieres que retorne el área de cada retángulo (que es más apropiado que imprimir desde el método) debes usar return en vez de print y meterlos en una lista o usar un generador:

def areas(self):
    areas = []
    for r in self.rectangulos:
        salida = r.base * r.altura
        reas.append(salida)
    return salida

Si quieres imprimir desde el método, entonces:

def areas(self):
    for r in self.rectangulos:
        salida = r.base * r.altura
        print(salida)


r = Rectangulo(2, 4)
r2 = pruebaRectangulo([r])
r2.agregar(Rectangulo(3, 3))
r2.agregar(Rectangulo(4, 5))
r2.area() # <<<<<<<<<<<<<<<<<<<<<<<<<< sin print

No obstante una estructura más lógica sería que el cálculo del área se haga en la propia clase Triangulo:

class Rectangulo:
    def __init__(self, base, altura):
        self.base = base
        self.altura = altura
        
    def area(self):
        return self.base * self.altura    
    

class Rectangulos:
    def __init__(self, rectangulos=None):
      self.rectangulos = rectangulos if rectangulos is not None else []

    def agregar(self, rectangulo):
       self.rectangulos.append(rectangulo)

    def areas(self):
        return [rect.area() for rect in self.rectangulos]


if __name__ == "__main__":
    r = Rectangulo(2, 4)
    r2 = Rectangulos([r])
    r2.agregar(Rectangulo(3, 3))
    r2.agregar(Rectangulo(4, 5))
    print(r2.areas())

o si quieres imprimir las áreas una debajo de otra:

if __name__ == "__main__":
    r = Rectangulo(2, 4)
    r2 = Rectangulos([r])
    r2.agregar(Rectangulo(3, 3))
    r2.agregar(Rectangulo(4, 5))
    for area in r2.areas():
        print(area)

Existen más alternativas, desde retornar un generador en vez de una lista a usar propiedades.

Tres observaciones:

  • No se debe hacer var == None, None es un singleton, lo correcto es usar el operador de identidad is con él:

    var is None 
    var is not None
    
  • Mucho cuidado con:

    def __init__(self, rectangulos=[]):
                       ^^^^^^^^^^^^^^
    

    Los argumentos por defecto se evalúan en tiempo de definición, todos los objetos de la clase que crees compartirán la misma lista por defecto, como una lista es un objeto mutable, ésto no es trivial:

    class Foo():
        def __init__(self, bar=[]):
            self.bar = bar
    
    >>> a = Foo()
    >>> b = Foo()
    >>> a.bar.append(4)
    >>> a.bar
    [4]
    >>> b.bar
    [4]
    >>> b.bar.append(13)
    >>> a.bar
    [4, 13]
    >>> c = Foo()
    >>> c.bar
    [4, 13]
    

    En cambio, una forma correcta de inicializar para evitar ésto podía ser:

    class Foo():
        def __init__(self, bar=None):
            self.bar = bar if bar is not None else []
    
    >>> a = Foo()
    >>> b = Foo()
    >>> a.bar.append(4)
    >>> b.bar
    []
    >>> a.bar
    [4]
    
  • No se si en tu segunda clase verdaderamente quieres que rectangulos sea un atributo de clase y no de instancia, porque lo defines como tal. Si es así bien, pero si no ten en cuenta:


    Si quieres que sea un atributo de case, la reasignación el el incializador crea un atributo de instancia que ensombrece al de clase, debería ser:

    class Rectangulos:
        rectangulos = []
    
        def __init__(self, rectangulos=None):
            if rectangulos is not None:
                type(self).rectangulos = rectangulos
    

    pero recuerda que todas las instancias de la clase comparten el mismo objeto y no creo que busques eso.

FJSevilla
  • 55,603
  • 7
  • 35
  • 58