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.