Can One List Comprehension Be Used for both a Method and Class?

Question:

I have a simple program that determines the lateral surface area and volume of a pyramid from preset values:

class RightSquarePyramid():
    def __init__(self, b=0, h=0):
        self.b = float(b)
        self.h = float(h)
        pass
    
    def slantHeight(self):
        self.l = sqrt((self.h ** 2 + (self.b / 2) ** 2))
        return self.l
    
    def lateralSurfaceArea(self):
        return 2 * self.b * self.l
    
    def volume(self):
        return 1/3 * self.b ** 2 * self.h

myList = [[2.5, 5.0], [4.3, 2.5], [8, 3]]

tri0, tri1, tri2 = [RightSquarePyramid(*x) for x in myList]
tri0.slantHeight()
tri1.slantHeight()
tri2.slantHeight()
print(round(tri0.lateralSurfaceArea(), 3), round(tri1.lateralSurfaceArea(), 3), round(tri2.lateralSurfaceArea(), 3))
print(round(tri0.volume(), 3), round(tri1.volume(), 3), round(tri2.volume(), 3))

This happens because we only have the height and length of the base, so we need to calculate the slant height first.

I am currently using this solution:

tri0, tri1, tri2 = [RightSquarePyramid(*x) for x in myList]
triForce = tri0, tri1, tri2
[x.slantHeight() for x in triForce]
# tri0.slantHeight()
# tri1.slantHeight()
# tri2.slantHeight()
print(round(tri0.lateralSurfaceArea(), 3), round(tri1.lateralSurfaceArea(), 3), round(tri2.lateralSurfaceArea(), 3))
print(round(tri0.volume(), 3), round(tri1.volume(), 3), round(tri2.volume(), 3))

But this essentially replaces 3 lines of code with 2.

Is there a way to use list comprehension for both RightSquarePyramid() and slantHeight()?
Maybe a common variable that I am just overlooking that could do it?

Asked By: xQT314

||

Answers:

You can use loops in all the cases where you’re currently enumerating all three tri variables. There’s no reason to destructure the list into those three variables in the first place.

The idiomatic way of writing a loop where you want to execute code for a side effect rather than doing something with its return value is to put it in a for loop rather than a list comprehension (the list comprehension is just going to give you a list of values that you don’t want, probably Nones).

For the print cases, you can use a generator expression and the * operator to pass the results of the iteration as arguments to the function.

triForce = [RightSquarePyramid(*x) for x in myList]
for t in triForce:
    t.slantHeight()

print(*(round(t.lateralSurfaceArea(), 3) for t in triForce))
print(*(round(t.volume(), 3) for t in triForce))

Since you’re doing the exact same print routine with two different methods of the class, you could even put those in another loop:

for f in (
    RightSquarePyramid.lateralSurfaceArea,
    RightSquarePyramid.volume
):
    print(*(round(f(t), 3) for t in triForce)))
Answered By: Samwise

Thank you to @Samwise and @Kaya3 for their inputs, I believe I solved the problem, here is the code below:

from math import sqrt


class RightSquarePyramid():
    def __init__(self, b=0, h=0):
        self.b = float(b)
        self.h = float(h)
        pass
    
    def slantHeight(self):
        return sqrt((self.h ** 2 + (self.b / 2) ** 2))
         
    
    def lateralSurfaceArea(self):
        self.l = self.slantHeight()
        return 2 * self.b * self.l
    
    def volume(self):
        return 1/3 * self.b ** 2 * self.h
    
myList = [[2.5, 5.0], [4.3, 2.5], [8, 3]]

triForce = [RightSquarePyramid(*x) for x in myList]
for f in (
    RightSquarePyramid.lateralSurfaceArea,
    RightSquarePyramid.volume
):
    print(*(round(f(t), 3) for t  in triForce))

Answered By: xQT314
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.