how to make a list of tuples from a file in python

2019-08-11 06:48发布

问题:

Ok I have a file like that looks like this.

panite,1,1800
ruby,2,100
diamond,0.75,900
emerald,3,250
amethyst,2,50
opal,1,300
sapphire,0.5,750
benitoite,1,2000
malachite,1,60

Our teacher gave us code that uses a try/except to help us open the file. I need to open the file and read each line and make each line a tuple and then put it in a list. The list is supposed to be the last numbe divided by the middle number, and then that value followed by the name of the gem(the middle number is the carat of the gem). The problem i'm having is I cant even get it to make a list from the file. This is what i've tried to open it without much success.

def main():
    fileFound = False
    while not fileFound:
        fileName = input('File name containing jewel data: ')
        try:
            dataFile = open(fileName, "r")
            fileFound = True
            knapsack()
        except:
            print ('Could not find that file -- try again')

def knapsack():
    list = dataFile.readline()

I've actually had a little success when I changed it to a simple printstatement under def knapsack() where it will print something simple like 2+2, but when I try to make a list, it gives me the except error instead. This is my first programming class so any insight into this would be appreciated.

回答1:

def make_jewel(line):
    name, carats, price = line.split(",")
    return (float(price)/float(carats), name)

def main():
    while True:
        file_name = input('File name containing jewel data: ')
        try:
            with open(file_name) as inf:
                data = [make_jewel(line) for line in inf]
            break
        except FileNotFoundError:
            print('Could not find that file -- try again')

main()

and some comments:

  • except: without a specified exception-type, also known as a "bare exception", is frowned on because it catches everything. You should specify the type of exceptions you expect to see, and only handle those; if you catch everything and something totally unexpected fails (ie ComputerOnFireError !) you will never find out about it.

  • opening a file using with is preferred because it ensures the file will always get closed properly.

  • when you open a file in text mode, you can iterate over it line-by-line; this is the most common way to process a file.

  • when you .split() a string, you get a list of strings back. Before doing math on the pieces you have to convert them from a string to a numeric value using int() or float().

Hope that helps.



回答2:

Use the csv module to read lines as csv rows.

import csv

def knapsack(datafile):
    output_data = []
    csv_reader = csv.reader(datafile, delimiter=',')
    for row in csv_reader:
        output_data.append(tuple(row))
    return output_data

This will give you output_data as:

[('panite', '1', '1800'),
 ('ruby', '2', '100'),
 ('diamond', '0.75', '900'),
 ('emerald', '3', '250'),
 ('amethyst', '2', '50'),
 ('opal', '1', '300'),
 ('sapphire', '0.5', '750'),
 ('benitoite', '1', '2000'),
 ('malachite', '1', '60')]

which is a list of tuples. This solves your problem of making a list from the file. Now, you should do:

  • The numerals in the tuples are strings. You need to make sure you convert them to int before you do the arithmetic operations as you mention in your description.
  • Pass output_data as an argument to a separate function that does the arithmetic functions you mentioned in your question. This function should build your output list.

A few remarks on your code:

  • You define the file handle in the main function, but do not pass it to the knapsack function. But you reference it in the knapsack function which will not give you what you want. So you need to pass the datafile file handle as argument to your knapsack function. You do this by replacing this line in your main method from:

    knapsack()
    

    to

    knapsack(datafile)
    
  • list is the name of a built in type in Python. So it is wise not to use it as the name of a variable in your code.



回答3:

You should:

  • send dataFile to your knapsack function.
  • change from except to except IOError to avoid catching exceptions you do want to see.
  • close the file (consider using with to open the file to avoid having to close it explicitly

    try:
        dataFile = open(fileName, "r")
        fileFound = True
        knapsack(dataFile)
        dataFile.close()
    except IOError:
        print ('Could not find that file -- try again')
    

If you had used except IOError from the start of, you would have seen this error:

Traceback (most recent call last):
  ...
  ...
NameError: global name 'dataFile' is not defined

knapsack does not know what dataFile is, hence the error. Always catch specific exceptions when using try..except. If you don't know which error is thrown - reproduce it before you write the code in the python interpreter (e.g try to open a file that doesn't exists, and notice that IOError is thrown).

In knapsack you need to change from readline to readlines.

def knapsack(dataFile):
    list = dataFile.readlines()

You should also consider to use the csv module as mentioned in the other answer to handle the data.



回答4:

If I understand your question correctly, the exception is because dataFile variable is not found inside the knapsack function. I'd suggest learning scoping rules in Python or read this excellent to-the-point chapter on the topic (or search for this topic on the Web!).

The other thing I'd recommend is not to handle all exceptions as you've shown in your snippet. Here's why.

The fixed code could look something like this:

def main():
    fileFound = False
    while not fileFound:
        fileName = raw_input('File name containing jewel data: ')
        try:
            dataFile = open(fileName, "r")
            fileFound = True
            knapsack(dataFile)
        except IOError:
            print ('Could not find that file -- try again')

def knapsack(dataFile):
    list = dataFile.readline() ### Or you want `readlines()` ?
    print list ### Print to let you know that it works
    # return list ### ???

if __name__ == '__main__':
    main()


回答5:

I would go with something more Pythonic using list comprehensions

def knapsack(dataFile):
    with open(dataFile, 'r') as fp:
        lines = [line.strip() for line in fp]

    data = [tuple(line.split(',')) for line in lines]

    return data

Where you are passing to your knapsack function the path to your file.