Andy Perez
Andy Perez

Reputation: 327

Looping through a list of class instances

I'm trying to write a Python script that will read a series of loads input through a CSV file, and return the resulting force vector. The format of the CSV is:

x,y,z,load_in_x,load_in_y,load_in_z
u,v,w,load_in_x,load_in_y,load_in_z
...

Here is the script:

# Fastener class containing an ID, location in space, and load vector
class Fastener(object):
    """A collection of fasteners"""
    noOfFstnrs = 0
    def __init__(self,xyz,load):
        self.xyz = xyz
        self.load = load
        Fastener.noOfFstnrs += 1
        self.fastID = Fastener.noOfFstnrs

    def __repr__(self):
        return """Fastener ID: %s
Location: %s
Load: %s""" % (self.fastID, self.xyz, self.load)


# Request the mapping CSV file from the user. The format should be:
# x,y,z,load_in_x,load_in_y,load_in_z
path = input("Fastener mapping file: ")

with open(path,"r") as inputFile:
    inputLines = inputFile.readlines()

# Create a list of Fastener objects from each line in the mapping file
F = []
for line in range(len(inputLines)):
    inputLines[line] = inputLines[line].strip('\n')
    inputLines[line] = inputLines[line].split(",")
    location = [float(i) for i in inputLines[line][:3]]
    load = [float(i) for i in inputLines[line][3:]]
    F.append(Fastener(location,load))

# Function to sum all fastener loads and return the resulting linear force
# vector
def sumLin(fastenerList):
    xSum = 0
    ySum = 0
    zSum = 0
    for i in fastenerList:
        xSum += fastenerList[i].load[1]
        ySum += fastenerList[i].load[2]
        zSum += fastenerList[i].load[3]
    linVector = [xSum, ySum, zSum]
    return linVector

# Print 
print(sumLin(F))

When I run it, I keep getting the following error:

Traceback (most recent call last):
  File "bolt_group.py", line 49, in <module>
    print(sumLin(F))
  File "bolt_group.py", line 42, in sumLin
    xSum += fastenerList[i].load[1]
TypeError: list indices must be integers, not Fastener

I've tried changing the looping index i to int(i) and it still gives me problems. If I manually add them like below there are no problems.

xSum = F[1].load[1] + F[2].load[1] + F[3].load[1]

Upvotes: 3

Views: 3570

Answers (3)

jochen
jochen

Reputation: 403

I think a more pythonic way is iterating the list of fasteners:

for fastener in fastenerList:
    xSum += fastener.load[0]
    ySum += fastener.load[1]
    zSum += fastener.load[2]

If you want your code to be really fast, you can load the csv data into a numpy.ndarray and let numpy do the summing (avoiding multiple for-loops in python) but you'll be fine with this approach if speed is not that important.

Upvotes: 2

PM 2Ring
PM 2Ring

Reputation: 55469

There are various improvements that can be made to your code. As jochen said, it's more Pythonic to directly iterate over lists rather than using an index. That principle can also be applied to the code that populates your list of Fastener objects.

with open(path,"r") as inputFile:
    # Create a list of Fastener objects from each line in the mapping file
    F = []
    for line in inputLines:
        data = [float(i) for i in line.strip().split(",")]
        location = data[:3]
        load = data[3:]
        F.append(Fastener(location, load))

# Function to sum all fastener loads and return the resulting linear force
# vector
def sumLin(fastenerList):
    xSum = 0
    ySum = 0
    zSum = 0
    for fastener in fastenerList:
        xSum += fastener.load[0]
        ySum += fastener.load[1]
        zSum += fastener.load[2]
    linVector = [xSum, ySum, zSum]
    return linVector

However, we can use the built-in zip function to condense the sumLin function even further:

def sumLin(fastenerList):
    loads = [fastener.load for fastener in fastenerList]
    return [sum(t) for t in zip(*loads)]

Obviously, my code is untested, since you didn't supply us with any sample data...

Upvotes: 2

Mohammed Aouf Zouag
Mohammed Aouf Zouag

Reputation: 17132

fastenerList[i].load[1]

i is a Fastener object, not an integer. So calling fastenerList[i] is invalid; you should be passing in an integer. Change:

for i in fastenerList: # 'i' is a 'Fastener' object

to

for i in range(len(fastenerList)): # 'i' is an integer 

Upvotes: 4

Related Questions