[Tutor] foreign lists LONG

Charlie Clark Charlie Clark <charlie@begeistert.org>
Sat, 04 Aug 2001 20:03:52 +0200


>sorry about that confusion.  i'll try to explain better.  i want to have a
>list that includes book names with book attributes (for example, the book
>name might be "Catcher in the Rye" and the book attributes might be
>"maturation" and "teenage").  i want to be able to have a list of about 50
>books with their attributes, and i want the list to be in the following
>form:
>
>[["Catcher in the Rye", "maturation", "teenage"], [book, attribute], ...]
>
>this way, the program i have written will access the first element of each
>element in the main list, which is the book title, and every other element
>in those smaller lists are attributes.  here's the code, maybe this will
>help:

<!-- code snipped -->

Looks fine. You just need to add a routine to read in your file to your 
booklists and to do this you need to change the way you write your records. 
If use 'split' and 'join' to make lists from strings and strings from lists 
you can reliably read what you've written

mm, I'm sure I like the way you're adding attributes. It looks prone to 
error. It might be easier to use a dictionary.

attributes = ['title', 'author', 'category'] # and so on. These will be our 
defaults as you currently haven't got the file.

You can then simplify managing your books by accessing via key rather than 
trusting for luck on accessing by index. If you use the first line of your 
file to store your the names of those attributes you're even better plus this 
might make it easier to move to a full blown database. This is going to mean 
rewriting some of your routines but I think you'll find it helpful.

Let's try this with two books just to get the principle right.
Our database is going to be semi-colon separated text-file, like this:

title;author;category
catcher in the rye;J D Salinger;maturity
gormenghast;Mervyn Peake;fantasy

if we just read the first line of our file we can set up our attributes:
try:
    file_in = open("books.txt", "rb")    # "rb" is necessary only in windows
    attributes = file_in.readline()
    file_in.close()    # close the file after use
except:        #file doesn't exist
    attributes = ['title', 'author', 'category']     # default attributes

attributes = attributes.split(";")  # will give us a list of attributes
booklist = []

let's read the rest of the file

books = f.readlines()

for record in books:
    record = record.split(";")
    book = {}
    for attribute in attributes:
        book[attribute] = record[attributes.index(attribute)]
    booklist.append(book)

booklist now looks like this:
[{'title': 'catcher in the rye', 'author': 'J D Salinger', 'category': 
'maturity'}, {'title': 'gormenghast', 'author': 'Mervyn Peake', 'category': 
'fantasy'}]

and it means you can do searches on it like this
Look for books with category == "maturity"...

for book in booklist:
    if book['category'] == 'maturity':
        print book.values()     # you might want to do some sorting or such

will give us
['catcher in the rye', 'J D Salinger', 'maturity']

Add a book simply by calling the input

new_book = {}
for attribute in attributes:
    new_book[attribute] = raw_input("%s "%(attribute))
booklist.append(new_book)

I think you should be able to rebuild the rest of your functions based on 
this method.

All that's left is storing our list when quitting. As we've decided to use a 
text file for storage, we're going to need to keep things in order but it 
doesn't matter which order I hope! Dictionaries rearrange their order to make 
themselves faster to use.

#storage
file_out = open("books.txt", "w")         # or whatever you're calling it
first_line = attributes.join(";")
file_out.write(first_line + "\n")

s = ""     # temporary string to store our records
for book in booklist:
    record = []
    for attribute in attributes:
        record.append(book[attribute])
    record = string.join(record, ";")    # turn a list into a string
    s += record + "\n"     separate each record by line

file_out.write(s)
file_out.close()       # always good practice to close files explicitly

Proof of the pudding will be in the eating... Let us know how you get on.

Using a text file is only one way of doing this and isn't a good idea for big 
catalogues. It might be quicker to use classes for your books and pickle the 
instances which would reduce the overhead at the beginning an end but don't 
ask me how this is done: I tried it once and failed miserably.

Charlie