Think Python/Answers

Chapter 1

Exercise 1.4

If you run a 10 kilometer race in 43 minutes 30 seconds, what is your average time per mile? What is your average speed in miles per hour? (Hint: there are 1.61 kilometers in a mile).

>>> 10/1.61 # Convert kilometers to miles
6.2111801242236018
>>> (43*60)+30 # Convert time to seconds
2610
>>> 2610/6.2111801242236018 # what is your average time (seconds) per mile
420.21000000000004
>>> 420.21000000000004/60 # what is your average time (minutes) per mile
7.0035000000000007
>>> 60/7.0035000000000007 # Miles per hour
8.5671449989291055

Comment: This is not valid, it ONLY works for 43min and 30 seconds to 10km's. Python should have a way to do this the proper way.

In order to do this the proper way, a person must do something like this.
43*60 -> convert the minutes to seconds.
2580+30 -> add the seconds
2610/10 -> divide by distance
261/60 -> change seconds into minutes
4.35 -> is the answer, now you must
.35*60 -> multiply the number after the decimal with 60
21 seconds..
End result = 4.21 minutes per KM, this technique works for all distances and times.

↑Jump back a section

Chapter 2

Exercise 2.1

If you type an integer with a leading zero, you might get a confusing error:

>>> zipcode = 02492
                  ^
SyntaxError: invalid token

Other number seem to work, but the results are bizarre:

>>> zipcode = 02132
>>> print zipcode
1114

So python is assuming you want to convert an octal number to a decimal number. In the base 8 numbering system where valid numbers are 0, 1, 2, 3, 4, 5, 6 and 7.

Base  8: 00 01 02 03 04 05 06 07 10 11 12 13 14 15 16 17 20 21 22 23 24
Base 10: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20

Every 8 numbers we increment the left hand columns. This means that the left most column is the number of 'ones'. The one to the right of that is a tally of the number of 'eights', the one next to that is a tally of a full column of 'eight' times the 'eight column' - 64. The one next to that is 64*8 - 512 and so on. For more information read [Base Eight math].

That is why zipcode = 02492 is invalid as the digit 9 is not a valid octal number. We can do the conversion manually as follows:

>>> print 02132
1114
>>> (2*512)+(1*64)+(3*8)+(2*1)
1114
>>> 

Exercise 2.4

The volume of a sphere with radius r is 4/3 π r3. What is the volume of a sphere with radius 5?

>>> pi = 3.1415926535897931
>>> r = 5
>>> 4/3*pi*r**3 # This is the wrong answer
392.69908169872411
>>> r = 5.0 # Radius can be a float here as well, but is not _necessary_.
>>> 4.0/3.0*pi*r**3 # Using floats give the correct answer
523.59877559829886
>>> 

Suppose the cover price of a book is $24.95, but bookstores get a 40% discount. Shipping costs $3 for the first copy and 75 cents for each additional copy. What is the total wholesale cost for 60 copies?

 $24.95  Cost
  $9.98  Discount per book
 $14.97  Cost per book after discount
  60     Total number of books
$898.20  Total cost not inc delivery

  $3.00  First book delivery
  59     Remaining books
  $0.75  Delivery cost for extra books
 $44.25  Total cost for extra books
 $47.25  Total Delivery cost
        
$945.45  Total Bill

This answer is wrong because 40.0/100.0 return wrong value 0.40000000000000002 for more info see IEEE 754 (Standard for Floating-Point Arithmetic)
>>> (24.95-24.95*40.0/100.0)*60+3+0.75*(60-1)
945.44999999999993
>>> 24.95*0.6*60+0.75*(60-1)+3
945.45

If I leave my house at 6:52 am and run 1 mile at an easy pace (8:15 per mile), then 3 miles at tempo (7:12 per mile) and 1 mile at easy pace again, what time do I get home for breakfast?

Answer: 7:30 am

How I did it:

