# -*- python -*-
# vim:set ft=python:

# @@PLEAC@@_NAME
# @@SKIP@@ Python

# @@PLEAC@@_WEB
# @@SKIP@@ http://www.python.org

# @@PLEAC@@_INTRO
# @@SKIP@@ The latest version of Python is 2.4 but users of 2.3 and 2.2 (and
# @@SKIP@@ in some cases earlier versions) can use the code herein.
# @@SKIP@@ Users of 2.2 and 2.3 should install or copy code from utils.py 
# @@SKIP@@ (http://aima.cs.berkeley.edu/python/utils.py)
# @@SKIP@@ [the first section provides compatability code with 2.4]
# @@SKIP@@ Users of 2.2 should install optik (http://optik.sourceforge.com) 
# @@SKIP@@ [for optparse and textwrap]
# @@SKIP@@ Where a 2.3 or 2.4 feature is unable to be replicated, an effort
# @@SKIP@@ has been made to provide a backward-compatible version in addition
# @@SKIP@@ to one using modern idioms.
# @@SKIP@@ Examples which translate the original Perl closely but which are
# @@SKIP@@ unPythonic are prefixed with a comment stating "DON'T DO THIS".
# @@SKIP@@ In some cases, it may be useful to know the techniques in these, 
# @@SKIP@@ though it's a bad solution for the specific problem.

# @@PLEAC@@_1.0
#-----------------------------
mystr = "\n"   # a newline character
mystr = r"\n"  # two characters, \ and n
#-----------------------------
mystr = "Jon 'Maddog' Orwant"  # literal single quote inside double quotes
mystr = 'Jon "Maddog" Orwant'  # literal double quote inside single quotes
#-----------------------------
mystr = 'Jon \'Maddog\' Orwant'  # escaped single quote
mystr = "Jon \"Maddog\" Orwant"  # escaped double quote
#-----------------------------
mystr = """
This is a multiline string literal
enclosed in triple double quotes.
"""
mystr = '''
And this is a multiline string literal
enclosed in triple single quotes.
'''
#-----------------------------

# @@PLEAC@@_1.1
#-----------------------------

# get a 5-char string, skip 3, then grab 2 8-char strings, then the rest
# Note that struct.unpack cannot use * for an unknown length.
# See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65224
import struct
(lead, s1, s2), tail = struct.unpack("5s 3x 8s 8s", data[:24]), data[24:]

