473,394 Members | 1,831 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,394 software developers and data experts.

450 Pound Library Program

mwt
So in a further attempt to learn some Python, I've taken the little
Library program
(http://groups.google.com/group/comp....a9ccf1bc136f84)
I wrote and added several features to it. Readers now quit when they've
read all the books in the Library. Books know how many times they've
been read. Best of all, you can now create your own list of books to
read!

Again, the point of all this is to get used to programming in Python.
So although the program is trivial, any feedback on style, structure,
etc. would be much appreciated. I'm a convert from Java, so I've
probably got some unconscious Javanese in there somewhere. Help me get
rid of it!

Here's the new, improved program:
Expand|Select|Wrap|Line Numbers
  1. #!/usr/bin/python
  2. # Filename: Library.py
  3. # author: mwt
  4. # Feb, 2006
  5.  
  6. import thread
  7. import time
  8. import threading
  9. import random
  10.  
  11.  
  12.  
  13. class Library2:
  14. def __init__(self, listOfBooks, totalBooks):
  15. self.stacks = listOfBooks
  16. self.cv = threading.Condition()
  17. self.totalBooks = totalBooks
  18.  
  19. def checkOutBook(self, readerName):
  20. "'Remove book from the front of the list, block if no books are
  21. available'"
  22. self.cv.acquire()
  23. while len(self.stacks) == 0:
  24. self.cv.wait()
  25. print "%s waiting for a book..." %readerName
  26. book = self.stacks.pop(0)
  27. self.cv.release()
  28. return book
  29.  
  30. def returnBook(self, returnedBook):
  31. "'put book at the end of the list, notify that a book is
  32. available'"
  33. returnedBook.wasRead()
  34. self.cv.acquire()
  35. self.stacks.append(returnedBook)
  36. self.cv.notify()
  37. self.cv.release()
  38.  
  39. class Reader(threading.Thread):
  40.  
  41. def __init__(self, library, name, readingSpeed, timeBetweenBooks):
  42. threading.Thread.__init__(self)
  43. self.library = library
  44. self.name = name
  45. self.readingSpeed = readingSpeed
  46. self.timeBetweenBooks = timeBetweenBooks
  47. self.book = ""
  48. self.numberOfBooksRead = 0
  49.  
  50. def run(self):
  51. "'Keep checking out and reading books until you've read all in
  52. the Library'"
  53. while  self.numberOfBooksRead < self.library.totalBooks:
  54. self.book = self.library.checkOutBook(self.name)
  55. print "%s reading %s" %(self.name, self.book.title),
  56. time.sleep(self.readingSpeed)
  57. self.numberOfBooksRead += 1
  58. self.library.returnBook(self.book)
  59. print "%s done reading %s" %(self.name, self.book.title),
  60. print"Number of books %s has read: %d" %(self.name,
  61. self.numberOfBooksRead)
  62. self.bookName = ""
  63. time.sleep(self.timeBetweenBooks)
  64. print "%s done reading." %self.name
  65.  
  66. class Book:
  67. def __init__(self, author, title):
  68. self.author = author
  69. self.title = title
  70. self.numberOfTimesRead = 0
  71. #print "%s,%s" % (self.author, self.title),#print as books are
  72. loaded in
  73.  
  74. def wasRead(self):
  75. self.numberOfTimesRead += 1
  76. print "Number of times %s has been read: %d" %(self.title,
  77. self.numberOfTimesRead)
  78.  
  79. if __name__=="__main__":
  80.  
  81. print "\nWELCOME TO THE THURMOND STREET PUBLIC LIBRARY"
  82. print "Checking which books are avialable...\n"
  83. try:
  84. theBookFile = open("books.txt", "r")#Create your own list of
  85. books!
  86. stacks = []#a place to put the books
  87. for line in theBookFile.readlines():
  88. L = line.split (",")  # a comma-delimited list
  89. author = L[0]
  90. bookName =  L[1]
  91. newBook = Book(author, bookName)
  92. stacks.append(newBook)
  93. theBookFile.close()
  94. except IOError:
  95. print "File not found!"
  96. #string = "How many books would you like in the Library?[1-" +
  97. str(len(stacks)) + "]"
  98. totalBooks = input("How many books would you like in the
  99. Library?[1-" + str(len(stacks)) + "]")
  100. stacks[totalBooks: len(stacks)] = []
  101. print "Number of books in the Library is:", len(stacks)
  102. library = Library2(stacks, totalBooks)
  103. readers = input("\nHow many readers would you like?")
  104. print "Number of readers is:", readers, "\n"
  105. for i in range(0,readers):
  106. newReader = Reader(library, "Reader" + str (i),
  107. random.randint(1,7), random.randint(1,7))
  108. newReader.start()
  109.  
And here's a handy text file of books for you, so you don't have to
make your own:
Expand|Select|Wrap|Line Numbers
  1. Conrad, Heart of Darkness
  2. Kafka, Die Verwandlung
  3. Hemingway, For Whom the Bell Tolls
  4. James Joyce, Dubliners
  5. Moliere, Cyrano de Bergerac
  6. William Golding, Lord of the Flies
  7. Dostoevski, Crime and Punishment
  8. Cervantes, Don Quixote
  9. Camus, L'Etranger
  10. Tolstoy, War and Peace
  11. Poe, Tales
  12. Faulkner, The Sound and the Fury
  13. Orwell, 1984
  14. Fitzgerald, The Great Gatsby
  15. Steinbeck, The Grapes of Wrath
  16. Huxley, Brave New World
  17. Twain, The Adventures of Huckleberry Finn
  18. Mann, Der Tod in Venedig
  19. Kesey, Sometimes a Great Notion
  20. Pynchon, Gravity's Rainbow
  21. McEwan, The Cement Garden
  22. Mįrquez, Cien Ańos de Soledad
  23. Salinger, The Catcher in the Rye
  24. Miltion, Paradise Lost
  25. Chapman et al , The Pythons
  26.  
Feb 7 '06 #1
10 1779
just a few style notes...
def checkOutBook(self, readerName):
"'Remove book from the front of the list, block if no books are
available'"
I don't understand what "' is supposed to imply. If you
meant to use triple quoting, you need to use ''' or """.
Then the string can contain line breaks. (Perhaps you have
a really stupid email client/news client/editor/whatever that
replaces ''' with "'? Please loose that or use """.)

Also, please use lines that are short enough not to wrap
around. Line wrapping makes the code very ugly. Do like this:

def checkOutBook(self, readerName):
"""Remove book from the front of the list, block if no
books are available."""

[...] for line in theBookFile.readlines():
In modern Python you simply write:
for line in theBookFile:
L = line.split (",") # a comma-delimited list
author = L[0]
bookName = L[1]
Why bother with L? The follwing is as clear I think, and solves
the problem of commas in the title. Also, don't put a space between
the callable and the parenthesis please. See the Python style guide,
PEP 008.

author, bookName = line.split(",", 2)

[...] totalBooks = input("How many books would you like in the
Library?[1-" + str(len(stacks)) + "]")


Again, don't make the lines so long. You don't have to do that.
You can break lines freely inside (), {} and [], and adjacent
string literals are automatically concatenated.

totalBooks = input("How many books would you like in "
"the Library?[1-%d]" % len(stacks))
Feb 8 '06 #2
mwt wrote:
So in a further attempt to learn some Python, I've taken the little
Library program
(http://groups.google.com/group/comp....a9ccf1bc136f84)
I wrote and added several features to it. Readers now quit when they've
read all the books in the Library. Books know how many times they've
been read. Best of all, you can now create your own list of books to
read!

Again, the point of all this is to get used to programming in Python.
So although the program is trivial, any feedback on style, structure,
etc. would be much appreciated. I'm a convert from Java,
Welcome !-)
so I've
probably got some unconscious Javanese in there somewhere. Help me get
rid of it!
Well, apart from namingConventions (vs naming_conventions), I did not
spot too much javaism in your code.
Here's the new, improved program:
[code]
#!/usr/bin/python
# Filename: Library.py
# author: mwt
# Feb, 2006

import thread
import time
import threading
import random

class Library2:
Old-style classes are deprecated, use new-style classes instead:
class Library2(object):
def __init__(self, listOfBooks, totalBooks):
self.stacks = listOfBooks
If its a collection of books, why not call it 'books' ?
self.cv = threading.Condition()
self.totalBooks = totalBooks
What is 'totalBooks' ?
def checkOutBook(self, readerName):
"'Remove book from the front of the list, block if no books are
available'"
Why not using triple-quoted strings ?
self.cv.acquire()
while len(self.stacks) == 0:
self.cv.wait()
print "%s waiting for a book..." %readerName
book = self.stacks.pop(0)
This last line will crash (IndexError) on an empty list, and then the
resource may not be released... A first step would be to enclose this in
a try/finally block.
self.cv.release()
return book

def returnBook(self, returnedBook):
"'put book at the end of the list, notify that a book is
available'"
returnedBook.wasRead()
self.cv.acquire()
self.stacks.append(returnedBook)
self.cv.notify()
self.cv.release()
You have a recurring pattern in the last 2 methods: aquire/do
something/release. You could factor this out in a method decorator
(don't forget that functions and methods are objects too, so you can do
a *lot* of things with them).
class Reader(threading.Thread):

def __init__(self, library, name, readingSpeed, timeBetweenBooks):
threading.Thread.__init__(self) or :
super(Reader, self).__init__()

but this wouldn't make a big difference here
self.library = library
self.name = name
self.readingSpeed = readingSpeed
self.timeBetweenBooks = timeBetweenBooks
self.book = ""
You later user Reader.book to hold a reference to a Book object, so it
would be wiser to initialize it with None.
self.numberOfBooksRead = 0

def run(self):
"'Keep checking out and reading books until you've read all in
the Library'"
while self.numberOfBooksRead < self.library.totalBooks:
self.book = self.library.checkOutBook(self.name)
print "%s reading %s" %(self.name, self.book.title),
time.sleep(self.readingSpeed)
self.numberOfBooksRead += 1
self.library.returnBook(self.book)
print "%s done reading %s" %(self.name, self.book.title),
print"Number of books %s has read: %d" %(self.name,
self.numberOfBooksRead)
self.bookName = ""
time.sleep(self.timeBetweenBooks)
print "%s done reading." %self.name

class Book:
def __init__(self, author, title):
self.author = author
self.title = title
self.numberOfTimesRead = 0
#print "%s,%s" % (self.author, self.title),#print as books are
loaded in

def wasRead(self):
self.numberOfTimesRead += 1
print "Number of times %s has been read: %d" %(self.title,
self.numberOfTimesRead)

if __name__=="__main__":

You should define a main() function and call it from here.
print "\nWELCOME TO THE THURMOND STREET PUBLIC LIBRARY"
print "Checking which books are avialable...\n" s/avialable/available/ !-)
try:
theBookFile = open("books.txt", "r")#Create your own list of
books!
Filenames should not be hardcoded.

(Ok, you probably know this already, but I thought it would be better to
point this out for newbie programmers)
stacks = []#a place to put the books
for line in theBookFile.readlines():
L = line.split (",") # a comma-delimited list
Mmm... may not be the best format. What if there are commas in the book
title ? Hint : there's a CSV module in the standard lib.

Also, by convention, UPPERCASE identifiers are considered as CONSTANTS
author = L[0]
bookName = L[1]
newBook = Book(author, bookName)
stacks.append(newBook)
You can smash all this in a single line:
stacks = [Book(line.split(",", 1) for line in theBookFile]
theBookFile.close()
except IOError:
print "File not found!"
An IOError can result from a lot of different reasons (like user not
having read access on the file). So don't assume the file was not found:

except IOError, e:
print "Could not open file %s : %s" % ('books.txt', e)

You may also want to exit here...
#string = "How many books would you like in the Library?[1-" +
str(len(stacks)) + "]"
totalBooks = input("How many books would you like in the
Library?[1-" + str(len(stacks)) + "]")
1/ use raw_input() and *validate* user data

2/ use string formatting:
prompt = "How many books would you like in the "
" Library? [1-%d]" % len(stacks)
stacks[totalBooks: len(stacks)] = [] stacks = stacks[:totalBooks]

May be less efficient, but much more readable IMHO.

Also, the naming is somewhat misleading. Something along the line of
numberOfBooks or booksCount would probably be better.
print "Number of books in the Library is:", len(stacks)
You've called len(stacks) 3 times in 3 lines.
library = Library2(stacks, totalBooks)
What's the use of totalBooks here ? The Library2 class can figure it out
by itself, so it's useless - and it goes against DRY and SPOT.

hint: given the use of Library.totalBook, you'd better make it a
computed attribute

class LibraryN(object):
booksCount = property(fget=lambda self: len(self.books))
readers = input("\nHow many readers would you like?")
idem. Also, something like readersCount would be less misleading (I
first thought it was a collection of Reader objects)
print "Number of readers is:", readers, "\n"
for i in range(0,readers):
newReader = Reader(library, "Reader" + str (i),
random.randint(1,7), random.randint(1,7))
performance hint: names are first looked up in the local namespace:

randint = random.randint
for i in range(0, readers):
newReader = Reader(library,
"Reader%d" % i,
randint(1,7),
randint(1,7))
newReader.start()


My 2 cents...

(snip)

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Feb 8 '06 #3
Magnus Lycka wrote:
just a few style notes...
(snip)
Why bother with L? The follwing is as clear I think, and solves
the problem of commas in the title. Also, don't put a space between
the callable and the parenthesis please. See the Python style guide,
PEP 008.

author, bookName = line.split(",", 2)
"toto, tata, tutu".split(",", 2) ['toto', ' tata', ' tutu']


You want line.split(',', 1) here.

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Feb 8 '06 #4
mwt <mi*********@gmail.com> wrote:
while len(self.stacks) == 0:


To (kind of) repeat myself, the idiomatic Python would be:

while not self.stacks:

An empty list is considered to be false, hence testing the list
itself is the same as testing len(l) > 0 .

As someone else has noticed, you're using len() an awful lot
when you don't need to.

--
\S -- si***@chiark.greenend.org.uk -- http://www.chaos.org.uk/~sion/
___ | "Frankly I have no feelings towards penguins one way or the other"
\X/ | -- Arthur C. Clarke
her nu becomež se bera eadward ofdun hlęddre heafdes bęce bump bump bump
Feb 8 '06 #5
Ok,

I give up. DRY = Don't Repeat Yourself (google Pragmatic Programmers)
but SPOT? Google is little help here, SPOT is too common a word.

Thanks!

Feb 8 '06 #6
pl****@alumni.caltech.edu wrote:
Ok,

I give up. DRY = Don't Repeat Yourself (google Pragmatic Programmers)
but SPOT? Google is little help here, SPOT is too common a word.


The only SPOT I worked with (as I know of) was SPOT4
(Le Systeme Pour 'l Observation de la Terre) but that's
probably not it...

Single Point Of Truth? It seems some claim that DRY and
SPOT are the same things.
http://www.artima.com/cppsource/reducepnp3.html
Feb 8 '06 #7
pl****@alumni.caltech.edu wrote:
Ok,

I give up. DRY = Don't Repeat Yourself (google Pragmatic Programmers)
but SPOT? Google is little help here, SPOT is too common a word.


Single Point Of Truth (or Single Point Of Transformation)

And before you mention it, yes, it's *almost* the same thing as DRY !-)
--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Feb 8 '06 #8
Magnus Lycka wrote:
pl****@alumni.caltech.edu wrote:
Ok,

I give up. DRY = Don't Repeat Yourself (google Pragmatic Programmers)
but SPOT? Google is little help here, SPOT is too common a word.

The only SPOT I worked with (as I know of) was SPOT4
(Le Systeme Pour 'l Observation de la Terre) but that's
probably not it...


Probably not !-)
Single Point Of Truth?
And the winner is... <your-name-here />
It seems some claim that DRY and
SPOT are the same things.
http://www.artima.com/cppsource/reducepnp3.html


Well, they are at least very closely related !-)

And I of course forgot OnceAndOnlyOnce...

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Feb 8 '06 #9
mwt
A million thanks for in-depth critique. I look forward to figuring out
half of what you're talking about ;)

Feb 9 '06 #10
mwt
BTW - I can't find any code examples of how to interrupt a beast like
this. A regular ctrl-c doesn't work, since the separate threads just
keep executing. Is this a case where you need to iterate through all
threads and stop each one individually? How would that look?

Feb 9 '06 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Robert Zierhofer | last post by:
hi there, it seems as if i can“t convert the euro and pound sign to their html equivalents. i tried eregi_replace("€", "&euro;", $haystack); eregi_replace("£", "&pound;", $haystack); as...
14
by: Vic Russell | last post by:
Hi, I'm trying to get the British pound (£) symbol through LibXML and just get "£" instead. Any ideas? Vic
4
by: ben | last post by:
Hi all, I have a simple PHP page that takes values from a form and puts them in a database (MySQL). The code is in a file test.php, which I have typed in at the bottom of this post (please...
5
by: Waldy | last post by:
Hi there, I am using the .Net XML Serialization classes to create XML strings. This has been working fine up until the point that one of the strings contained a pound sterling symbol. The...
2
by: junk | last post by:
Hi, Sorry if this has been asked before, and apologise if this is the wrong NG. I am using PHP 5.0.5 and Apache 2.0.54 in a Win2k environment. Lately I have been playng with RSS feeds. I...
3
by: quat | last post by:
Hi, I have a char* string where I assigned a string literal with the British pound symbol, which is included in the extended ASCII, at least on my OS. However, when I cout the string, I get a...
4
by: monomaniac21 | last post by:
hi im outputting text from a db and where a £ sign has been entered a ? character appears instead which i cant seem to get rid of with a simple str_replace. is their a way to get rid of it? and...
5
by: ramaswamynanda | last post by:
Hello, I have a currency field on my form. The default formats for this field are dollar, euro. There is no pound symbol.....How do i put in a currency format, having the pound sign. Any help...
0
by: RobR2009 | last post by:
I am having trouble with a C# proxy page I am writing which allows me to do cross domain AJAX calls with Javascript. The problem is with certain pages that contain pound signs £ that are not HTML...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.