>>> start = (6*60+52)*60
>>> easy = (8*60+15)*2
>>> fast = (7*60+12)*3
>>> finish_hour = (start + easy + fast)/(60*60.0)
>>> finish_floored = (start + easy + fast)/(60*60)  #int() function can also be used to get integer value, but isn't taught yet.
>>> finish_minute  = (finish_hour - finish_floored)*60
>>> print 'Finish time was %d:%d' % (finish_hour,finish_minute)
Finish time was 7:30
>>> 
↑Jump back a section

Chapter 3

Exercise 3.3

Python provides a built-in function called len that returns the length of a string, so the value of len('allen') is 5. Write a function named right_justify that takes a string named s as a parameter and prints the string with enough leading spaces so that the last letter of the string is in column 70 of the display.

>>> def right_justify(s):
...     print (' '*(70-len(s))+s)
... 
>>> right_justify('allen')
                                                                 allen
>>> 

Exercise 3.5

You can see my solution at [http://thinkpython.com/code/grid.py].

"""
Solution to Exercise X.X on page X of Think Python
Allen B. Downey
 
"""
 
# here is a mostly-straightforward solution to the
# two-by-two version of the grid.
 
def do_twice(f):
    f()
    f()
 
def do_four(f):
    do_twice(f)
    do_twice(f)
 
def print_beam():
    print '+ - - - -',
 
def print_post():
    print '|        ',
 
def print_beams():
    do_twice(print_beam)
    print '+'
 
def print_posts():
    do_twice(print_post)
    print '|'
 
def print_row():
    print_beams()
    do_four(print_post)
 
def print_grid():
    do_twice(print_row)
    print_beams()
 
print_grid()
 
 
# here is a less-straightforward solution to the
# four-by-four grid
 
def one_four_one(f, g, h):
    f()
    do_four(g)
    h()
 
def print_plus():
    print '+',
 
def print_dash():
    print '-',
 
def print_bar():
    print '|',
 
def print_space():
    print ' ',
 
def print_end():
    print
 
def nothing():
    "do nothing"
 
def print1beam():
    one_four_one(nothing, print_dash, print_plus)
 
def print1post():
    one_four_one(nothing, print_space, print_bar)
 
def print4beams():
    one_four_one(print_plus, print1beam, print_end)
 
def print4posts():
    one_four_one(print_bar, print1post, print_end)
 
def print_row():
    one_four_one(nothing, print4posts, print4beams)
 
def print_grid():
    one_four_one(print4beams, print_row, nothing)
 
print_grid()
 
comment = """
After writing a draft of the 4x4 grid, I noticed that many of the
functions had the same structure: they would do something, do
something else four times, and then do something else once.
 
So I wrote one_four_one, which takes three functions as arguments; it
calls the first one once, then uses do_four to call the second one
four times, then calls the third.
 
Then I rewrote print1beam, print1post, print4beams, print4posts,
print_row and print_grid using one_four_one.
 
Programming is an exploratory process.  Writing a draft of a program
often gives you insight into the problem, which might lead you to
rewrite the code to reflect the structure of the solution.
 
--- Allen
"""
 
print comment
↑Jump back a section

Chapter 9

Exercise 9.1

fin = open('words.txt')
for line in fin:
        word = line.strip()
        if len(word) >= 20:
                print (word)
↑Jump back a section

Chapter 10

Exercise 10.1

Write a function that takes a list of numbers and returns the cumulative sum; that is, a new list where the ith element is the sum of the first i+1 elements from the original list. For example, the cumulative sum of [1, 2, 3] is [1, 3, 6].

def cumulative(a):
        cumulative = []
        sum = 0
        for i in a:
                sum += i
                cumulative.append(sum)
 
 
        return cumulative
a = [1, 2, 3]
print(cumulative(a))

Exercise 10.2

Write a function called chop that takes a list and modifies it, removing the first and last elements, and returns None.

>>> def chop(x):
        del x[:1]
        del x[-1:]

Then write a function called middle that takes a list and returns a new list that contains all but the first and last elements.

>>> def middle(x):
        res = []
        i = 1
        while i <= len(x)-2:
                res.append(x[i])
                i += 1
        return res

This can also be done simply with a slice.

>>> def middle(x):
        return x[1:-1]
↑Jump back a section

Chapter 11

Exercise 11.1

Write a function that reads the words in words.txt and stores them as keys in a dictionary. It doesn’t matter what the values are. Then you can use the in operator as a fast way to check whether a string is in the dictionary.

englishdictionary = dict()
fin = open('words.txt')
line = fin.readline()
def engdicdefine():
    index = 0
    while index <= 1000:
        word = line.strip()
        englishdictionary[word] = index
        index += 1
    return englishdictionary

Exercise 11.2

def histogram(s):
    d = dict()
    for c in s:
        d[c] = 1 + d.get(c, 0)
    return d

Exercise 11.3

Dictionaries have a method called keys that returns the keys of the dictionary, in no particular order, as a list. Modify print_hist to print the keys and their values in alphabetical order.

v = {'p' : 1, 'a' : 1, 'r' : 2, 'o' : 1, 't' : 1}
def print_hist(h):
    x = h.keys()
    y = list(x)
    y.sort()
    for c in y:
        print(c, h[c])

Exercise 11.4

def reverse_lookup(d,v):
    l = list()
    for c in d:
        if d[c] == v:
            l.append(c)
    return l
↑Jump back a section

Chapter 12

Exercise 12.1

numbers = (1,2,3)
def sumall(numbers):
    x = 0
    for i in numbers:
        x = x + i
    print x
sumall(numbers)

or

def sumall(*t):
    x = 0
    for i in range(len(t)):
        x += t[i]
    return x

or

def sumall(*args):
        t = list(args)
        return sum(t)

Exercise 12.2

import random
 
def sort_by_length(words):
        t = []
        for word in words:
                t.append((len(word),word))
        t.sort(reverse=True)
        res = []
        for length, word in t:
                res.append(word)
        i=0
        final = []
        while i <= len(res)-2:
                if len(res[i]) == len(res[i+1]):
                        y_list = [res[i], res[i+1]]
                        random.shuffle(y_list)
                        final = final + y_list
                        i += 2
                else:
                        final.append(res[i])
                        i += 1
        if i == len(res)-1:
                final.append(res[i])
        return final

or

from random import shuffle
 
def sort_by_length(words):
        r = []
        d = dict()
        for word in words:
                d.setdefault(len(word), []).append(word)
        for key in sorted(d, reverse=True):
                if len(d[key]) > 1:
                        shuffle(d[key])
                r.extend(d[key])
        return r

Exercise 12.3

import string
 
def most_frequent(s):
        d = dict()
        inv = dict()
        for char in s:
                if char in string.ascii_letters:
                        letter = char.lower()           
                        d[letter] = d.get(letter, 0) + 1
 
        for letter, freq in d.items():
                inv.setdefault(freq, []).append(letter)
 
        for freq in sorted(inv, reverse=True):
                print('{:.2%}:'.format(freq/(sum(list(inv)*len(inv[freq])))), ', '.join(inv[freq]))
↑Jump back a section

Chapter 13

Exercise 13.7

from string import punctuation, whitespace, digits
from random import randint
from bisect import bisect_left
 
def process_file(filename):
        h = dict()
        fp = open(filename)
        for line in fp:
                process_line(line, h)
        return h
 
def process_line(line, h):
        line = line.replace('-', ' ')
        for word in line.split():
                word = word.strip(punctuation + whitespace + digits)
                word = word.lower()
                if word != '':
                        h[word] = h.get(word, 0) + 1
 
hist = process_file('emma.txt')
 
def cum_sum(list_of_numbers):
        cum_list = []
        for i, elem in enumerate(list_of_numbers):
                if i == 0:
                        cum_list.append(elem)
                else:
                        cum_list.append(cum_list[i-1] + elem)
        return cum_list
 
def random_word(h):
        word_list = list(h.keys())
        num_list = []
        for word in word_list:
                num_list.append(h[word])
        cum_list = cum_sum(num_list)
        i = randint(1, cum_list[-1])
        pos = bisect_left(cum_list, i)
        return word_list[pos]
 
print(random_word(hist))
↑Jump back a section

Chapter 14

Exercise 14.3

import shelve
 
def dict_of_signatures_and_words(filename='words.txt'):
        d = dict()
        for line in open(filename):
                word = line.lower().strip()
                signature = ''.join(sorted(word))
                d.setdefault(signature, []).append(word)
        return d
 
def db_of_anagrams(filename='anagrams', d=dict_of_signatures_and_words()):
        db = shelve.open(filename)
        for key, values in d.items():
                if len(values)>1:
                        for index, value in enumerate(values):
                                db[value]=values[:index]+values[index+1:]
        db.close()
 
def print_contents_of_db(filename='anagrams'):
        db = shelve.open(filename, flag='r')
        for key in sorted(db):
                print(key.rjust(12), '\t<==>\t', ', '.join(db[key]))
        db.close()
 
db_of_anagrams()
print_contents_of_db()

Exercise 14.5

# Replace urllib.request with urllib if you use Python 2.
# I would love to see a more elegant solution for this exercise, possibly by someone who understands html.
 
import urllib.request
 
def check(zip_code):
        if zip_code == 'done':
                return break
 
        if len(zip_code) != 5:
                print('\nThe zip code must have five digits!')
                return continue
 
def get_html(zip_code):
        gibberish = urllib.request.urlopen('http://www.uszip.com/zip/' + zip_code)
        less_gib = gibberish.read().decode('utf-8')
        return less_gib
 
def extract_truth(code, key, delimiter):
        pos = code.find(key) + len(key)
        nearly_true = code[pos:pos+40]
        truth = nearly_true.split(delimiter)[0]
        return truth
 
while True:
        zip_code = input('Please type a zip code (5 digits) or "done" if want to stop:\n')
 
        check(zip_code)
 
        code = get_html(zip_code)
 
        invalid_key = '(0 results)'
        if invalid_key in code:
                print('\nNot a valid zip code.')
                continue
 
        name_key = 'zip code of <strong>'
        name_del = '<'
        name = extract_truth(code, name_key, name_del)
 
        pop_key = 'Population:</b></td><td>'
        pop_del = ' <'
        name = extract_truth(code, pop_key, pop_del)
 
        if not 1 < len(pop) < 9:
                pop = 'not available'
 
        print('\n' + name)
        print('Population:', pop, '\n')
↑Jump back a section

Chapter 15

Exercise 15.1

import math
 
class Point(object):
        """represents a point in 2-D space"""
 
def distance(p1, p2):
        distance = math.sqrt((p2.x - p1.x)**2 + (p2.y - p1.y)**2)
        return distance
 
p1 = Point()
p2 = Point()
 
p1.x = 3
p1.y = 2
p2.x = 4
p2.y = 3
 
print(distance(p1, p2))
↑Jump back a section

Chapter 16

Exercise 16.1

def print_time(t):
    print '%.2d:%.2d:%.2d' % (t.hour, t.minute, t.second)

or

# Solution for Python3
# More on string formatting: http://docs.python.org/py3k/library/string.html#formatspec
 
def print_time(t):
    # 0 is a fill character, 2 defines the width
    print('{}:{:02}:{:02}'.format(t.hour, t.minute, t.second))

Exercise 16.2

def is_after(t1, t2):
    return (t1.hour, t1.minute, t1.second) > (t2.hour, t2.minute, t2.second)

Exercise 16.3

# Comment not by the author: This will give a wrong result, if (time.second + seconds % 60) > 60
 
def increment(time, seconds):
 
    n = seconds/60
    time.second += seconds - 60.0*n
    time.minute += n
 
    m = time.minute/60
    time.minute -= m*60
    time.hour += m

or

# Solution for Python3
# Replace '//' by '/' for Python2
 
def increment(time, seconds):
        time.second += seconds
        time.minute += time.second//60
        time.hour += time.minute//60
 
        time.second %= 60
        time.minute %= 60
        time.hour %= 24

Exercise 16.4

# Solution for Python3
# Replace '//' by '/' for Python2
 
from copy import deepcopy
 
def increment(time, seconds):
        r = deepcopy(time)
 
        r.second += seconds
        r.minute += r.second//60
        r.hour += r.minute//60
 
        r.second %= 60
        r.minute %= 60
        r.hour %= 24
 
        return r

Exercise 16.5

class Time(object):
    """represents the time of day.
        attributes: hour, minute, second"""
 
time = Time()
time.hour = 11
time.minute = 59
time.second = 30
 
def time_to_int(time):
    minutes = time.hour * 60 + time.minute
    seconds = minutes * 60 + time.second
    return seconds
 
def int_to_time(seconds):
    time = Time()
    minutes, time.second = divmod(seconds, 60)
    time.hour, time.minute = divmod(minutes, 60)
    return time
 
def increment(time, addtime):
    seconds = time_to_int(time)
    return int_to_time(seconds + addtime)
 
def print_time (x):
    print 'The time is %.2d : %.2d : %.2d' % (x.hour, x.minute, x.second)
print_time (time)
 
newtime = increment (time, 70)
 
print_time (newtime)

Exercise 16.6

def time_to_int(time):
        minutes = time.hour * 60 + time.minute
        seconds = minutes * 60 + time.second
        return seconds
 
def int_to_time(seconds):
        time = Time()
        minutes, time.second = divmod(seconds, 60)
        time.hour, time.minute = divmod(minutes, 60)
        return time
 
def mul_time(time, factor):
        seconds = time_to_int(time)
        seconds *= factor
        seconds = int(seconds)
        return int_to_time(seconds)
 
def average_pace(time, distance):
        return mul_time(time, 1/distance)

Exercise 16.7

Write a class definition for a Date object that has attributes day, month and year. Write a function called increment_date that takes a Date object, date, and an integer, n, and returns a new Date object that represents the day n days after date. Hint: “Thirty days hath September...” Challenge: does your function deal with leap years correctly? See wikipedia.org/wiki/Leap_year.

class Date(object):
        """represents a date.
        attributes: day, month, year"""
 
def print_date(date):
        # German date format
 
        print('{}.{}.{}'.format(date.day, date.month, date.year))
 
def is_leap_year(year):
        # http://en.wikipedia.org/wiki/Leap_year#Algorithm
 
        if year % 4 == 0:
                if year % 100 == 0:
                        if year % 400 == 0:
                                return True
                        return False
                return True
        return False
 
def month_list(year):
        if is_leap_year(year):
                return [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        return [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
 
def days_of_year(year):
        if is_leap_year(year):
                return 366
        return 365
 
def date_to_int(date):
        days = 0
        for year in range(1, date.year):
                days += days_of_year(year)
 
        month_days = month_list(date.year)
        for month in range(1, date.month):
                days += month_days[month - 1]
 
        days += date.day - 1
        return days
 
def int_to_date(days):
        date = Date()
 
        date.year = 1
        next_days = 365
        while days >= next_days:
                date.year += 1
                days -= next_days
                next_days = days_of_year(date.year)
 
        date.month = 1
        next_days = 31
        month_days = month_list(date.year)
        while days >= next_days:
                date.month += 1
                days -= next_days
                next_days = month_days[date.month - 1]
 
        date.day = days + 1
        return date
 
def increment_date(date, n):
        days = date_to_int(date)
        return int_to_date(days + n)
 
d1 = Date()
d1.day, d1.month, d1.year = 8, 3, 2012
print_date(d1)
 
d2 = increment_date(d1, 7)
print_date(d2)

Exercise 16.8

1. Use the datetime module to write a program that gets the current date and prints the day of the week.

from datetime import date
 
def current_weekday():
        i = date.today().weekday()
        print(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][i])
 
current_weekday()

2. Write a program that takes a birthday as input and prints the user’s age and the number of days, hours, minutes and seconds until their next birthday.

# Python3 solution. Replace "input" by "raw_input" for Python2.
from datetime import datetime
 
def time_until_birthday():
    dob_input = input(('Please enter the date of your birth in '
                       'the format "mm/dd/yyyy": '))
    dob = datetime.strptime(dob_input, '%m/%d/%Y')
    now = datetime.now()
    if now > datetime(now.year, dob.month, dob.day):
        age = now.year - dob.year
        next_year = True
    else:
        age = now.year - dob.year - 1
        next_year = False
    time_to_birthday = datetime(now.year + next_year,
                                dob.month, dob.day) - now
    days = time_to_birthday.days
    hours, remainder = divmod(time_to_birthday.seconds, 3600)
    minutes, seconds = divmod(remainder, 60)
    print("\nYou are {} years old.".format(age))
    print(("You have {0} days, {1} hours, {2} minutes and {3} "
           "seconds left until your next birthday.").format(
               days, hours, minutes, seconds))
 
time_until_birthday()
↑Jump back a section

Chapter 17

Exercise 17.8

2.

from visual import scene, sphere
 
scene.range = (256, 256, 256)
scene.center = (128, 128, 128)
 
t = range(0, 256, 51)
 
for x in t:
    for y in t:
        for z in t:
            pos = x, y, z
            color = (x/255., y/255., z/255.)
            sphere(pos=pos, radius=10, color=color)

3. Download http://thinkpython.com/code/color_list.py and use the function read_colors to generate a list of the available colors on your system, their names and RGB values. For each named color draw a sphere in the position that corresponds to its RGB values.

# As there currently (2013-04-12) is no function read_colors in color_list.py
# I use a workaround and simply import the variable COLORS from color_list.py.
# I then use the function all_colors() on COLORS to get a list of the colors.
 
from color_list import COLORS
from visual import scene, sphere
 
def all_colors(colors_string=COLORS):
    """Extract a list of unique RGB-tuples from COLORS.
    The tuples look like (r, g, b), where r, g and b are each integers in
    [0, 255].
    """
 
    # split the string into lines and remove irrelevant lines
    lines = colors_string.split('\n')[2:-2]  
 
    # split the individual lines and remove the names
    numbers_only = [line.split()[:3] for line in lines]
 
    # turn strings into ints and rgb-lists into tuples
    rgb_tuples = [tuple([int(s) for s in lst]) for lst in numbers_only]
 
    # return a list of unique tuples
    return list(set(rgb_tuples))
 
def make_spheres(color_tuples=all_colors()):
    scene.range = (256, 256, 256)
    scene.center = (128, 128, 128)
    for (r, g, b) in color_tuples:
        sphere(pos=(r, g, b), radius=7, color=(r/255., g/255., b/255.))
 
if __name__ == '__main__':
    make_spheres()
↑Jump back a section

Chapter 3.5

calculator

#recursion or recursive
print "\n       INDEX\n""\n     C=1 for addition\n""\n  C=2 for substraction\n""\n      
C=3 for multiplication\n""\n    C=4 for division\n""\n  C=5 for to find modulus\n""\n   C=6 to find factorial\n"
C=input("Enter your choice here: ")
def add(x,y):
        c=x+y
        print x,"+",y,"=",c
def sub(x,y):
        c=x-y
        print x,"-",y,"=",c
def mul(x,y):
        c=x*y
        print x,"*",y,"=",c
def div(x,y):
        c=x/y
        print x,"/",y,"=",c
def mod(x,y):
        c=x%y
        print x,"%",y,"=",c
if C==6:
        def f(n):
                if n==1:
                        print n
                        return n
                else:
                        print n,"*",
                        return n*f(n-1)
        n=input("enter your no here: ")
        print f(n)
if C==1:
        a=input("Enter your first no here: ")
        b=input("Enter your second no here: ")
        add(a,b)
elif C==2:
        a=input("Enter your first no here: ")
        b=input("Enter your second no here: ")
        sub(a,b)
elif C==3:
        a=input("Enter your first no here: ")
        b=input("Enter your second no here: ")
        mul(a,b)
elif C==4:
        a=input("Enter your first no here: ")
        b=input("Enter your second no here: ")  
        div(a,b)
elif C==5:
        a=input("Enter your first no here: ")
        b=input("Enter your second no here: ")
        mod(a,b)

palindrome

def first(word):
        return word[0]
def last(word):
        return word[-1]
def middle(word):
        return word[1:-1]
def palindrome(word):
        if first(word)==last(word):
                word = middle(word)
                n=len(word)
                if n<2:
                        print "palindrome"
                else:           
                        return palindrome(word)
        else:
                print "not palindrome"
 
 
word=raw_input("Enter the  string:")
palindrome(word)

sum of all digits

def sum_of_n_numbers(number):
        if(number==0):
                return 0
        else:
                return number + sum_of_n_numbers(number-1)
num = raw_input("Enter a number:")
num=int(num)
sum = sum_of_n_numbers(num)
print sum
###another answer in case of while loops
def sum_of_Digits(number):
        sum=0
        while number>0:
                digit=number%10
                sum=sum+digit
                number=number/10
        return sum
num=raw_input("enter the number")
num=int(num)
sum_of_digits=sum_of_Digits(num)
print sum_of_digits

Exercise 18.5

class Card(object):
 
        suit_names = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
        rank_names = [None, 'Ace', '2', '3', '4', '5', '6', '7', 
                                        '8', '9', '10', 'Jack', 'Queen', 'King']
 
        def __init__(self, suit = 0, rank = 2):
                self.suit = suit
                self.rank = rank
 
        def __str__(self):
                return '%s of %s' % (Card.rank_names[self.rank], 
                                                        Card.suit_names[self.suit])
 
        def __cmp__(self, other):
                c1 = (self.suit, self.rank)
                c2 = (other.suit, other.rank)
                return cmp(c1, c2)
 
        def is_valid(self):
                return self.rank > 0
 
class Deck(object):
 
        def __init__(self, label = 'Deck'):
                self.label = label
                self.cards = []
                for i in range(4):
                        for k in range(1, 14):
                                card = Card(i, k)
                                self.cards.append(card)
 
        def __str__(self):
                res = []
                for card in self.cards:
                        res.append(str(card))
                print self.label
                return '\n'.join(res)
 
 
        def deal_card(self):
                return self.cards.pop(0)
 
        def add_card(self, card):
                self.cards.append(card)
 
        def shuffle(self):
                import random
                random.shuffle(self.cards)
 
        def sort(self):
                self.cards.sort()
 
        def move_cards(self, other, num):
                for i in range(num):
                        other.add_card(self.deal_card())
 
        def deal_hands(self, num_hands, num_cards):
                if num_hands*num_cards > 52:
                        return 'Not enough cards.'
 
                l = []
 
                for i in range(1, num_hands + 1):
                        hand_i = Hand('Hand %d' % i)
                        self.move_cards(hand_i, num_cards)
                        l.append(hand_i)
 
                return l
 
class Hand(Deck):
 
        def __init__(self, label = ''):
                self.cards = []
                self.label = label
 
# 18-6, 1-4:
class PokerHand(Hand):
 
        def suit_hist(self):
                self.suits = {}
                for card in self.cards:
                        self.suits[card.suit] = self.suits.get(card.suit, 0) + 1
                return self.suits
 
        def rank_hist(self):
                self.ranks = {}
                for card in self.cards:
                        self.ranks[card.rank] = self.ranks.get(card.rank, 0) + 1
                return self.ranks
 
        def P(self):
                self.rank_hist()
                for val in self.ranks.values():
                        if val >= 2:
                                return True
                return False
 
        def TP(self):
                self.rank_hist()
                count = 0
                for val in self.ranks.values():
                        if val == 4:
                                return True
                        elif val >= 2 and val < 4:
                                count += 1
                return count >= 2
 
        def TOAK(self):
                self.rank_hist()
                for val in self.ranks.values():
                        if val >= 3:
                                return True
                return False
 
        def STRseq(self):
                seq = []
                l = STRlist()
                self.rank_hist()
                h = self.ranks.keys()
                h.sort()
                if len(h) < 5:
                        return []
 
                # Accounts for high Aces:
                if 1 in h:
                        h.append(1)
 
                for i in range(5, len(h)+1):
                        if h[i-5:i] in l:
                                seq.append(h[i-5:i])
                return seq
 
        def STR(self):
                seq = self.STRseq()
                return seq != []
 
        def FL(self):
                self.suit_hist()
                for val in self.suits.values():
                        if val >= 5:
                                return True
                return False
 
        def FH(self):
                d = self.rank_hist()
                keys = d.keys()
 
                for key in keys:
                        if d[key] >= 3:
                                keys.remove(key)
                                for key in keys:
                                        if d[key] >= 2:
                                                return True
                return False
 
        def FOAK(self):
                self.rank_hist()
                for val in self.ranks.values():
                        if val >= 4:
                                return True
                return False
 
        def SFL(self):
                seq = self.STRseq()
                if seq == []:
                        return False
                for list in seq:
                        list_suits = []
                        for index in list:
                                for card in self.cards:
                                        if card.rank == index:
                                                list_suits.append(card.suit)
                        list_hist = histogram(list_suits)
                        for key in list_hist.keys():
                                if list_hist[key] >= 5:
                                        return True
                return False
 
        def classify(self):
                self.scores = []
                hands = ['Pair', 'Two-Pair',
                'Three of a Kind', 'Straight',
                'Flush', 'Full House',
                'Four of a Kind', 'Straight Flush']
                if self.P():
                        self.scores.append(1)
                if self.TP():
                        self.scores.append(2)
                if self.TOAK():
                        self.scores.append(3)
                if self.STR():
                        self.scores.append(4)
                if self.FL():
                        self.scores.append(5)
                if self.FH():
                        self.scores.append(6)
                if self.FOAK():
                        self.scores.append(7)
                if self.SFL():
                        self.scores.append(8)
                if self.scores != []:
                        return hands[max(self.scores)-1]
 
def STRlist():
        s = []
        for i in range(0,9):
                s.append(range(1,14)[i:i+5])
        s.append([10,11,12,13,1])       
        return s
 
def histogram(l):
        d = dict()
        for k in range(len(l)):
                d[l[k]] = 1 + d.get(l[k],0)
        return d
 
# 18-6, 5:
def p(config = '', trials = 10000, n = 1):
        """Estimates probability that the
        nth dealt hand will be config. A hand
        consists of seven cards."""
 
        successes = 0
 
        for i in range(1, trials + 1):
                deck = Deck('Deck %d' % i)
                deck.shuffle()
 
                box = Hand()
                deck.move_cards(box, (n-1)*7)
 
                hand = PokerHand('Poker Hand %d' % i)
                deck.move_cards(hand, 7)
                if hand.classify() == config:
                        successes += 1
 
        return 1.0*successes/trials
 
#Iterate until first desired config.:
if __name__ == '__main__':
 
        c = 1
 
        while True:
                deck = Deck()
                deck.shuffle()
                hand = PokerHand('Poker Hand %d' % c)
                deck.move_cards(hand, 5)
                print hand
                print hand.SFL()
                if hand.SFL():
                        print hand.STRseq()
                        break
                print ''
                c += 1
 
Code by Victor Alvarez
↑Jump back a section

Appendix B

Exercise B.3

Write a function called bisection that takes a sorted list and a target value and returns the index of the value in the list, if it’s there, or None if it’s not.

from bisect import bisect_left
 
def bisection(sorted_list, item):
    i = bisect_left(sorted_list, item)
    if i < len(sorted_list) and sorted_list[i] == item:
        return i
    else:
        return None
 
if __name__ == '__main__':
    a = [1, 2, 3]
    print(bisection(a, 2))  # expect 1
    b = [1, 3]
    print(bisection(b, 2))  # expect None
    c = [1, 2]
    print(bisection(c, 3))  # expect None
↑Jump back a section
Last modified on 12 April 2013, at 12:28