# split at five-char boundaries
fivers = struct.unpack("5s" * (len(data)//5), data)
fivers = print [x[i*5:i*5+5] for i in range(len(x)/5)]

# chop string into individual characters
chars = list(data)
#-----------------------------
mystr = "This is what you have"
#       +012345678901234567890  Indexing forwards  (left to right)
#        109876543210987654321- Indexing backwards (right to left)
#         note that 0 means 10 or 20, etc. above

first = mystr[0]                            # "T"
start = mystr[5:7]                          # "is"
rest = mystr[13:]                           # "you have"
last = mystr[-1]                            # "e"
end = mystr[-4:]                            # "have"
piece = mystr[-8:-5]                        # "you"
#-----------------------------
# Python strings are immutable.
# In general, you should just do piecemeal reallocation:
mystr = "This is what you have"
mystr = mystr[:5] + "wasn't" + mystr[7:]

# Or replace and reallocate
mystr = "This is what you have"
mystr = mystr.replace(" is ", " wasn't ")

# DON'T DO THIS: In-place modification could be done using character arrays
import array
mystr = array.array("c", "This is what you have")
mystr[5:7] = array.array("c", "wasn't")
# mystr is now array('c', "This wasn't what you have")

# DON'T DO THIS: It could also be done using MutableString 
from UserString import MutableString
mystr = MutableString("This is what you have")
mystr[-12:] = "ondrous"
# mystr is now "This is wondrous"
#-----------------------------
# you can test simple substrings with "in" (for regex matching see ch.6):
if txt in mystr[-10:]:
    print "'%s' found in last 10 characters"%txt

# Or use the startswith() and endswith() string methods:
if mystr.startswith(txt):
    print "%s starts with %s."%(mystr, txt)
if mystr.endswith(txt):
    print "%s ends with %s."%(mystr, txt)

#-----------------------------

# @@PLEAC@@_1.2
#-----------------------------
# Introductory Note: quite a bit of this section is not terribly Pythonic
# as names must be set before being used. For instance, unless myvar has 
# been previously defined, these next lines will all raise NameError:
myvar = myvar or some_default
myvar2 = myvar or some_default
myvar |= some_default          # bitwise-or, not logical-or - for demo

# The standard way of setting a default is often:
myvar = default_value
if some_condition:
    pass                     # code which may set myvar to something else

# if myvar is returned from a function and may be empty/None, then use:
myvar = somefunc()
if not myvar:
    myvar = default_value

# If you want a default value that can be overridden by the person calling 
# your code, you can often wrap it in a function with a named parameter:
def myfunc(myvar="a"):
   return myvar + "b"
print myfunc(), myfunc("c")
#=> ab cb

# Note, though, that this won't work for mutable objects such as lists or
# dicts that are mutated in the function as the object is only created once 
# and repeated calls to the same function will return the same object.  This
# can be desired behaviour however - see section 10.3, for instance.
def myfunc(myvar=[]):
    myvar.append("x")
    return myvar
print myfunc(), myfunc()
#=> ['x'] ['x', 'x']

# You need to do:
def myfunc(myvar=None):
    if myvar is None:
        myvar = []
    myvar.append("x")
    return myvar
print myfunc(), myfunc()
#=> ['x'] ['x']

#=== Perl Equivalencies start here
# use b if b is true, otherwise use c
a = b or c

# as that is a little tricksy, the following may be preferred:
if b:
    a = b
else:
    a = c

# set x to y unless x is already true
if not x:
    x = y
#-----------------------------
# use b if b is defined, else c
try:
    a = b
except NameError:
    a = c
#-----------------------------
foo = bar or "DEFAULT VALUE"
#-----------------------------
# To get a user (for both UNIX and Windows), use:
import getpass
user = getpass.getuser()

# DON'T DO THIS: find the user name on Unix systems 
import os
user = os.environ.get("USER")
if user is None:
    user = os.environ.get("LOGNAME")
#-----------------------------
if not starting_point:
    starting_point = "Greenwich"
#-----------------------------
if not a:         # copy only if empty
    a = b

if b:             # assign b if nonempty, else c
    a = b
else:
    a = c
#-----------------------------

# @@PLEAC@@_1.3
#-----------------------------
v1, v2 = v2, v1
#-----------------------------
# DON'T DO THIS:
temp = a
a = b
b = temp
#-----------------------------
a = "alpha"
b = "omega"
a, b = b, a   # the first shall be last -- and versa vice 
#-----------------------------
alpha, beta, production = "January March August".split()
alpha, beta, production = beta, production, alpha
#-----------------------------

# @@PLEAC@@_1.4
#-----------------------------
num = ord(char)
char = chr(num)
#-----------------------------
char = "%c" % num
print "Number %d is character %c" % (num, num)
print "Number %(n)d is character %(n)c" % {"n": num}
print "Number %(num)d is character %(num)c" % locals()
#=> Number 101 is character e
#-----------------------------
ascii_character_numbers = [ord(c) for c in "sample"]
print ascii_character_numbers
#=> [115, 97, 109, 112, 108, 101]

word = "".join([chr(n) for n in ascii_character_numbers])
word = "".join([chr(n) for n in [115, 97, 109, 112, 108, 101]])
print word
#=> sample
#-----------------------------
hal = "HAL"
ibm = "".join([chr(ord(c)+1) for c in hal]) # add one to each ASCII value
print ibm   
#=> IBM
#-----------------------------

# @@PLEAC@@_1.5
#-----------------------------
mylist = list(mystr)
#-----------------------------
for char in mystr:
    pass # do something with char
#-----------------------------
mystr = "an apple a day"
uniq = sorted(set(mystr))
print "unique chars are: '%s'" % "".join(uniq)
#=> unique chars are: ' adelnpy'
#-----------------------------
ascvals = [ord(c) for c in mystr]
print "total is %s for '%s'."%(sum(ascvals), mystr)
#=> total is 1248 for 'an apple a day'.
#-----------------------------
# sysv checksum
def checksum(myfile):
    values = [ord(c) for line in myfile for c in line]
    return sum(values)%(2**16) - 1

import fileinput
print checksum(fileinput.input())   # data from sys.stdin

# Using a function means any iterable can be checksummed:
print checksum(open("C:/test.txt")  # data from file
print checksum("sometext")          # data from string
#-----------------------------
#!/usr/bin/python
# slowcat - emulate a   s l o w  line printer
# usage: slowcat [- DELAY] [files ...]
import sys, select
import re
DELAY = 1
if re.match("^-\d+$",sys.argv[1]):
    DELAY=-int(sys.argv[1])
    del sys.argv[1]
for ln in fileinput.input():
    for c in ln:
        sys.stdout.write(c)
        sys.stdout.flush()
        select.select([],[],[], 0.005 * DELAY)
#-----------------------------

# @@PLEAC@@_1.6
#-----------------------------
# 2.3+ only
revchars = mystr[::-1]  # extended slice - step is -1
revwords = " ".join(mystr.split(" ")[::-1])

# pre 2.3 version:
mylist = list(mystr)
mylist.reverse()
revbytes = "".join(mylist)

mylist = mystr.split()
mylist.reverse()
revwords = ' '.join(mylist)

# Alternative version using reversed():
revchars = "".join(reversed(mystr))
revwords = " ".join(reversed(mystr.split(" ")))

# reversed() makes an iterator, which means that the reversal
# happens as it is consumed.  This means that "print reversed(mystr)" is not
# the same as mystr[::-1].  Standard usage is:
for char in reversed(mystr):
   pass  # ... do something
#-----------------------------
# 2.3+ only
word = "reviver"
is_palindrome = (word == word[::-1])
#-----------------------------
# Generator version
def get_palindromes(fname):
    for line in open(fname):
        word = line.rstrip()
        if len(word) > 5 and word == word[::-1]:
            yield word
long_palindromes = list(get_palindromes("/usr/share/dict/words"))

# Simpler old-style version using 2.2 string reversal
def rev_string(mystr):
    mylist = list(mystr)
    mylist.reverse()
    return "".join(mylist)

long_palindromes=[]
for line in open("/usr/share/dict/words"):
    word = line.rstrip()
    if len(word) > 5 and word == rev_string(word):
        long_palindromes.append(word)
print long_palindromes
#-----------------------------

# @@PLEAC@@_1.7
#-----------------------------
mystr.expandtabs()
mystr.expandtabs(4)
#-----------------------------

# @@PLEAC@@_1.8
#-----------------------------
text = "I am %(rows)s high and %(cols)s long"%{"rows":24, "cols":80)
print text
#=> I am 24 high and 80 long

rows, cols = 24, 80
text = "I am %(rows)s high and %(cols)s long"%locals()
print text
#=> I am 24 high and 80 long
#-----------------------------
import re
print re.sub("\d+", lambda i: str(2 * int(i.group(0))), "I am 17 years old")
#=> I am 34 years old
#-----------------------------
# expand variables in text, but put an error message in
# if the variable isn't defined
class SafeDict(dict):
    def __getitem__(self, key):
        return self.get(key, "[No Variable: %s]"%key)
    
hi = "Hello"
text = "%(hi)s and %(bye)s!"%SafeDict(locals())
print text
#=> Hello and [No Variable: bye]!

#If you don't need a particular error message, just use the Template class:
from string import Template
x = Template("$hi and $bye!")
hi = "Hello"
print x.safe_substitute(locals())
#=> Hello and $bye!
print x.substitute(locals()) # will throw a KeyError

#-----------------------------

# @@PLEAC@@_1.9
#-----------------------------
mystr = "bo peep".upper()  # BO PEEP
mystr = mystr.lower()      # bo peep
mystr = mystr.capitalize() # Bo peep
#-----------------------------
beast = "python"
caprest = beast.capitalize().swapcase() # pYTHON
#-----------------------------
print "thIS is a loNG liNE".title()
#=> This Is A Long Line
#-----------------------------
if a.upper() == b.upper():
    print "a and b are the same"
#-----------------------------
import random
def randcase_one(letter):
    if random.randint(0,5):   # True on 1, 2, 3, 4
        return letter.lower()
    else:
        return letter.upper()

def randcase(myfile):
    for line in myfile:
        yield "".join(randcase_one(letter) for letter in line[:-1])

for line in randcase(myfile):
    print line
#-----------------------------

# @@PLEAC@@_1.10
#-----------------------------
"I have %d guanacos." % (n + 1)
print "I have", n+1, "guanacos."
#-----------------------------
#Python templates disallow in-string calculations (see PEP 292)
from string import Template

email_template = Template("""\
To: $address
From: Your Bank
CC: $cc_number
Date: $date

Dear $name,

Today you bounced check number $checknum to us.
Your account is now closed.

Sincerely,
the management
""")

import random
import datetime

person = {"address":"Joe@somewhere.com",
          "name": "Joe",
          "cc_number" : 1234567890,
          "checknum" : 500+random.randint(0,99)}

print email_template.substitute(person, date=datetime.date.today())
#-----------------------------

# @@PLEAC@@_1.11
#-----------------------------
# indenting here documents
#
# in python multiline strings can be used as here documents
var = """
      your text
      goes here
      """

# using regular expressions
import re
re_leading_blanks = re.compile("^\s+",re.MULTILINE)
var1 = re_leading_blanks.sub("",var)[:-1]

# using string methods 
# split into lines, use every line except first and last, left strip and rejoin.
var2 = "\n".join([line.lstrip() for line in var.split("\n")[1:-1]])

poem = """
       Here's your poem:
       Now far ahead the Road has gone,
          And I must follow, if I can,
       Pursuing it with eager feet,
          Until it joins some larger way
       Where many paths and errand meet.
          And whither then? I cannot say.
               --Bilbo in /usr/src/perl/pp_ctl.c  
       """

import textwrap
print textwrap.dedent(poem)[1:-1]
#-----------------------------
    

# @@PLEAC@@_1.12
#-----------------------------
from textwrap import wrap 
output = wrap(para,
              initial_indent=leadtab
              subsequent_indent=nexttab)
#-----------------------------
#!/usr/bin/env python
# wrapdemo - show how textwrap works

txt = """\
Folding and splicing is the work of an editor,
not a mere collection of silicon
and
mobile electrons!
"""

from textwrap import TextWrapper

wrapper = TextWrapper(width=20,
                      initial_indent=" "*4,
                      subsequent_indent=" "*2)

print "0123456789" * 2
print wrapper.fill(txt)

#-----------------------------
"""Expected result:

01234567890123456789
    Folding and
  splicing is the
  work of an editor,
  not a mere
  collection of
  silicon and mobile
  electrons!
"""

#-----------------------------
# merge multiple lines into one, then wrap one long line

from textwrap import fill
import fileinput

print fill("".join(fileinput.input()))

#-----------------------------
# Term::ReadKey::GetTerminalSize() isn't in the Perl standard library. 
# It isn't in the Python standard library either. Michael Hudson's 
# recipe from python-list #530228 is shown here.
# (http://aspn.activestate.com/ASPN/Mail/Message/python-list/530228)
# Be aware that this will work on Unix but not on Windows.

from termwrap import wrap
import struct, fcntl
def getheightwidth():
    height, width = struct.unpack(
        "hhhh", fcntl.ioctl(0, TERMIOS.TIOCGWINSZ ,"\000"*8))[0:2]
    return height, width

# PERL <>, $/, $\ emulation
import fileinput
import re

_, width = getheightwidth()
for para in re.split(r"\n{2,}", "".join(fileinput.input())):
    print fill(para, width)


# @@PLEAC@@_1.13
#-----------------------------
mystr = '''Mom said, "Don't do that."'''  #"
re.sub("['\"]", lambda i: "\\" + i.group(0), mystr)
re.sub("[A-Z]", lambda i: "\\" + i.group(0), mystr)
re.sub("\W", lambda i: "\\" + i.group(0), "is a test!") # no function like quotemeta?


# @@PLEAC@@_1.14
#-----------------------------
mystr = mystr.lstrip() # left
mystr = mystr.rstrip() # right
mystr = mystr.strip()  # both ends


# @@PLEAC@@_1.15
#-----------------------------
import csv
def parse_csv(line):
    reader = csv.reader([line], escapechar='\\')
    return reader.next()

line = '''XYZZY,"","O'Reilly, Inc","Wall, Larry","a \\"glug\\" bit,",5,"Error, Core Dumped,",''' #"

fields = parse_csv(line)

for i, field in enumerate(fields):
    print "%d : %s" % (i, field)

# pre-2.3 version of parse_csv
import re
def parse_csv(text):
    pattern = re.compile('''"([^"\\\]*(?:\\\.[^"\\\]*)*)",?|([^,]+),?|,''')
    mylist = ["".join(elem) 
              for elem in re.findall(pattern, text)]
    if text[-1] == ",": 
        mylist += ['']
    return mylist

# cvs.reader is meant to work for many lines, something like:
# (NB: in Python default, quotechar is *not* escaped by backslash,
#      but doubled instead. That's what Excel does.)
for fields in cvs.reader(lines, dialect="some"):
    for num, field in enumerate(fields):
        print num, ":", field
#-----------------------------

# @@PLEAC@@_1.16
#-----------------------------
def soundex(name, len=4):
    """ soundex module conforming to Knuth's algorithm
        implementation 2000-12-24 by Gregory Jorgensen
        public domain
    """

    # digits holds the soundex values for the alphabet
    digits = '01230120022455012623010202'
    sndx = ''
    fc = ''

    # translate alpha chars in name to soundex digits
    for c in name.upper():
        if c.isalpha():
            if not fc: 
                fc = c   # remember first letter
            d = digits[ord(c)-ord('A')]
            # duplicate consecutive soundex digits are skipped
            if not sndx or (d != sndx[-1]):
                sndx += d

    # replace first digit with first alpha character
    sndx = fc + sndx[1:]

    # remove all 0s from the soundex code
    sndx = sndx.replace('0','')

    # return soundex code padded to len characters
    return (sndx + (len * '0'))[:len]

user = raw_input("Lookup user: ")
if user == "":
    raise SystemExit

name_code = soundex(user)
for line in open("/etc/passwd"):
    line = line.split(":")
    for piece in line[4].split():
        if name_code == soundex(piece):
            print "%s: %s\n" % line[0], line[4])
#-----------------------------

# @@PLEAC@@_1.17
#-----------------------------
import sys, fileinput, re

data = """\
analysed        => analyzed
built-in        => builtin
chastized       => chastised
commandline     => command-line
de-allocate     => deallocate
dropin          => drop-in
hardcode        => hard-code
meta-data       => metadata
multicharacter  => multi-character
multiway        => multi-way
non-empty       => nonempty
non-profit      => nonprofit
non-trappable   => nontrappable
pre-define      => predefine
preextend       => pre-extend
re-compiling    => recompiling
reenter         => re-enter
turnkey         => turn-key
"""
mydict = {}
for line in data.split("\n"):
    if not line.strip():
        continue
    k, v = [word.strip() for word in line.split("=>")]
    mydict[k] = v
pattern_text = "(" + "|".join([re.escape(word) for word in mydict.keys()]) + ")"
pattern = re.compile(pattern_text)

args = sys.argv[1:]
verbose = 0
if args and args[0] == "-v":
    verbose = 1
    args = args[1:]

if not args:
    sys.stderr.write("%s: Reading from stdin\n" % sys.argv[0])

for line in fileinput.input(args, inplace=1, backup=".orig"):
    output = ""
    pos = 0
    while True:
        match = pattern.search(line, pos)
        if not match:
            output += line[pos:]
            break
        output += line[pos:match.start(0)] + mydict[match.group(1)]
        pos = match.end(0)
    sys.stdout.write(output)
#-----------------------------

# @@PLEAC@@_1.18
#-----------------------------
#!/usr/bin/python
# psgrep - print selected lines of ps output by
#          compiling user queries into code.
#
# examples :
# psgrep "uid<10"
import sys, os, re

class PsLineMatch:
    # each field from the PS header
    fieldnames = ("flags","uid","pid","ppid","pri","nice","size", \
                  "rss","wchan","stat","tty","time","command")
    numeric_fields = ("flags","uid","pid","ppid","pri","nice","size","rss")
    def __init__(self):
        self._fields = {}

    def new_line(self, ln):
        self._ln = ln.rstrip()
        # ps header for option "wwaxl" (different than in the perl code)
        """
          F   UID   PID  PPID PRI  NI   VSZ  RSS WCHAN  STAT TTY        TIME COMMAND"
        004     0     1     0  15   0   448  236 schedu S    ?          0:07 init"
        .   .     .     .     .   .   .     .    .      .    .    .          .
        """
        # because only the last entry might contain blanks, splitting
        # is safe
        data = self._ln.split(None,12)
        for fn, elem in zip(self.fieldnames, data):
            if fn in self.numeric_fields:  # make numbers integer 
                self._fields[fn] = int(elem)
            else:
                self._fields[fn] = elem

    def set_query(self, args):
        # assume args: "uid==500", "command ~ ^wm"
        conds=[]
        m = re.compile("(\w+)([=<>]+)(.+)")
        for a in args:
            try:
                (field,op,val) = m.match(a).groups()
            except:
                print "can't understand query \"%s\"" % (a)
                raise SystemExit
            if field in self.numeric_fields:
                conds.append(a)
            else:
                conds.append("%s%s'%s'",(field,op,val))
        self._desirable = compile("(("+")and(".join(conds)+"))", "<string>","eval")

    def is_desirable(self):
        return eval(self._desirable, {}, self._fields)

    def __str__(self):
        # to allow "print".
        return self._ln

if len(sys.argv)<=1:
    print """usage: %s criterion ...
    Each criterion is a Perl expression involving: 
    %s
    All criteria must be met for a line to be printed.""" \
    % (sys.argv[0], " ".join(PsLineMatch().fieldnames))
    raise SystemExit

psln = PsLineMatch()
psln.set_query(sys.argv[1:])
p = os.popen("ps wwaxl")
print p.readline()[:-1]        # emit header line
for ln in p.readlines():
    psln.new_line(ln)
    if psln.is_desirable():
        print psln
p.close()

# alternatively one could consider every argument being a string and
# support wildcards: "uid==500" "command~^wm" by means of re, but this
# does not show dynamic python code generation, although re.compile
# also precompiles.
#-----------------------------


# @@PLEAC@@_2.1
#-----------------------------
# The standard way of validating numbers is to convert them and catch
# an exception on failure

try:
    myfloat = float(mystr)
    print "is a decimal number" 
except TypeError:
    print "is not a decimal number"

try:
    myint = int(mystr)
    print "is an integer"
except TypeError:
    print "is not an integer"

# DON'T DO THIS. Explicit checking is prone to errors:
if mystr.isdigit():                         # Fails on "+4"
    print 'is a positive integer'   
else:
    print 'is not'

if re.match("[+-]?\d+$", mystr):            # Fails on "- 1" 
    print 'is an integer'           
else:
    print 'is not'

if re.match("-?(?:\d+(?:\.\d*)?|\.\d+)$", mystr):  # Opaque, and fails on "- 1"
    print 'is a decimal number'
else:
    print 'is not'

#-----------------------------

# @@PLEAC@@_2.2
#-----------------------------
# equal(num1, num2, accuracy) : returns true if num1 and num2 are
#   equal to accuracy number of decimal places

def equal(num1, num2, accuracy):
    return abs(num1 - num2) < 10**(-accuracy)
#-----------------------------
from __future__ import division  # use / for float div and // for int div

wage = 536                                      # $5.36/hour
week = 40 * wage                                # $214.40
print "One week's wage is: $%.2f" % (week/100)
#=> One week's wage is: $214.40
#-----------------------------

# @@PLEAC@@_2.3
#-----------------------------
rounded = round(num)            # rounds to integer
#-----------------------------
a = 0.255
b = "%.2f" % a
print "Unrounded: %f\nRounded: %s" % (a, b)
print "Unrounded: %f\nRounded: %.2f" % (a, a)
#=> Unrounded: 0.255000
#=> Rounded: 0.26
#=> Unrounded: 0.255000
#=> Rounded: 0.26
#-----------------------------
from math import floor, ceil

print "number\tint\tfloor\tceil"
a = [3.3, 3.5, 3.7, -3.3]
for n in a:
    print "% .1f\t% .1f\t% .1f\t% .1f" % (n, int(n), floor(n), ceil(n))
#=> number  int   floor ceil
#=>  3.3     3.0   3.0   4.0
#=>  3.5     3.0   3.0   4.0
#=>  3.7     3.0   3.0   4.0
#=> -3.3    -3.0  -4.0  -3.0
#-----------------------------

# @@PLEAC@@_2.4
#-----------------------------
# To convert a string in any base up to base 36, use the optional arg to int():
num = int('0110110', 2)   # num is 54

# To convert an int to an string representation in another base, you could use
# <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/111286>:
import baseconvert 
def dec2bin(i):
    return baseconvert.baseconvert(i, baseconvert.BASE10, baseconvert.BASE2)

binstr = dec2bin(54)      # binstr is 110110
#-----------------------------

# @@PLEAC@@_2.5
#-----------------------------
for i in range(x,y):
    pass # i is set to every integer from x to y, excluding y

for i in range(x, y, 7):
    pass # i is set to every integer from x to y, stepsize = 7

print "Infancy is:",
for i in range(0,3):
    print i,
print

print "Toddling is:",
for i in range(3,5):
    print i,
print

# DON'T DO THIS:
print "Childhood is:",
i = 5
while i <= 12:
    print i
    i += 1

#=> Infancy is: 0 1 2
#=> Toddling is: 3 4
#=> Childhood is: 5 6 7 8 9 10 11 12
#-----------------------------

# @@PLEAC@@_2.6
#-----------------------------
# See http://www.faqts.com/knowledge_base/view.phtml/aid/4442
# for a module that does this
#-----------------------------

# @@PLEAC@@_2.7
#-----------------------------
import random          # use help(random) to see the (large) list of funcs

rand = random.randint(x, y)
#-----------------------------
rand = random.randint(25, 76)
print rand
#-----------------------------
elt = random.choice(mylist)
#-----------------------------
import string
chars = string.letters + string.digits + "!@$%^&*"
password = "".join([random.choice(chars) for i in range(8)])
#-----------------------------

# @@PLEAC@@_2.8
#-----------------------------
# Changes the default RNG
random.seed()

# Or you can create independent RNGs
gen1 = random.Random(6)
gen2 = random.Random(6)
gen3 = random.Random(10)
a1, b1 = gen1.random(), gen1.random()
a2, b2 = gen2.random(), gen2.random()
a3, b3 = gen3.random(), gen3.random()
# a1 == a2 and b1 == b2
#-----------------------------

# @@PLEAC@@_2.9
#-----------------------------
# see http://www.sbc.su.se/~per/crng/ or http://www.frohne.westhost.com/rv11reference.htm
#-----------------------------

# @@PLEAC@@_2.10
#-----------------------------
import random
mean = 25
sdev = 2
salary = random.gauss(mean, sdev)
print "You have been hired at %.2f" % salary
#-----------------------------

# @@PLEAC@@_2.11
#-----------------------------
radians = math.radians(degrees)
degrees = math.degrees(radians)

# pre-2.3:
from __future__ import division
import math
def deg2rad(degrees):
    return (degrees / 180) * math.pi
def rad2deg(radians):
    return (radians / math.pi) * 180
#-----------------------------
# Use deg2rad instead of math.radians if you have pre-2.3 Python.
import math
def degree_sine(degrees):
    radians = math.radians(degrees)
    return math.sin(radians)
#-----------------------------

# @@PLEAC@@_2.12
#-----------------------------
import math

# DON'T DO THIS.  Use math.tan() instead.
def tan(theta):
    return math.sin(theta) / math.cos(theta)
#----------------
# NOTE: this sets y to 16331239353195370.0
try:
  y = math.tan(math.pi/2)
except ValueError:
  y = None
#-----------------------------

# @@PLEAC@@_2.13
#-----------------------------
import math
log_e = math.log(VALUE)
#-----------------------------
log_10 = math.log10(VALUE)
#-----------------------------
def log_base(base, value):
    return math.log(value) / math.log(base)
#-----------------------------
# log_base defined as above
answer = log_base(10, 10000)
print "log10(10,000) =", answer
#=> log10(10,000) = 4.0
#-----------------------------

# @@PLEAC@@_2.14
#-----------------------------
# NOTE: must have NumPy installed.  See
#   http://www.pfdubois.com/numpy/

import Numeric
a = Numeric.array( ((3, 2, 3),
                    (5, 9, 8) ), "d")
b = Numeric.array( ((4, 7),
                    (9, 3),
                    (8, 1) ), "d")
c = Numeric.matrixmultiply(a, b)

print c
#=> [[  54.   30.]
#=>  [ 165.   70.]]

print a.shape, b.shape, c.shape
#=> (2, 3) (3, 2) (2, 2)
#-----------------------------

# @@PLEAC@@_2.15
#-----------------------------
a = 3+5j
b = 2-2j
c = a * b
print "c =", c
#=> c = (16+4j)

print c.real, c.imag, c.conjugate()
#=> 16.0 4.0 (16-4j)
#-----------------------------
import cmath
print cmath.sqrt(3+4j)
#=> (2+1j)
#-----------------------------

# @@PLEAC@@_2.16
#-----------------------------
number = int(hexadecimal, 16)
number = int(octal, 8)
s = hex(number)
s = oct(number)

num = raw_input("Gimme a number in decimal, octal, or hex: ").rstrip()
if num.startswith("0x"):
    num = int(num[2:], 16)
elif num.startswith("0"):
    num = int(num[1:], 8)
else:
    num = int(num)
print "%(num)d %(num)x %(num)o\n" % { "num": num }
#-----------------------------


# @@PLEAC@@_2.17
#-----------------------------
def commify(amount):
    amount = str(amount)
    firstcomma = len(amount)%3 or 3  # set to 3 if would make a leading comma
    first, rest = amount[:firstcomma], amount[firstcomma:]
    segments = [first] + [rest[i:i+3] for i in range(0, len(rest), 3)]
    return ",".join(segments)

print commify(12345678) 
#=> 12,345,678

# DON'T DO THIS. It works on 2.3+ only and is slower and less straightforward
# than the non-regex version above.
import re
def commify(amount):
    amount = str(amount)
    amount = amount[::-1]
    amount = re.sub(r"(\d\d\d)(?=\d)(?!\d*\.)", r"\1,", amount)
    return amount[::-1]

# @@PLEAC@@_2.18
# Printing Correct Plurals
#-----------------------------
def pluralise(value, root, singular="", plural="s"):
    if value == 1:
        return root + singular
    else:
        return root + plural

print "It took", duration, pluralise(duration, 'hour')

print "%d %s %s enough." % (duration, 
                            pluralise(duration, 'hour'), 
                            pluralise(duration, '', 'is', 'are'))
#-----------------------------
import re
def noun_plural(word):
    endings = [("ss", "sses"),
               ("([psc]h)", r"\1es"),
               ("z", "zes"),
               ("ff", "ffs"),
               ("f", "ves"),
               ("ey", "eys"),
               ("y", "ies"),
               ("ix", "ices"),
               ("([sx])", r"\1es"),
               ("", "s")]
    for singular, plural in endings:
        ret, found = re.subn("%s$"%singular, plural, word)
        if found:
            return ret
    
verb_singular = noun_plural;       # make function alias
#-----------------------------

# @@PLEAC@@_2.19
# Program: Calculating Prime Factors
#-----------------------------
#% bigfact 8 9 96 2178
#8          2**3
#
#9          3**2
#
#96         2**5 3
#
#2178       2 3**2 11**2
#-----------------------------
#% bigfact 239322000000000000000000
#239322000000000000000000 2**19 3 5**18 39887 
#
#
#% bigfact 25000000000000000000000000
#25000000000000000000000000 2**24 5**26
#-----------------------------
import sys

def factorise(num):
    factors = {}
    orig = num
    print num, '\t',

    # we take advantage of the fact that (i +1)**2 = i**2 + 2*i +1
    i, sqi = 2, 4
    while sqi <= num:
        while not num%i:
            num /= i
            factors[i] = factors.get(i, 0) + 1

        sqi += 2*i + 1
        i += 1

    if num != 1 and num != orig:
        factors[num] = factors.get(num, 0) + 1

    if not factors:
        print "PRIME"

    for factor in sorted(factors):
        if factor:
            tmp = str(factor)
            if factors[factor]>1: tmp += "**" + str(factors[factor])
            print tmp,
    print
    
#--------
if __name__ == '__main__':
    if len(sys.argv) == 1:
        print "Usage:", sys.argv[0], " number [number, ]"
    else:
        for strnum in sys.argv[1:]:
            try:
                num = int(strnum)
                factorise(num)
            except ValueError:
                print strnum, "is not an integer"
#-----------------------------
# A more Pythonic variant (which separates calculation from printing):
def format_factor(base, exponent):
    if exponent > 1:
        return "%s**%s"%(base, exponent)
    return str(base)

def factorise(num):
    factors = {}
    orig = num

    # we take advantage of the fact that (i+1)**2 = i**2 + 2*i +1
    i, sqi = 2, 4
    while sqi <= num:
        while not num%i:
            num /= i
            factors[i] = factors.get(i, 0) + 1
        sqi += 2*i + 1
        i += 1

    if num not in (1, orig):
        factors[num] = factors.get(num, 0) + 1

    if not factors:
        return ["PRIME"]

    out = [format_factor(base, exponent)
           for base, exponent in sorted(factors.items())]
    return out

def print_factors(value):
    try:
        num = int(value)
        if num != float(value):
            raise ValueError
    except (ValueError, TypeError):
        raise ValueError("Can only factorise an integer")
    factors = factorise(num) 
    print num, "\t", " ".join(factors)

# @@PLEAC@@_3.0
#----------------------------- 
#introduction
# There are three common ways of manipulating dates in Python
# mxDateTime - a popular third-party module (not discussed here) 
# time - a fairly low-level standard library module 
# datetime - a new library module for Python 2.3 and used for most of these samples 
# (I will use full names to show which module they are in, but you can also use
# from datetime import datetime, timedelta and so on for convenience) 

import time
import datetime

print "Today is day", time.localtime()[7], "of the current year" 
# Today is day 218 of the current year

today = datetime.date.today()
print "Today is day", today.timetuple()[7], "of ", today.year
# Today is day 218 of 2003

print "Today is day", today.strftime("%j"), "of the current year" 
# Today is day 218 of the current year
 

# @@PLEAC@@_3.1
#----------------------------- 
# Finding todays date

today = datetime.date.today()
print "The date is", today 
#=> The date is 2003-08-06

# the function strftime() (string-format time) produces nice formatting
# All codes are detailed at http://www.python.org/doc/current/lib/module-time.html
print t.strftime("four-digit year: %Y, two-digit year: %y, month: %m, day: %d") 
#=> four-digit year: 2003, two-digit year: 03, month: 08, day: 06


# @@PLEAC@@_3.2
#----------------------------- 
# Converting DMYHMS to Epoch Seconds
# To work with Epoch Seconds, you need to use the time module

# For the local timezone
t = datetime.datetime.now()
print "Epoch Seconds:", time.mktime(t.timetuple())
#=> Epoch Seconds: 1060199000.0

# For UTC
t = datetime.datetime.utcnow()
print "Epoch Seconds:", time.mktime(t.timetuple())
#=> Epoch Seconds: 1060195503.0


# @@PLEAC@@_3.3
#----------------------------- 
# Converting Epoch Seconds to DMYHMS

now = datetime.datetime.fromtimestamp(EpochSeconds)
#or use datetime.datetime.utcfromtimestamp()
print now
#=> datetime.datetime(2003, 8, 6, 20, 43, 20)
print now.ctime()
#=> Wed Aug  6 20:43:20 2003

# or with the time module
oldtimetuple = time.localtime(EpochSeconds)
# oldtimetuple contains (year, month, day, hour, minute, second, weekday, yearday, daylightSavingAdjustment) 
print oldtimetuple 
#=> (2003, 8, 6, 20, 43, 20, 2, 218, 1)


# @@PLEAC@@_3.4
#----------------------------- 
# Adding to or Subtracting from a Date
# Use the rather nice datetime.timedelta objects

now = datetime.date(2003, 8, 6)
difference1 = datetime.timedelta(days=1)
difference2 = datetime.timedelta(weeks=-2)

print "One day in the future is:", now + difference1
#=> One day in the future is: 2003-08-07

print "Two weeks in the past is:", now + difference2
#=> Two weeks in the past is: 2003-07-23

print datetime.date(2003, 8, 6) - datetime.date(2000, 8, 6)
#=> 1095 days, 0:00:00

#----------------------------- 
birthtime = datetime.datetime(1973, 01, 18, 3, 45, 50)   # 1973-01-18 03:45:50

interval = datetime.timedelta(seconds=5, minutes=17, hours=2, days=55) 
then = birthtime + interval

print "Then is", then.ctime()
#=> Then is Wed Mar 14 06:02:55 1973

print "Then is", then.strftime("%A %B %d %I:%M:%S %p %Y")
#=> Then is Wednesday March 14 06:02:55 AM 1973

#-----------------------------
when = datetime.datetime(1973, 1, 18) + datetime.timedelta(days=55) 
print "Nat was 55 days old on:", when.strftime("%m/%d/%Y").lstrip("0")
#=> Nat was 55 days old on: 3/14/1973


# @@PLEAC@@_3.5
#----------------------------- 
# Dates produce timedeltas when subtracted.

diff = date2 - date1
diff = datetime.date(year1, month1, day1) - datetime.date(year2, month2, day2)
#----------------------------- 

bree = datetime.datetime(1981, 6, 16, 4, 35, 25)
nat  = datetime.datetime(1973, 1, 18, 3, 45, 50)

difference = bree - nat
print "There were", difference, "minutes between Nat and Bree"
#=> There were 3071 days, 0:49:35 between Nat and Bree

weeks, days = divmod(difference.days, 7)

minutes, seconds = divmod(difference.seconds, 60)
hours, minutes = divmod(minutes, 60)

print "%d weeks, %d days, %d:%d:%d" % (weeks, days, hours, minutes, seconds)
#=> 438 weeks, 5 days, 0:49:35

#----------------------------- 
print "There were", difference.days, "days between Bree and Nat." 
#=> There were 3071 days between bree and nat


# @@PLEAC@@_3.6
#----------------------------- 
# Day in a Week/Month/Year or Week Number

when = datetime.date(1981, 6, 16)

print "16/6/1981 was:"
print when.strftime("Day %w of the week (a %A). Day %d of the month (%B).")
print when.strftime("Day %j of the year (%Y), in week %W of the year.")

#=> 16/6/1981 was:
#=> Day 2 of the week (a Tuesday). Day 16 of the month (June).
#=> Day 167 of the year (1981), in week 24 of the year.


# @@PLEAC@@_3.7
#----------------------------- 
# Parsing Dates and Times from Strings

time.strptime("Tue Jun 16 20:18:03 1981")
# (1981, 6, 16, 20, 18, 3, 1, 167, -1)

time.strptime("16/6/1981", "%d/%m/%Y")
# (1981, 6, 16, 0, 0, 0, 1, 167, -1)
# strptime() can use any of the formatting codes from time.strftime()

# The easiest way to convert this to a datetime seems to be; 
now = datetime.datetime(*time.strptime("16/6/1981", "%d/%m/%Y")[0:5])
# the '*' operator unpacks the tuple, producing the argument list.


# @@PLEAC@@_3.8
#----------------------------- 
# Printing a Date
# Use datetime.strftime() - see helpfiles in distro or at python.org

print datetime.datetime.now().strftime("The date is %A (%a) %d/%m/%Y") 
#=> The date is Friday (Fri) 08/08/2003

# @@PLEAC@@_3.9
#----------------------------- 
# High Resolution Timers

t1 = time.clock()
# Do Stuff Here
t2 = time.clock()
print t2 - t1

# 2.27236813618
# Accuracy will depend on platform and OS,
# but time.clock() uses the most accurate timer it can

time.clock(); time.clock()
# 174485.51365466841
# 174485.55702610247

#----------------------------- 
# Also useful;
import timeit
code = '[x for x in range(10) if x % 2 == 0]'
eval(code)
# [0, 2, 4, 6, 8]

t = timeit.Timer(code)
print "10,000 repeats of that code takes:", t.timeit(10000), "seconds" 
print "1,000,000 repeats of that code takes:", t.timeit(), "seconds"

# 10,000 repeats of that code takes: 0.128238644856 seconds
# 1,000,000 repeats of that code takes:  12.5396490336 seconds

#----------------------------- 
import timeit
code = 'import random; l = random.sample(xrange(10000000), 1000); l.sort()' 
t = timeit.Timer(code)

print "Create a list of a thousand random numbers. Sort the list. Repeated a thousand times." 
print "Average Time:", t.timeit(1000) / 1000
# Time taken: 5.24391507859


# @@PLEAC@@_3.10
#----------------------------- 
# Short Sleeps

seconds = 3.1
time.sleep(seconds)
print "boo"

# @@PLEAC@@_3.11
#----------------------------- 
# Program HopDelta
# Save a raw email to disk and run "python hopdelta.py FILE"
# and it will process the headers and show the time taken
# for each server hop (nb: if server times are wrong, negative dates
# might appear in the output).

import datetime, email, email.Utils
import os, sys, time

def extract_date(hop):
    # According to RFC822, the date will be prefixed with
    # a semi-colon, and is the last part of a received
    # header.
    date_string = hop[hop.find(';')+2:]
    date_string = date_string.strip()
    time_tuple = email.Utils.parsedate(date_string)

    # convert time_tuple to datetime
    EpochSeconds = time.mktime(time_tuple) 
    dt = datetime.datetime.fromtimestamp(EpochSeconds)
    return dt

def process(filename):
    # Main email file processing
    # read the headers and process them
    f = file(filename, 'rb')
    msg = email.message_from_file(f)

    hops = msg.get_all('received')
    
    # in reverse order, get the server(s) and date/time involved
    hops.reverse()
    results = []
    for hop in hops:
        hop = hop.lower()
        
        if hop.startswith('by'):  # 'Received: by' line
            sender = "start"
            receiver = hop[3:hop.find(' ',3)]
            date = extract_date(hop)

        else:  # 'Received: from' line
            sender = hop[5:hop.find(' ',5)]
            by = hop.find('by ')+3
            receiver = hop[by:hop.find(' ', by)]
            date = extract_date(hop)

        results.append((sender, receiver, date))
    output(results)

def output(results):
    print "Sender, Recipient, Time, Delta"
    print
    previous_dt = delta = 0
    for (sender, receiver, date) in results:
        if previous_dt:
            delta = date - previous_dt
        
        print "%s, %s, %s, %s" % (sender,
                               receiver,
                               date.strftime("%Y/%d/%m %H:%M:%S"),
                               delta)
        print
        previous_dt = date   
            
def main():
    # Perform some basic argument checking
    if len(sys.argv) != 2:
        print "Usage: mailhop.py FILENAME"

    else:
        filename = sys.argv[1]
        if os.path.isfile(filename):
            process(filename)
        else:
            print filename, "doesn't seem to be a valid file."

if __name__ == '__main__':
    main()


# @@PLEAC@@_4.0
#-----------------------------
# Python does not automatically flatten lists, in other words
# in the following, non-nested contains four elements and
# nested contains three elements, the third element of which
# is itself a list containing two elements:
non_nested = ["this", "that", "the", "other"]
nested = ["this", "that", ["the", "other"]]
#-----------------------------
tune = ["The", "Star-Spangled", "Banner"]
#-----------------------------

# @@PLEAC@@_4.1
#-----------------------------
a = ["quick", "brown", "fox"]
a = "Why are you teasing me?".split()

text = """
    The boy stood on the burning deck,
    It was as hot as glass.
"""
lines = [line.lstrip() for line in text.strip().split("\n")]
#-----------------------------
biglist = [line.rstrip() for line in open("mydatafile")]
#-----------------------------
banner = "The Mines of Moria"
banner = 'The Mines of Moria'
#-----------------------------
name = "Gandalf"
banner = "Speak, " + name + ", and enter!"
banner = "Speak, %s, and welcome!" % name
#-----------------------------
his_host = "www.python.org"
import os
host_info = os.popen("nslookup " + his_host).read()

# NOTE: not really relevant to Python (no magic '$$' variable)
python_info = os.popen("ps %d" % os.getpid()).read()
shell_info = os.popen("ps $$").read()
#-----------------------------
# NOTE: not really relevant to Python (no automatic interpolation)
banner = ["Costs", "only", "$4.95"]
banner = "Costs only $4.95".split()
#-----------------------------
brax = """ ' " ( ) < > { } [ ] """.split()            #"""
brax = list("""'"()<>{}[]""")                         #"""
rings = '''They're  "Nenya Narya Vilya"'''.split()    #'''
tags   = 'LI TABLE TR TD A IMG H1 P'.split()
sample = r'The backslash (\) is often used in regular expressions.'.split()

#-----------------------------
banner = "The backslash (\\) is often used in regular expressions.".split()
#-----------------------------
ships = u"Niña Pinta Santa María".split()          # WRONG (only three ships)
ships = [u"Niña", u"Pinta", u"Santa María"]        # right
#-----------------------------

# @@PLEAC@@_4.2
#-----------------------------
def commify_series(args):
    n = len(args)
    if n == 0: 
        return ""
    elif n == 1: 
        return args[0]
    elif n == 2: 
        return args[0] + " and " + args[1]
    return ", ".join(args[:-1]) + ", and " + args[-1]

commify_series([])
commify_series(["red"])
commify_series(["red", "yellow"])
commify_series(["red", "yellow", "green"])
#-----------------------------
mylist = ["red", "yellow", "green"]
print "I have", mylist, "marbles."
print "I have", " ".join(mylist), "marbles."
#=> I have ['red', 'yellow', 'green'] marbles.
#=> I have red yellow green marbles.

#-----------------------------
#!/usr/bin/env python
# commify_series - show proper comma insertion in list output
data = (
    ( 'just one thing', ),
    ( 'Mutt Jeff'.split() ),
    ( 'Peter Paul Mary'.split() ),
    ( 'To our parents', 'Mother Theresa', 'God' ),
    ( 'pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna' ),
    ( 'recycle tired, old phrases', 'ponder big, happy thoughts' ),
    ( 'recycle tired, old phrases',
      'ponder big, happy thoughts',
      'sleep and dream peacefully' ),
    )

def commify_series(terms):
    for term in terms:
        if "," in term:
            sepchar = "; "
            break
    else:
        sepchar = ", "

    n = len(terms)
    if n == 0: 
        return ""
    elif n == 1:
        return terms[0]
    elif n == 2:
        return " and ".join(terms)
    return "%s%sand %s" % (sepchar.join(terms[:-1]), sepchar, terms[-1])

for item in data:
    print "The list is: %s." % commify_series(item)

#=> The list is: just one thing.
#=> The list is: Mutt and Jeff.
#=> The list is: Peter, Paul, and Mary.
#=> The list is: To our parents, Mother Theresa, and God.
#=> The list is: pastrami, ham and cheese, peanut butter and jelly, and tuna.
#=> The list is: recycle tired, old phrases and ponder big, happy thoughts.
#=> The list is: recycle tired, old phrases; ponder big, happy thoughts; and
#   sleep and dream peacefully.
#-----------------------------

# @@PLEAC@@_4.3
#-----------------------------
# Python allocates more space than is necessary every time a list needs to
# grow and only shrinks lists when more than half the available space is
# unused.  This means that adding or removing an element will in most cases
# not force a reallocation.

del mylist[size:]         # shrink mylist
mylist += [None] * size   # grow mylist by appending 'size' None elements

# To add an element to the end of a list, use the append method:
mylist.append(4)

# To insert an element, use the insert method:
mylist.insert(0, 10) # Insert 10 at the beginning of the list

# To extend one list with the contents of another, use the extend method:
list2 = [1,2,3]
mylist.extend(list2)

# To insert the contents of one list into another, overwriting zero or 
# more elements, specify a slice:
mylist[1:1] = list2   # Don't overwrite anything; grow mylist if needed
mylist[2:3] = list2   # Overwrite mylist[2] and grow mylist if needed

# To remove one element from the middle of a list:
# To remove elements from the middle of a list:
del mylist[idx1:idx2]  # 0 or more
x = mylist.pop(idx)    # remove mylist[idx] and assign it to x

# You cannot assign to or get a non-existent element:
# >>> x = []
# >>> x[4] = 5
#
# Traceback (most recent call last):
#   File "<pyshell#1>", line 1, in -toplevel-
#     x[4] = 5
# IndexError: list assignment index out of range
#
# >>> print x[1000]
#
# Traceback (most recent call last):
#  File "<pyshell#16>", line 1, in -toplevel-
#    print x[1000]
# IndexError: list index out of range
#-----------------------------
def what_about_that_list(terms):
    print "The list now has", len(terms), "elements."
    print "The index of the last element is", len(terms)-1, "(or -1)."
    print "Element #3 is %s." % terms[3]

people = "Crosby Stills Nash Young".split()
what_about_that_list(people)
#-----------------------------
#=> The list now has 4 elements.
#=> The index of the last element is 3 (or -1).
#=> Element #3 is Young.
#-----------------------------
people.pop()
what_about_that_list(people)
#-----------------------------
people += [None] * (10000 - len(people))
#-----------------------------
#>>> people += [None] * (10000 - len(people))
#>>> what_about_that_list(people)
#The list now has 10000 elements.
#The index of the last element is 9999 (or -1).
#Element #3 is None.
#-----------------------------

# @@PLEAC@@_4.4
#-----------------------------
for item in mylist:
    pass # do something with item
#-----------------------------
for user in bad_users:
    complain(user)
#-----------------------------
import os
for (key, val) in sorted(os.environ.items()):
    print "%s=%s" % (key, val)
#-----------------------------
for user in all_users:
    disk_space = get_usage(user)    # find out how much disk space in use
    if disk_space > MAX_QUOTA:      # if it's more than we want ...
        complain(user)              # ... then object vociferously
#-----------------------------
import os
for line in os.popen("who"):
    if "dalke" in line:
        print line,  # or print line[:-1]

# or:
print "".join([line for line in os.popen("who")
                   if "dalke" in line]),

#-----------------------------
for line in myfile:
    for word in line.split(): # Split on whitespace
        print word[::-1],     # reverse word
    print

# pre 2.3:
for line in myfile:
    for word in line.split(): # Split on whitespace
        chars = list(word)    # Turn the string into a list of characters
        chars.reverse()
        print "".join(chars),
    print
#-----------------------------
for item in mylist:
    print "i =", item
#-----------------------------
# NOTE: you can't modify in place the way Perl does:
# data = [1, 2, 3]
# for elem in data:
#     elem -= 1
#print data
#=>[1, 2, 3]

data = [1, 2, 3]
data = [i-1 for i in data]
print data
#=>[0, 1, 2]

# or
for i, elem in enumerate(data):
    data[i] = elem - 1
#-----------------------------
# NOTE: strings are immutable in Python so this doesn't translate well.
s = s.strip()
data = [s.strip() for s in data]
for k, v in mydict.items():
    mydict[k] = v.strip()
#-----------------------------

# @@PLEAC@@_4.5
#-----------------------------
fruits = ["Apple", "Blackberry"]
for fruit in fruits:
    print fruit, "tastes good in a pie."
#=> Apple tastes good in a pie.
#=> Blackberry tastes good in a pie.
#-----------------------------
# DON'T DO THIS:
for i in range(len(fruits)):
    print fruits[i], "tastes good in a pie."

# If you must explicitly index, use enumerate():
for i, fruit in enumerate(fruits):
    print "%s) %s tastes good in a pie."%(i+1, fruit)
#-----------------------------
rogue_cats = ["Morris", "Felix"]
namedict = { "felines": rogue_cats }
for cat in namedict["felines"]:
    print cat, "purrs hypnotically."
print "--More--\nYou are controlled."
#-----------------------------
# As noted before, if you need an index, use enumerate() and not this:
for i in range(len(namedict["felines"])):
    print namedict["felines"][i], "purrs hypnotically."
#-----------------------------

# @@PLEAC@@_4.6
#-----------------------------
uniq = list(set(mylist))
#-----------------------------
# See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/259174
# for a more heavyweight version of a bag
seen = {}
for item in mylist:
    seen[item] = seen.get(item, 0) + 1

uniq = seen.keys()
#-----------------------------
seen = {}
uniq = []
for item in mylist:
    count = seen.get(item, 0)
    if count == 0:
        uniq.append(item)
    seen[item] = count + 1
#-----------------------------
# generate a list of users logged in, removing duplicates
import os
usernames = [line.split()[0] for line in os.popen("who")]
uniq = sorted(set(usernames))
print "users logged in:", " ".join(uniq)

# DON'T DO THIS:
import os
ucnt = {}
for line in os.popen("who"):
    username = line.split()[0]  # Get the first word
    ucnt[username] = ucnt.get(username, 0) + 1 # record the users' presence

# extract and print unique keys
users = ucnt.keys()
users.sort()
print "users logged in:", " ".join(users)
#-----------------------------

# @@PLEAC@@_4.7
#-----------------------------
# assume a_list and b_list are already loaded
aonly = [item for item in a_list if item not in b_list]

# A slightly more complex Pythonic version using sets - if you had a few
# lists, subtracting sets would be clearer than the listcomp version above
a_set = set(a_list)
b_set = set(b_list)
aonly = list(a_set - b_set)  # Elements in a_set but not in b_set

# DON'T DO THIS.
seen = {}                 # lookup table to test membership of B
aonly = []                # answer

#    build lookup table
for item in b_list:
    seen[item] = 1

#    find only elements in a_list and not in b_list
for item in a_list:
    if not item not in seen:
        # it's not in 'seen', so add to 'aonly'
        aonly.append(item)
#-----------------------------
# DON'T DO THIS.  There's lots of ways not to do it.
seen = {}   # lookup table
aonly = []  # answer

#     build lookup table - unnecessary and poor Python style
[seen.update({x: 1}) for x in b_list]

aonly = [item for item in a_list if item not in seen]

#-----------------------------
aonly = list(set(a_list))

# DON'T DO THIS.
seen = {}
aonly = []
for item in a_list:
    if item not in seen:
        aonly.append(item)
    seen[item] = 1                    # mark as seen
#-----------------------------
mydict["key1"] = 1
mydict["key2"] = 2
#-----------------------------
mydict[("key1", "key2")] = (1,2)
#-----------------------------
# DON'T DO THIS:
seen = dict.fromkeys(B.keys())

# DON'T DO THIS pre-2.3:
seen = {}
for term in B:
    seen[term] = None
#-----------------------------
# DON'T DO THIS:
seen = {}
for k, v in B:
    seen[k] = 1
#-----------------------------

# @@PLEAC@@_4.8
#-----------------------------
a = (1, 3, 5, 6, 7, 8)
b = (2, 3, 5, 7, 9)

a_set = set(a)
b_set = set(b)

union = a_set | b_set   # or a_set.union(b_set)
isect = a_set & b_set   # or a_set.intersection(b_set) 
diff = a_set ^ b_set    # or a_set.symmetric_difference(b_set)


# DON'T DO THIS:
union_list = []; isect_list = []; diff = []
union_dict = {}; isect_dict = {}
count = {}
#-----------------------------
# DON'T DO THIS:
for e in a:
    union_dict[e] = 1

for e in b:
    if union_dict.has_key(e):
        isect_dict[e] = 1
    union_dict[e] = 1

union_list = union_dict.keys()
isect_list = isect_dict.keys()
#-----------------------------
# DON'T DO THIS:
for e in a + b:
    if union.get(e, 0) == 0:
        isect[e] = 1
    union[e] = 1

union = union.keys()
isect = isect.keys()
#-----------------------------
# DON'T DO THIS:
count = {}
for e in a + b:
    count[e] = count.get(e, 0) + 1

union = []; isect = []; diff = []

for e in count.keys():
    union.append(e)
    if count[e] == 2:
        isect.append(e)
    else:
        diff.append(e)
#-----------------------------
# DON'T DO THIS:
isect = []; diff = []; union = []
count = {}
for e in a + b:
    count[e] = count.get(e, 0) + 1

for e, num in count.items():
    union.append(e)
    [None, diff, isect][num].append(e)
#-----------------------------

# @@PLEAC@@_4.9
#-----------------------------
# "append" for a single term and
# "extend" for many terms
mylist1.extend(mylist2)
#-----------------------------
mylist1 = mylist1 + mylist2
mylist1 += mylist2
#-----------------------------
members = ["Time", "Flies"]
initiates = ["An", "Arrow"]
members.extend(initiates)
# members is now ["Time", "Flies", "An", "Arrow"]
#-----------------------------
members[2:] = ["Like"] + initiates
print " ".join(members)
members[:1] = ["Fruit"]           # or members[1] = "Fruit"
members[-2:] = ["A", "Banana"]
print " ".join(members)
#-----------------------------
#=> Time Flies Like An Arrow
#=> Fruit Flies Like A Banana
#-----------------------------

# @@PLEAC@@_4.10
#-----------------------------
# reverse mylist into revlist

revlist = mylist[::-1]

# or
revlist = list(reversed(mylist))

# or pre-2.3
revlist = mylist[:]    # shallow copy
revlist.reverse()
#-----------------------------
for elem in reversed(mylist):
    pass # do something with elem

# or
for elem in mylist[::-1]:
    pass # do something with elem

# if you need the index and the list won't take too much memory:
for i, elem in reversed(list(enumerate(mylist))):
    pass

# If you absolutely must explicitly index:
for i in range(len(mylist)-1, -1, -1):
    pass
#-----------------------------
descending = sorted(users, reverse=True)
#-----------------------------

# @@PLEAC@@_4.11
#-----------------------------
# remove n elements from the front of mylist
mylist[:n] = []       # or del mylist[:n]

# remove n elements from front of mylist, saving them into front
front, mylist[:n] = mylist[:n], []

# remove 1 element from the front of mylist, saving it in front:
front = mylist.pop(0)

# remove n elements from the end of mylist
mylist[-n:] = []      # or del mylist[-n:]

# remove n elements from the end of mylist, saving them in end
end, mylist[-n:] = mylist[-n:], []

# remove 1 element from the end of mylist, saving it in end:
end = mylist.pop()

#-----------------------------
def shift2(terms):
    front = terms[:2]
    terms[:2] = []
    return front

def pop2(terms):
    back = terms[-2:]
    terms[-2:] = []
    return back
#-----------------------------
friends = "Peter Paul Mary Jim Tim".split()
this, that = shift2(friends)
# 'this' contains Peter, 'that' has Paul, and
# 'friends' has Mary, Jim, and Tim

beverages = "Dew Jolt Cola Sprite Fresca".split()
pair = pop2(beverages)
# pair[0] contains Sprite, pair[1] has Fresca,
# and 'beverages' has (Dew, Jolt, Cola)

# In general you probably shouldn't do things that way because it's 
# not clear from these calls that the lists are modified.
#-----------------------------

# @@PLEAC@@_4.12
for item in mylist:
    if criterion:
        pass    # do something with matched item
        break
else:
    pass     # unfound
#-----------------------------
for idx, elem in enumerate(mylist):
    if criterion:
        pass    # do something with elem found at mylist[idx]
        break
else:
    pass ## unfound
#-----------------------------
# Assuming employees are sorted high->low by wage.
for employee in employees:
    if employee.category == 'engineer':
        highest_engineer = employee
        break

print "Highest paid engineer is:", highest_engineer.name
#-----------------------------
# If you need the index, use enumerate:
for i, employee in enumerate(employees):
    if employee.category == 'engineer':
        highest_engineer = employee
        break
print "Highest paid engineer is: #%s - %s" % (i, highest_engineer.name)


# The following is rarely appropriate:
for i in range(len(mylist)):
    if criterion:
        pass    # do something
        break
else:
    pass ## not found
#-----------------------------


# @@PLEAC@@_4.13
matching = [term for term in mylist if test(term)]
#-----------------------------
matching = []
for term in mylist:
    if test(term):
        matching.append(term)
#-----------------------------
bigs = [num for num in nums if num > 1000000]
pigs = [user for (user, val) in users.items() if val > 1e7]
#-----------------------------
import os
matching = [line for line in os.popen("who") 
                if line.startswith("gnat ")]
#-----------------------------
engineers = [employee for employee in employees
                 if employee.position == "Engineer"]
#-----------------------------
secondary_assistance = [applicant for applicant in applicants
                            if 26000 <= applicant.income < 30000]
#-----------------------------

# @@PLEAC@@_4.14
sorted_list = sorted(unsorted_list)
#-----------------------------
# pids is an unsorted list of process IDs
import os, signal, time
for pid in sorted(pids):
    print pid

pid = raw_input("Select a process ID to kill: ")
try:
    pid = int(pid)
except ValueError:
    raise SystemExit("Exiting ... ")
os.kill(pid, signal.SIGTERM)
time.sleep(2)
try:
    os.kill(pid, signal.SIGKILL)
except OSError, err:
    if err.errno != 3:  # was it already killed?
        raise
#-----------------------------
descending = sorted(unsorted_list, reverse=True)
#-----------------------------
allnums = [4, 19, 8, 3]
allnums.sort(reverse=True)              # inplace
#-----------------------------
# pre 2.3
allnums.sort()                          # inplace
allnums.reverse()                       # inplace
#or
allnums = sorted(allnums, reverse=True) # reallocating
#-----------------------------

# @@PLEAC@@_4.15
ordered = sorted(unordered, cmp=compare)
#-----------------------------
ordered = sorted(unordered, key=compute)

# ...which is somewhat equivalent to: 
precomputed = [(compute(x), x) for x in unordered]
precomputed.sort(lambda a, b: cmp(a[0], b[0]))
ordered = [v for k,v in precomputed.items()]
#-----------------------------
# DON'T DO THIS.
def functional_sort(mylist, function):
    mylist.sort(function)
    return mylist

ordered = [v for k,v in functional_sort([(compute(x), x) for x in unordered],
                                        lambda a, b: cmp(a[0], b[0]))]
#-----------------------------
ordered = sorted(employees, key=lambda x: x.name)
#-----------------------------
for employee in sorted(employees, key=lambda x: x.name):
    print "%s earns $%s" % (employee.name, employee.salary)
#-----------------------------
sorted_employees = sorted(employees, key=lambda x: x.name):
for employee in sorted_employees:
    print "%s earns $%s" % (employee.name, employee.salary)

# load bonus
for employee in sorted_employees:
    if bonus(employee.ssn):
        print employee.name, "got a bonus!"
#-----------------------------
sorted_employees = sorted(employees, key=lambda x: (x.name, x.age)):
#-----------------------------
# NOTE: Python should allow access to the pwd fields by name
# as well as by position.
import pwd
# fetch all users
users = pwd.getpwall()
for user in sorted(users, key=lambda x: x[0]):
    print user[0]
#-----------------------------
sorted_list = sorted(names, key=lambda x: x[:1])
#-----------------------------
sorted_list = sorted(strings, key=len)
#-----------------------------
# DON'T DO THIS.
temp = [(len(s), s) for s in strings]
temp.sort(lambda a, b: cmp(a[0], b[0]))
sorted_list = [x[1] for x in temp]
#-----------------------------
# DON'T DO THIS.
def functional_sort(mylist, function):
    mylist.sort(function)
    return mylist

sorted_fields = [v for k,v in functional_sort(
              [(int(re.search(r"(\d+)", x).group(1)), x) for x in fields],
                                   lambda a, b: cmp(a[0], b[0]))]
#-----------------------------
entries = [line[:-1].split() for line in open("/etc/passwd")]

for entry in sorted(entries, key=lambda x: (x[3], x[2], x[0])):
    print entry
#-----------------------------

# @@PLEAC@@_4.16
#-----------------------------
import itertools
for process in itertools.cycle([1, 2, 3, 4, 5]):
    print "Handling process", process
    time.sleep(1)

# pre 2.3:
import time
class Circular(object):
    def __init__(self, data):
        assert len(data) >= 1, "Cannot use an empty list"
        self.data = data

    def __iter__(self):
        while True:
            for elem in self.data:
                yield elem

circular = Circular([1, 2, 3, 4, 5])

for process in circular:
    print "Handling process", process
    time.sleep(1)

# DON'T DO THIS. All those pops and appends mean that the list needs to be 
# constantly reallocated.  This is rather bad if your list is large:
import time
class Circular(object):
    def __init__(self, data):
        assert len(data) >= 1, "Cannot use an empty list"
        self.data = data

    def next(self):
        head = self.data.pop(0)
        self.data.append(head)
        return head

circular = Circular([1, 2, 3, 4, 5])
while True:
    process = circular.next()
    print "Handling process", process
    time.sleep(1)
#-----------------------------

# @@PLEAC@@_4.17
#-----------------------------
# generate a random permutation of mylist in place
import random
random.shuffle(mylist)
#-----------------------------

# @@PLEAC@@_4.18
#-----------------------------
import sys

def make_columns(mylist, screen_width=78):
    if mylist:
        maxlen = max([len(elem) for elem in mylist])
        maxlen += 1   # to make extra space

        cols = max(1, screen_width/maxlen) 
        rows = 1 + len(mylist)/cols

        # pre-create mask for faster computation
        mask = "%%-%ds " % (maxlen-1)

        for n in range(rows):
            row = [mask%elem
                       for elem in mylist[n::rows]]
            yield "".join(row).rstrip()

for row in make_columns(sys.stdin.readlines(), screen_width=50):
    print row


# A more literal translation
import sys

# subroutine to check whether at last item on line
def EOL(item):
    return (item+1) % cols == 0

# Might not be portable to non-linux systems
def getwinsize():
    # Use the curses module if installed
    try:
        import curses
        stdscr = curses.initscr()
        rows, cols = stdscr.getmaxyx()
        return cols
    except ImportError:
        pass

    # Nope, so deal with ioctl directly.  What value for TIOCGWINSZ?
    try:
        import termios
        TIOCGWINSZ = termios.TIOCGWINSZ
    except ImportError:
        TIOCGWINSZ = 0x40087468  # This is Linux specific

    import struct, fcntl
    s = struct.pack("HHHH", 0, 0, 0, 0)
    try:
        x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s)
    except IOError:
        return 80
    rows, cols = struct.unpack("HHHH", x)[:2]
    return cols

cols = getwinsize()

data = [s.rstrip() for s in sys.stdin.readlines()]
if not data:
    maxlen = 1
else:
    maxlen = max(map(len, data))

maxlen += 1       # to make extra space

# determine boundaries of screen
cols = (cols / maxlen) or 1
rows = (len(data)+cols) / cols

# pre-create mask for faster computation
mask = "%%-%ds " % (maxlen-1)

# now process each item, picking out proper piece for this position
for item in range(rows * cols):
    target = (item % cols) * rows + (item/cols)
    if target < len(data):
        piece = mask % data[target]
    else:
        piece = mask % ""
    if EOL(item):
        piece = piece.rstrip()  # don't blank-pad to EOL
    sys.stdout.write(piece)
    if EOL(item):
        sys.stdout.write("\n")

if EOL(item):
  sys.stdout.write("\n")
#-----------------------------

# @@PLEAC@@_4.19
#-----------------------------
def factorial(n):
    s = 1
    while n:
        s *= n
        n -= 1
    return s   
#-----------------------------
def permute(alist, blist=[]):
    if not alist:
        yield blist
    for i, elem in enumerate(alist):
        for elem in permute(alist[:i] + alist[i+1:], blist + [elem]):
            yield elem

for permutation in permute(range(4)):
    print permutation
#-----------------------------
# DON'T DO THIS
import fileinput

# Slightly modified from
#   http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66463
def print_list(alist, blist=[]):
    if not alist:
        print ' '.join(blist)
    for i in range(len(alist)):
        blist.append(