Computation for Physics 計算物理概論 Python Programming for Physicists

Computation for Physics
計算物理概論
Python Programming for Physicists
What is Python?
Python Programming Language
• Python is a general-purpose, high-level
programming language whose design philosophy
emphasizes code readability.
• Python supports multiple programming
paradigms, including object-oriented, imperative
and functional programming styles.
• Python features a fully dynamic type system and
automatic memory management,
PEP 20 “The Zen of Python”
PEP=Python Enhancement Proposal
• Beautiful is better than ugly.
• Explicit is better than implicit.
• Simple is better than complex.
• Complex is better than complicated.
• Readability counts.
Guido van Rossum
• Creator of python
• Benevolent Dictator for Life
Programming Language
• High-level language
• Low-level language
Compilation v.s. Interpretation
SOURCE
CODE
SOURCE
CODE
INTERPRETER
COMPILER
OBJECT
CODE
OUTPUT
EXECUTOR
OUTPUT
Python: Interpreted Language
• Interactive mode
– You type Python programs and the interpreter
displays the result.
• Script mode
– You store code in a file and use the interpreter to
execute the contents of the file.
PYTHON
DEVELOPMENT ENVIRONMENT
Python(x,y)
Scientific-oriented Python Distribution based on Qt and Spyder
• Python(x,y) is a free scientific and engineering
development software for numerical
computations, data analysis and data
visualization based on Python programming
language, Qt graphical user interfaces and
Spyder interactive scientific development
environment.
• https://code.google.com/p/pythonxy/
Download Python(x,y)
Download from one of the mirrors
Python(x,y) Plugins=Python Packages
• IDLE, IPython
– Development environment
• NumPy
– Multidimensional arrays support and basic operations
• SciPy
– Advanced math, signal processing, optimization, statistics
• Matplotlib
– 2D plotting library
• VPython
– Creation of 3D interactive models of physical systems
• SymPy
– Symbolic Mathematics Library
IDLE
Integrated DeveLopment Environment
• IDLE is the Python IDE built with the Tkinter GUI toolkit.
• IDLE has the following features:
– coded in 100% pure Python, using the Tkinter GUI toolkit
– cross-platform: works on Windows and Unix
– multi-window text editor with multiple undo, Python colorizing and
many other features, e.g. smart indent and call tips
– Python shell window (a.k.a. interactive interpreter)
– debugger (not complete, but you can set breakpoints, view and step)
• It is a part of the standard library and is described in the reference
manual: http://www.python.org/doc/current/lib/idle.html
• The source code for IDLE is included in the standard library. More
information on developing IDLE can be found at
http://docs.python.org/devguide/
IDLE
Integrated development environment
Python 2.7.3 (default, Apr 10 2012, 23:31:26)
[MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for
more information.
>>>
Start IDLE
開始所有程式python(x,y)IDLE
File  New Window
File  Save As
Run  Run Module
Python Shell
PYTHON
PROGRAMMING LANGUAGE
What is a Program?
• A sequence of instructions that specifies how
to perform a computation
• Basic instructions
– Input
– Output
– Math
– Conditional execution
– Repetition
What is Debugging
• Programming is error-prone
• Programming errors=bugs
• The process of tracking bugs down=debugging
Three kinds of errors
• Syntax errors
• Runtime errors
• Semantic errors
First Program=“Hello, World!”
• Traditionally, the first program you write in a
new language is to display the words
– “Hello, World”
print(‘Hello, World!’)
• Case-sensitive
Variables, Expressions, Statements
Variables, Values, Stage Diagram
• State diagram
– variables  value
•
•
•
•
message  ’And now for something’
n  17
pi  3.1415926535
z  1+3i
Python Keywords
False
None
True
and
as
assert
break
class
continue
def
del
elif
else
except
finally
for
from
global
if
import
in
is
lambda
nonlocal
not
or
pass
raise
return
try
while
with
yield
Assignment Statement
•
•
•
•
•
“=“ is an “assignment”
message = ’And now for something’
n = 17
pi = 3.1415926535
z = 1+3j
• 2*x=y
– Syntax Error
data types
• integer=integer number
• float=floating point number
• complex=complex floating point number
• str=string
>>> type(17)
<type ‘int’>
>>> type(3.2)
<type ‘float’>
>>> type(1+3j)
<type ‘complex’>
>>> type(‘Hello, World!’)
<type ‘str’>
data types
• integer=integer number
• float=floating point number
• complex=complex floating point number
• str=string
>>> type(n)
<type ‘int’>
>>> type(pi)
<type ‘float’>
>>> type(z)
<type ‘complex’>
>>> type(message)
<type ‘str’>
Dynamically Typed
>>> x=3
# x is of integer type
>>> x=3.0
# x is of float type
>>> x=‘blah’
# x is of string type
>>> x=[3,3.0,’blah’]
# x is of list type
Variable Names & Keywords
• Keywords
– The interpreter uses keywords to recognize the
structure of the program, and they cannot be used
as variable names
– and
– if
– else
– while
– …etc
Operators & Operand
• +
– addition: x+y
• – subtraction: x-y
• *
– multiplication: x*y
• /
– division: x/y
• **
– exponentizaton x**y
• //
– the integer part of x/y: 14.5//3.64.0, 14.5//3.73.0
• %
– module=remainder after x/y: 1.5%0.40.3
Expression & Statement
• An expression is a combination of values,
variables, and operators.
–x
– x+17
• A statement is a unit of code that the Python
interpreter can execute.
– x=3
– print(‘Hello’)
Interactive & Script Mode
• Interactive mode
• Script mode
• Major difference
– Python actually evaluates the expression, but it
doesn’t display the value unless you tell it to
Order of Operations
• Rue of precedence
•
•
•
•
Parentheses
Exponentiation
Multiplication and Division
Left to right for operators with the same
precedence
Comments
• It is a good idea to add notes to your programs
to explain in natural language what the
program is doing.
• Comments start with the # symbol
# compute the velocity
v=dx / dt
v=dx / dt
# compute the velocity
x=1
print(x)
• variable
• assignment statement
Variables Types
• Integer
– x=1
• Float
– x=1.5
– x=1.0
– x=float(1)
• Complex
– x=1.5+2.0j
– x=1.5+0j
– x=complex(1.5)
string
String literals can be written enclosed in either two
single or two double quotes
x='This is a string'
x="This is a string"
y='1.234' # create a string with value ‘1.234’
y=1.234 # create a real number with value 1.234
z='x=1' # create a string with value ‘x=1’
type conversion functions
string to integer
>>> int(‘32’)
32
>>> int(3.999)
3
>>> int(-2.3)
-2
>>> int('Hello')
ValueError: invalid literal for int() with
base 10: 'hello'
>>> int('3.999')
ValueError: invalid literal for int() with
base 10: '3.999'
Help(int)
>>>help(int)
Help on class int in module __builtin__:
class int(object)
| int(x[, base]) -> integer
|
| Convert a string or number to an integer, if possible. A floating point
| argument will be truncated towards zero (this does not include a string
| representation of a floating point number!) When converting a string, use
| the optional base. It is an error to supply a base when converting a
| non-string. If base is zero, the proper base is guessed based on the
| string content. If the argument is outside the integer range a
| long object will be returned instead.
Type conversion functions
string to float
>>> float(‘32’)
32.0
>>> (‘3.14159’)
3.14159
>>> int(float(‘3.999’))
3
class float(object)
| float(x) -> floating point number
|
| Convert a string or number to a
floating point number, if possible.
type conversion functions
‘blah’ to complex
>>> complex(32)
(32+0j)
>>> complex(‘3.2’)
(3.2+0j)
>>> complex(3+j)
NameError: name 'j' is not defined
>>> complex(3+0j)
(3+0j)
>>> complex(3+1j)
(3+1j)
>>> complex(‘3+j’)
(3+1j)
>>> complex(‘3+0j’)
(3+0j)
>>> complex(‘3+1j’)
(3+1j)
Output Statements
>>>
>>>
1
>>>
>>>
>>>
1 2
>>>
The
x = 1
print(x)
x=1
y=2
print(x,y)
print("The value of x is", x, "and y=",y)
value of x is 1 and y=2
Input Statement
>>> x = input("Enter the value of x:”)
(The computer will stop and wait)
>>> print("The value of x is ",x)
>>> x = input("Enter the value of x:")
(You enter "Hello")
>>> print("The value of x is ",x)
The value of x is Hello
Input Statement
Convert input string to float
temp = input("Enter the value of x: ")
x=float(temp)
print("The value of x is ",x)
x = float(input("Enter the value of x: "))
print("The value of x is ",x)
Python 3 v.s. Python 2
Include following statement in your code
from __future__ import division, print_function
• Division return a floating value
– 3/2  1
– 3/2  1.5
– 4/2  2
– 4/2  2.0
# python 2
# python 3
# python 2
#python 3
• Print is a statement
– print "The energy is ", E
– print("The energy is ",E)
# python 2
# python 3
Python 3 v.s. Python 2
• Input returns a string
– In Python 3 the input function always returns a
string
– x=input()
– you enter ‘2.5’
– type(x)  <type ‘float’>
# python 2
– type(x)  <type ‘str’>
# python 3
– x=raw_input()
– type(x)  <type ‘str’>
# python 2
Python 3 v.s. Python 2
• Iterators v.s. List
– range(10)  [0,1,2,3,4,5,6,7,8,9] # python 2
– range(10)  range(0,10) # python 3
– list(range(10)) [0,1,2,3,4,5,6,7,8,9] # python 3
– x = [1,2,3]
– map(log,x)  [0.0, 0.69…, 1.09…] # python 2
– map(log,x)  <map object at …> # python 3
– list(map(log,x))  [0.0, 0.69…, 1.09…] # python 3
“x=a+b” is an assignment statement!
• x = 1
–x  1
• x = x+1
– x  x+1
– temp = x+1
– x=temp
• x = x**2 – 2
– x  x**2 - 2
Python Modifiers
• x += 1
–x = x + 1
• x -= 4
–x = x - 4
• x *= -2.6
– x = x*(-2.6)
• x /= 5*y
– x = x / (5*y)
• x //= 3.4
– x = x // 3.4
Assignment of Multiple Variables
• x, y = 1, 2.5
–x = 1
– y = 2.5
• x, y = 2*z+1, (x+y)/3
– temp1 = 2*z+1
– temp2 = (x+y)/3
– x = temp1
– y = temp2
Assignment of Multiple Variables
You can interchange of values of two variables
• x, y = y, x
– temp = y
–y = x
– x = temp
Try: A ball dropped from a tower
• Write a program to ask the user to enter h the
height of the tower and a time interval t, then
prints the height of the ball above the ground
at time t after it is dropped.
A ball dropped from a tower
Simple version
h = float(input(“Enter the height of the tower: "))
t = float(input("Enter the time interval: "))
s = 9.81*t**2/2
print("The height of the ball is", h-s, "meters")
To make it more readable
g = 9.81
s = g*t**2/2
Another ball dropped from a tower
• A ball is dropped from a tower of height h
with initial velocity zero. Write a program that
asks the user to enter the height in meters
and the calculates and prints the time the ball
takes until it hits the ground.
Functions, Packages, & Modules
>>> from math import log
>>> x = log(2.5)
log(...)
log(x[, base])
Return the logarithm of x to the given base.
If the base not specified, returns the natural
logarithm (base e) of x.
math—Mathematical Functions
from math import *
Functions:
• log
• log10
• exp
• sin, cos, tan
• asin, acos, atan
• sinh, cosh, tanh
• sqrt
Constants:
• pi
• e
Try: Converting polar coordinates
• Get the user to enter the value of r and θ
• Convert those values to Cartesian coordinates
• Print out the results
Converting polar coordinates
from math import sin, cos, pi
r = float(raw_input("Enter r: "))
d = float(raw_input("Enter theta in
degrees: "))
theta = d*pi/180
x = r*cos(theta)
y = r*sin(theta)
print("x= ",x, "y= ",y)
Comment Statements
from math import sin, cos, pi
# Ask the user for the values of the radius and angle
r = float(input(“Enter r: “))
d = float(input(“Enter theta in degrees: “))
# Convert the angle to radians
theta = d*pi/180
# Calculate the equivalent Cartesian coordinates
x = r*cos(theta)
y = r*sin(theta)
print(“x= “,x,”y= “,y) # Print out the results
Multiline \
1+2\
3
help
>>> help(sum)
Help on built-in function sum in module __builtin__:
sum(...)
sum(sequence[, start]) -> value
Returns the sum of a sequence of numbers (NOT
strings) plus the value of parameter 'start' (which
defaults to 0). When the sequence is empty, returns
start.
Branching
Boolean expressions
>>>5 == 5
True
>>>5 == 6
False
>>>type(True)
<type ‘bool’>
>>>type(False)
<type ‘boo’>
Relational Operators
x
x
x
x
x
x
== y
!= y
> y
< y
>= y
<= y
#
#
#
#
#
#
x
x
x
x
x
x
is
is
is
is
is
is
equal to y
not equal to
greater than
less than y
greater than
less than or
y
y
or equal to y
equal to y
Logical Operators
• and
• or
• not
>>> 3>1 and 3>2
True
>>> 3>1 or 3<2
Ture
>> not (3>1)
False
Numerical Values of True & False
>>>int(True)
1
>>>int(False)
0
>>>0==False
True
>>>1==True
True
>>>2==True
False
Conditional Execution
if x > 0: Condition
print(‘x is positive’)
print(‘Hello, World!’)
Compound statements
Alternative Execution
if x % 2 ==0:
print(‘x is even’)
else:
print(‘x is odd’)
Chained Conditionals
if x < y:
print(‘x is less than y’)
elif x > y:
print(‘x is greater than y’)
else:
print(‘x and y are equal’)
elif=“else if”
Chained Conditionals
if choice == ‘a’
draw_a()
elif choice == ‘b’
draw_b()
elif choice == ‘c’
draw_c()
function call
Nested Conditionals
if x == y:
print(‘x and y are equal’)
else:
if x < y:
print(‘x is less than y’)
else:
print(‘x is greater than y’)
Use Logic Operators to Simplify
if 0 < x:
if x < 10:
print(‘x is ……’)
if 0 < x and x < 10:
print(‘x is ……’)
If (anything):
•
•
•
•
0 is interpreted as False
Any other number is interpreted as True
Empty lists and objects return False
Non-empty lists and objects return True
if (anything):
>>>if 0:
...
print(‘x’)
>>>if 1:
...
print(‘x’)
x
>>>if 1.1:
...
print(‘x’)
x
>>>d=[]
>>>if d:
...
print(‘x’)
>>>d=[1,2]
>>>if 1.1:
...
print(‘x’)
x
Iteration
The ‘While’ Statement
n=10
while n > 0:
print(n)
n = n-1
Loop
You should change the value of one or
more variables, otherwise it will become an
infinite loop
Will the Loop Terminates for all n?
n = 3
while n != 1:
print(n)
if n%2 == 0:
n = n//2
else:
n = n*3+1
3,10,5,16,8,4,2,1
# n is even
# n is odd
Break
while True:
line = raw_input(‘> ‘)
if line == ‘done’:
break
print(line)
print(‘finished’)
Try: Fibonacci Sequence
• Sequence of integers in which each is the sum
of previous two, starting with 1,1
• 1, 1, 2, 3, 5, 8, 13, 21
f1 = 1
f2 = 1
next = f1 + f2
while f1 <= 1000:
print(f1)
f1 = f2
f2 = next
next = f1 + f2
Fibonacci Sequence, Simplified
f1, f2 = 1, 1
while f1 < 1000:
print(f1)
f1, f2 = f2, f1+f2
Try: Catalan Numbers
• 𝐶0 = 1, 𝐶𝑛+1 =
4𝑛+2
𝐶𝑛
𝑛+2
• Write a program that prints in increasing order
all Catalan numbers less than or equal to one
billion
Try: Square Roots
• Newton’s method to find the square root of a
– Start from any estimate x
– Compute a better estimate 𝑦 =
– Repeat until it converges
• This is an algorithm
𝑥+𝑎/𝑥
2
Newton’s Method, Interactively
>>>a=4.0
>>>x=3.0
>>>y=(x+a/x)/2
>>>print(y)
2.16666666667
>>>x=y
>>>y=(x+a/x)/2
>>>print(y)
2.00641025641
Square Roots
while True:
print(x)
y = (x + a/x ) / 2
if y == x:
break
x = y
Square Roots (Safer Check)
epsilon = 0.0000001
while True:
print(x)
y = (x + a/x ) / 2
if abs(y-x) < epsilon:
break
x = y
User-Defined Functions
Factorial Function
Argument
def factorial(n):
f = 1.0
for k in range(1,n+1)
f *= k
return f
Local variables
a = factorial(10)
b = factorial(r+2*s)
Distance Function
Multiple arguments
def distance(r, theta, z):
x = r*cos(theta)
y = r*sin(theta)
d = sqrt(x**2+y**2+z**2)
return d
D = distance(2.0,0.1,-1.5)
Use “def” Statement
to Define a Function
def add(arg1, arg2)
x=arg1+arg2
return x
def power(x,y)
if x <=0:
return 0
else
return x**y
Return Arbitrary Type
def cartesian(r,theta):
x = r*cos(theta)
y = r*sin(theta)
position = [x,y]
return position
def f(z):
# Some calculations here...
return a,b
x,y = f(1)
Return No Value
def print_vector(r):
print("(",r[0],r[1],r[2],")")
Map User-defined Function to a List
def f(x):
return 2*x-1
newlist = list(map(f,oldlist))
Try: Square Root Function
•
•
•
•
Write your own square root function
Input a and epsilon
Output square root of a
Compare the result with built-in function sqrt()
Lists
Build-in Type: List
• List=[element1, elemen2, element3,…]
r = [ 1, 1, 2, 3, 5, 8, 13, 21]
print(r)
[ 1, 1, 2, 3, 5, 8, 13, 21]
x = 1.0
y = 1.5
z = -2.2
r = [x,y,z]
r = [ 2*x, x+y, z/sqrt(x**2+y**2) ]
Access List Elements
>>> r = [ 1.0, 1.5, -2.2]
>>> r[0]
The index number starts at zero
1.0
>>> r[4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> r[-1]
-2.2
Modify List Elements
>>>r = [ 1.0, 1.5, -2.2]
>>>r[0] = 3.5
1.0
>>>r
[3.5, 1.5, -2.2]
List Operations
>>>
>>>
>>>
>>>
[1,
a = [1, 2, 3]
b = [4, 5, 6]
c = a + b
print(c)
2, 3, 4, 5, 6]
>>>
[0,
>>>
[1,
[0] * 4
0, 0, 0]
[1, 2, 3] * 3
2, 3, 1, 2, 3, 1, 2, 3]
List Functions
• Functions that take ‘list’ as an argument
>>> r = [ 1.0, 1.5, -2.2]
>>> sum(r)
0.3
>>> sum(r)/len(r)
0.1
Meta-Function: map
• Apply functions to all elements of a list
• map(log, r) take the natural logarithm of each
element of a list r
from math import log
r = [ 1.0, 1.5, 2.2 ]
logr = list(map(log,r))
print(logr)
[0.0, 0.4054651081, 0.7884573603]|
List Methods: Add an Element
>>>r = [ 1.0, 1.5, -2.2 ]
>>>x = 0.8
>>>r.append(2*x+1)
>>>print(r)
[1.0, 1.5, -2.2, 2.6]
>>>t1 = [‘a’, ‘b’, ‘c’]
>>>t2 = [‘d’, ‘e’]
>>>t1.extend(t2)
>>>print(t1)
[‘a’, ‘b’, ‘c’, ‘d’, ‘e’]
Starts from an Empty List
>>>r=[]
>>>r.append(1.0)
>>>r.append(1.5)
>>>r.append(-2.2)
>>>print(r)
[1.0, 1.5, -2.2]
List Methods: Remove am Element
>>>r = [ 1.0, 1.5, -2.2, 2.6 ]
>>>r.pop()
>>>print(r)
[1.0, 1.5, -2.2]
>>>t =[‘a’, ‘b’, ‘c’]
>>>x = t.pop(1)
>>>print(t)
>>>print(x)
Remove a known Element
>>>t = [‘a’, ‘b’, ‘c’]
>>>t.remove(‘b’)
>>>print(t)
[‘a’, ‘c’]
Traveling a List: For Loop
r = [ 1, 3, 5 ]
for n in r:
print(n)
print(2*n)
print("Finished")
1
2
3
6
5
10
Finished
Traveling a List: For Loop
for i in [3, ‘Hello’, 9.5]:
print i
3
Hello
9.5
for x in []:
print(‘This never happens’)
Run Some Code a Number of Times
r = [0, 1, 2, 3, 4 ]
for i in r:
blah
blah
blah
print(‘Finished’)
If we want to loop 10000000 times?
Built-in Function: range()
range(5)
gives [0, 1, 2, 3, 4]
range(2,8)
gives [2, 3, 4, 5, 6, 7]
range(2,20,3)
gives [2, 5, 8, 11, 14, 17]
range(20,2,-3)
gives [20, 17, 14, 11, 8, 5]
range(integer)
p = 10
q = 2
range(p/q) (error!)
range(p//q) (ok!)
Performing a Sum
• Evaluate the sum 𝑠 = 100
𝑘=1 1/𝑘
s = 0.0
for k in range(1,101):
s += 1/k
print(s)
List Slices: list[lower:upper:step]
• Inclusive lower element index (default=0)
• Exclusive upper element index (default=Length)
>>>l=[1,2,3,4,5]
>>>l[0:4]
[1,2,3,4]
>>>l[0:4:2]
[1,3]
>>>l[:4]
[1,2,3,4]
>>>l[2:]
[3,4,5]
>>>l[::2]
[1,3,5]
List Comprehensions
>>>[i*i for i in range(5)]
[0, 1, 4, 9, 16]
>>>[k*5 for k in [4,8,9]]
[20, 40, 45]
>>>[q**(0.5) for q in [4,6,9]]
[2.0, 3.0, 4.0]
>>>[ k%2 == 0 for k in range(5)]
[True, False, True, False, True]
Examples
Try: Emission Line of Hydrogen
1
1
1
=𝑅
− 2
2
𝜆
𝑚
𝑛
R = 1.097e-2
for m in [1,2,3]:
print("Series for m =", m)
for k in [1,2,3,4,5]:
n=m+k
invlambda = R*(1/m**2-1/n**2)
print(" ",1/invlambda," nm")
Use range() Function to Simplify
R = 1.097e-2
for m in range(1,4):
print("Series for m =",m)
for n in range(m+1,m+6):
invlambda = R*(1/m**2-1/n**2)
print(" ",1/invlambda," nm")
Try: The Madelung Constant
• In condensed matter physics the Madelung constant gives
the total electric potential felt by an atom in a solid. It
depends on the charges on the other atoms nearby and
their locations.
• Consider for instance solid sodium chloride—table salt. The
sodium chloride crystal has atoms arranged on a cubic
lattice, but with alternating sodium and chlorine atoms, the
sodium ones having a single positive charge +e and the
chlorine ones a single negative charge −e, where e is the
charge on the electron.
• If we label each position on the lattice by three integer
coordinates (i, j, k), then the sodium atoms fall at positions
where i + j + k is even, and the chlorine atoms at positions
where i + j + k is odd.
Try: The Madelung Constant
• Consider a sodium atom at the origin, i = j = k = 0, the spacing of atoms on the
lattice is a. The distance from the origin to the atom at position (i, j, k) is
𝑖𝑎
2
+ 𝑗𝑎
2
+ 𝑘𝑎
2
= 𝑎 𝑖2 + 𝑗2 + 𝑘2
• The potential at the origin created by such an atom is
𝑉 𝑖, 𝑗, 𝑘 = ±
𝑒
4𝜋𝜀0 𝑎 𝑖 2 + 𝑗 2 + 𝑘 2
• The sign of the expression depending on whether i + j + k is even or odd. The total
potential felt by the sodium atom is the sum of this quantity over all other atoms.
Assume a cubic box around the sodium at the origin, with L atoms in all directions.
𝐿
𝑉𝑡𝑜𝑡 =
𝑉 𝑖, 𝑗, 𝑘 =
𝑖,𝑗,𝑘=−𝐿,(𝑖,𝑗,𝑘) ≠(0,0,0)
𝑒
𝑀
4𝜋𝜀0 𝑎
• M=Madelung constant is the value of M when L → ∞, but one can get a good
approximation just by using a large value of L.
Try: The Madelung Constant
• Write a program to calculate and print the
Madelung constant for sodium chloride. Use
as large a value of L as you can, while still
having your program run in reasonable time—
say in a minute or less.
The Semi-Empirical Mass Formula
•
A formula for calculating the approximate nuclear binding energy B of an atomic nucleus
with atomic number Z and mass number A:
𝐵 = 𝑎1 𝐴 − 𝑎2 𝐴
•
•
•
•
•
2/3
𝑍2
𝐴 − 2𝑍
− 𝑎3 1/3 − 𝑎4
𝐴
𝐴
2
𝑎5
+ 1/2
𝐴
0
if 𝐴 is odd
𝑎1 = 15.6, 𝑎2 = 17.23, 𝑎3 = 0.75, 𝑎4 = 93.2. 𝑎5 = 12.0 𝐴 and 𝑍 are both even
−12.0 𝐴 is even and 𝑍 is odd
Write a program that takes as its input the values of A and Z, and prints out the binding
energy for the corresponding atom. Use your program to find the binding energy of an atom
with A = 58 and Z = 28. (Hint: Answer is around 490 MeV.)
Modify your program to print out not the total binding energy B, but the binding energy per
nucleon, which is B/A.
Modify your program so that it takes as input just a single value of the atomic number Z and
then goes through all values of A from A = Z to A = 3Z, to find the one that has the largest
binding energy per nucleon. Have your program print out the value of A for this most stable
nucleus and the value of the binding energy per nucleon.
Modify your program so that, instead of taking Z as input, it runs through all values of Z from
1 to 100 and prints out the most stable value of A for each one. At what value of Z does the
maximum binding energy per nucleon occur?
Try: Prime Factors and Prime Numbers
• Suppose we have an integer n and we want to
know its prime factors. The prime factors can
be calculated relatively easily by dividing
repeatedly by all integers from 2 up to n and
checking to see if the remainder is zero.
Try: Factor List
•
•
•
•
Input an integer n
Output the factor list
Input=28
Output=[2,2,7]
Try: Factorlist
def factor(n):
factorlist = []
k = 2
while k <= n:
while n%k == 0:
factorlist.append(k)
n //= k #n=n//k
k += 1
return factorlist
Try: Optimization: Find All the Primes
• Finds all the primes up to n.
• Create a list to store the primes, which starts
out with just the one prime number 2 in it.
• Look over n=2 to 10000
• Call factor(n)
• If the factorlist contains only one element, the
n is a prime. Add n to the prime list.
• Slow and stupid, any better way?
Try: Optimization: Find All the Primes
• Finds all the primes up to n.
• Create a list to store the primes, which starts out with
just the one prime number 2 in it.
• For each number n from 3 to 10000 check whether the
number is divisible by any of the primes in the list up to
and including . As soon as you find a single prime factor
you can stop checking the rest of them—you know n is
not a prime. If you find no prime factors or less then n
is prime and you should add it to the list.
• You can print out the list all in one go at the end of the
program, or you can print out the individual numbers
as you find them.
Time your code!
•
•
•
•
•
•
•
from time import *
start_time=time()
blah
blah
blah
end_time=time()
used_time=end_time-start_time
Good Programming Style
Good Programming Style
•
•
•
•
•
•
•
•
•
Include comments in your programs.
Use meaningful variable names.
Use the right types of variables.
Import functions first.
Give your constants names.
Employ user-defined functions, where appropriate.
Print out partial results and updates throughout your program.
Lay out your programs clearly.
Don’t make your programs unnecessarily complicated.
Dictionary
Dictionary {Key:Value}
>>>eng2sp = dict()
>>>print(eng2sp)
{}
>>>eng2sp[‘one’]='uno'
>>>print(eng2sp)
{'one':'uno'}
>>>eng2sp={'one':'uno','two':'dos','three':'tres'}
>>>print(eng2sp}
{'one':'uno','three':'tres','two':'dos'}
>>>print(eng2sp['two'])
dos
>>>print(eng2sp['four'])
KeyError: 'four'
>>>len(eng2sp)
3
Something as a Key or a Value?
>>>'one' in eng2sp
True
>>>'uno' in eng2sp
False
>>>vals = eng2sp.values()
>>>'uno' in vals
True
• Python2's "has_key()" is moved in Python3
Dictionary as a Set of Counters
• Want to count how many times each letter appears
in a given string.
• Create 26 variables, one for each letter of the
alphabet.
• Create a list with 26 elements. Convert each
character to a number which is used as an index.
• Create a a dictionary with characters as key and
counter as values.
Dictionary as a Set of Counters
def histogram(s):
d = dict()
for c in s:
if c not in d:
d[c] = 1
else:
d[c] += 1
return d
>>>h = histogram('brontosaurus')
>>>print(h)
{'a':1, 'b':1, 'o':2, 'n':1, 's':2, 'r':2 ,
'u':2, 't':1 }
Dictionaries and Lists
def invert_dict(d):
inverse = dict()
for key in d:
val = d[key]
if val not in inverse:
inverse[val] = [key]
else:
inverse[val].append(key)
return inverse
>>> hist = histogram('parrot')
>>> print hist
{'a': 1, 'p': 1, 'r': 2, 't': 1, 'o': 1}
>>> inverse = invert_dict(hist)
>>> print inverse
{1: ['a', 'p', 't', 'o'], 2: ['r']}
Fibonacci Function
• fibonacci(0)=0
• fibonacci(1)=1
• fibonacci(n)=ibonacci(n-1)+ibonacci(n-2)
def fibonacci (n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-1) +
fibonacci(n-2)
Keep Tracks of the Values
known = {0:0, 1:1}
def fibonacci(n):
if n in known:
return known[n]
res = fibonacci(n-1) + fibonacci(n-2)
known[n] = res
return res
Tuples
Tuples are Immutable
• A tuple is a sequence of values. The values can
be any type, and they are indexed by integers,
so in that respect tuples are a lot like lists. The
important difference is that tuples are
immutable.
tuple=a comma-separated list of values
>>>t = 'a', 'b', 'c', 'd', 'e'
>>>t = ('a', 'b', 'c', 'd', 'e')
>>>t1 = 'a',
>>>type(t1)
<type 'tuple'>
>>>t2 = ('a')
>>>type(t2)
<type 'str'>
Built-in Function: tuple
>>>t = tuple()
>>>print(t)
()
>>>t = tuple('lupins')
>>>print(t)
('l', 'u', 'p', 'i', 'n', 's')
>>>t = tuple(['l','u','p','i','n',s'])
>>>print(t)
('l', 'u', 'p', 'i', 'n', 's')
Most List Operators also Work on Tuples
>>> t = ('a', 'b', 'c', 'd', 'e')
>>> print t[0]
'a'
>>> print t[1:3]
('b', 'c')
>>> t[0] = 'A'
TypeError: object doesn't support item assignment
>>> t = ('A',) + t[1:]
>>> print t
('A', 'b', 'c', 'd', 'e')
Tuple Assignment
>>>a, b = b, a
>>>a, b = 1, 2, 3
ValueError: too many values to unpack
>>>addr = '[email protected]'
>>>uname,domain = addr.split('@')
>>>print(uname)
monty
>>>print(domain)
python.org
Tuples ad Return Values
>>>t = divmod(7,3)
>>>print(t)
(2, 1)
>>>quot, rem = divmod(7,3)
>>>print(quot)
2
>>>print(rem)
1
def min_max(t):
return min(t), max(t)
Variable-length argument tuples
• Functions can take a variable number of arguments
• A parameter name that begins with * gathers
arguments into a tuple
def printall(*agr):
print args
>>>printall(1, 2.0, '3')
(1, 2.0, '3')
Scatter
• divmod takes exactly two arguments; it
doesn't work with a tuple
• But you can scatter the tuple
>>>t = (7,3)
>>>divmod(t)
TypeError: divmod expected 2 arguments, got 1
>>>divmod(*t)
(2, 1)
Lists and Tuples
• zip is a built-in function that takes two or more
sequences and "zip" them into a list/iterator of
tuples where each tuple contains one element from
each sequence.
>>>s = 'abc'
>>>t = [0,1,2]
>>>zip(s, t)
[('a',0),('b',1),('c',2)]
>>>zip('Anne', 'Elk')
[('A','E'),('n','l'),('n','k')]
Module
• from xxx import yyy
• from xxx import *
• The advantage of importing everything from the
module is that your code can be more concise.
• import xxx
• The disadvantage is that there might be conflicts
between names defined in different modules, or
between a name from a module and one of your
variables.
Module
• import random
– random.random()
• from random import seed
– random()
• from random import *
– random()
math
• Constants
– pi
–e
math
•
•
•
•
•
•
•
•
Power and logarithmic functions
exp(x)
expml(x)
log(x[,base])
log1p(x)
log2(x)
log10(x)
pow(x,y)
random
•
seed(a=None, version=2)
– Initialize the random number generator.
•
getstate()
– Return an object capturing the current internal state of the generator.
•
setstate(state)
– Restores the internal state of the generator to what it was at the time getstate() was called.
•
random()
– Return the next random floating point number in the range [0.0, 1.0).
•
uniform(a,b)
– Return a random floating point number N such that a <= N <= b for a <= b and b <= N <= a for b < a.
•
•
randrange(stop)
randrange(start,stop[,step])
– Return a randomly selected element from range(start, stop, step).
•
randint(a,b)
– Return a random integer N such that a <= N <= b. Alias for randrange(a, b+1).
•
choice(seq)
– Return a random element from the non-empty sequence seq.
•
shuffle(x[,random])
– Shuffle the sequence x in place.
•
sample(polulation,k)
– Return a k length list of unique elements chosen from the population sequence or set.
Examples and Recipes
>>>random()
0.37444887175646646
>>>uniform(1,10)
1.1800146073117523
>>>randrange(10)
7
>>>randrange(0,101,2)
26
>>>choice('abcdefghjk')
'c'
>>>items=[1,2,3,4,5,6,7]
>>>shuffle(items)
>>>items
[7,3,2,5,6,4,1]
>>>sample([1,2,3,4,5],3)
[4,1,5]
Global Variables
Case Study: Data Structure Selection
Class and Objects
User-defined Types: Points
• Create a type called Point that represents a
point in 2D space.
• In mathematical notation, points are written
as (x,y)
Class
• A user-defined type is also called a class
• Define a class named Point creates a class object
>>>class Point(object):
>>>"""Represents a pint in 2D space."""
>>>print(Point)
<class '__main__.Point'>
>>>blank = Point()
>>>print(blank)
<__main__.Point object at 0x10c180890>
Class and Objects
• Creating a new object is called instantiation,
• Object is an instance of the class.
• In the example above
– "blank" is an instance of the class "Point".
• When you print an instance Python tells you
– What class it belongs.
– Where it is stored in memory.
Attributes
• You can assign values to an instance using dot
notation.
>>>blank.x = 3.0
>>>blank.y = 4.0
• You can read the value of an attribute
>>>print(blank.y)
4.0
>>>x=blank.x
>>>print(blank.x)
3.0
Attributes
• You can use dot notation as part of expression
>>>print('(%g %g)' % (blank.x, blank.y)
>>>(3.0, 4.0)
• You can pass an instance as an argument
>>>def print_point(p):
>>> print('(%g, %g)' % (p.x, p.y))
>>>print_point(blank)
(3.0, 4.0)
User-defined Types: Rectangles
• Create a type called Rectangle that represents
a rectangle in 2D space.
• In mathematical notation, we need to specify
– lower-left corner (as a Point)
– width
– height
Rectangles
>>>class Rectangle(object):
>>> """Represents a rectangle.
>>>
>>> attributes: width, height, corner
>>> """
>>>box = Rectangle()
>>>box.width = 100.0
>>>box.height = 200.0
>>>box.corner = Point()
>>>box.corner.x = 0.0
>>>box.corner.y = 0.0
Object Diagram
Rectangle
box
width
height
corner
100
100
Point
x
y
0.0
0.0
Instances as Return Values
• find_center takes a Rectangle as an argument and
returns a Point that contains the coordinates of the
center of the Rectangle.
>>>def find_center(rect):
>>> p = Point()
>>> p.x = rect.corner.x + rect.width/2.0
>>> p.y = rect.corner.y + rect.height/2.0
>>> return p
>>>center = find_center(box)
>>>print_point(center)
(50.0, 100.0)
Objects are Mutable
• You can change the state of an object by making
an assignment to one of its attributes.
• You can write functions that modify objects.
>>>def grow_rectangle(rect,dwidth,dheight):
>>> rect.width += dwidth
>>> rect.height += dheight
>>>print(box.width,box.height)
100
>>>grow_rectangle(box,50,100)
200
>>>print(box.width,box.height)
(150,300)
Copying
>>>p1 = Point()
>>>p1.x = 3.0
>>>p1.y = 4.0
>>>pp1 = p1
>>>print_point(p1)
(3.0, 4.0)
>>>print_point(pp1)
(3.0, 4.0)
>>>print( p1 is pp1 )
True
>>>print( p1 == pp1 )
True
Object Diagram
Point
p1
pp1
x
3.0
y
4.0
Copying
>>>import copy
>>>p1 = Point()
>>>p1.x = 3.0
>>>p1.y = 4.0
>>>p2 = copy.copy(p1)
>>>print_point(p1)
(3.0, 4.0)
>>>print_point(pp1)
(3.0, 4.0)
>>>print( p1 is p2 )
False
>>>print( p1 == p2 )
False
Object Diagram
Point
p1
x
3.0
y
4.0
Copy
Point
p2
x
3.0
y
4.0
Copying
>>>box2 = copy.copy(box)
>>>print(box2 is box)
False
>>>print(box2.corner is box.corner)
True
Object Diagram
Rectangle
box
width
height
100
200
corner
x
y
Rectangle
box2
width
height
corner
Point
100
200
0.0
0.0
>>>box3 = copy.deepcopy(box)
>>>print(box3 is box)
False
>>>print(box3.corner is box.corner)
False
Object Diagram
Rectangle
box
width
height
100
200
corner
x
y
Rectangle
box3
width
height
corner
Point
100
200
0.0
0.0
Point
x
y
0.0
0.0
Debugging
• If you try to access an attribute that doesn't exist, you get
an AttributeError:
>>>p = Point()
>>>print(p.z)
AttributeError: Point instance has no attribute 'z'
• If you are not sure what type an object is you can ask:
>>>type(p)
<type '__main__.Point>
• If you are not sure whether an object has a particular
attribute, you can use the build-in function hasattr:
>>>hasattr(p, 'x')
True
>>>hasattr(p, 'z')
False
Try
• Write a function called "distance_between_points"
that takes two Points as arguments and return the
distance between them.
• Write a function called "move_rectangle" that takes
a Rectangle and two numbers named "dx" and "dy".
It should change the location of the rectangle by
adding dx to the x coordinate of corner and adding
dy to the y coordinate of corner.
• Write a version of "move_rectangle" that creates
and returns a new Rectangle instead of modifying
the old one.
Class and Functions
• A class called Time that record the time of day
>>>class Time(object):
>>> """Represents the time of day.
>>>
>>> attributes: hour, minute, second
>>> """
>>>time = Time()
>>>time.hour = 11
>>>time.minute = 59
>>>time.second = 30
print_time
>>>def print_time(t):
>>>
print('%.2d:%.2d:%.2d' % (t.hour, t.minute, t.second))
Pure Functions
• Pure functions don't modify any of the objects
passed to it as arguments.
• add_time
>>>def add_time(t1, t2):
>>> sum = Time()
>>> sum.hour = t1.hour + t2.hour
>>> sum.minute = t1.minute + t2.minute
>>> sum.second = t1.second + t2.second
>>> return sum
>>>
>>>
add_time
>>>start = Time()
>>>start.hour = 9
>>>start.minute = 45
>>>start.second = 0
>>>duration = Time()
>>>duration.hour = 1
>>>duration.minute = 35
>>>duration.second = 0
>>>done = add_time(start, duration)
>>>print_time(done)
10:80:00
add_time
>>>def add_time(t1, t2):
>>> sum = Time()
>>> sum.hour = t1.hour + t2.hour
>>> sum.minute = t1.minute + t2.minute
>>> sum.second = t1.second + t2.second
>>> return sum
>>> # carry
>>> if sum.second >= 60:
>>>
sum.second -= 60
>>>
sum.minute +=1
>>> if sum.minute >= 60:
>>>
sum.minute -= 60
>>>
sum.hour +- 1
>>> return sum
Modifiers
>>>def increment(time, seconds):
>>> time.second += seconds
>>> if time.second >= 60:
>>>
time.second -= 60
>>>
time.minute +=1
>>> if time.minute >= 60:
>>>
time.minute -= 60
>>>
time.hour +- 1
• What happens if the parameters seconds
is much greater than sixty?
Time Object  Integers
>>>def time_to_int(time):
>>> minutes = time.hour * 60 + time.minutes
>>> seconds = minutes * 60 + time.second
>>> return seconds
>>>def int_to_time(seconds):
>>> time = Time()
>>> minute, time.second = divmod(second, 60)
>>> time.hour, time.minute = divmod(minutes, 60)
>>> return time
Try
• Write a boolean function called is_after that
takes two Times objects t1 and t2, and return
True if t1 follows t2 chronologically and False
otherwise.
– Try not to use 'if' statement!
• Write a correct version of increment that doesn't
contain any loops.
• Write a pure version of increments that creates
and returns a new Time object.
• Rewrite increment using time_to_int and int_to
time
Try
• Write a function called mul_time that takes a
Time object and a number and returns a new
Time object that contains the product of the
original Time and the number.
• Use mul_time to write a function that takes a
Time object that represents the finishing time
in a race, and a number that represents the
distance, and return a Time object that
represents the average pace (time per mile).
Class and Methods
• Object-oriented features
– Programs are made up of object definitions and
function definitions, and most of the computation
is expressed in terms of operations on objects.
– Each object definition corresponds to some object
or concept in the real world, and the functions
that operate on that object correspond to the
ways real-world objects interact.
Methods
• A method is a function that is associated with a
particular class.
– We have seen methods for strings, lists, dictionaries and
tuples.
– We will define methods for user-defined types.
• Methods are semantically the same as functions, but
there are two syntactic differences:
– Methods are defined inside a class definition in order to
make the relationship be- tween the class and the method
explicit.
– The syntax for invoking a method is different from the syntax
for calling a function.
print_time method
class Time(object):
"""Represents the time of day.
attributes: hour, minute, second
"""
def print_time(t):
print('%.2d:%.2d:%.2d' % (t.hour, t.minute, t.second))
print_time method
• The first (less common) way is to use function
syntax.
• The second (more common) way is to use method
syntax.
>>>start = Time()
>>>start.hour = 9
>>>start.minute = 45
>>>start.second = 00
>>>Time.print_time(start)
09:45:00
>>>start.print_time()
09:45:00
method syntax : dot notation
• start.print_time()
• In the use dot notation.
– print_time is the name of the method
– start is the object the method is invoked on, which
is called the subject.
– Inside the method, the subject is assigned to the
first parameter.
• By convention, the first parameters of a
method is called self
print_time method using self
class Time(object):
"""Represents the time of day.
attributes: hour, minute, second
"""
def print_time(self):
print('%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second))
increment method using self
class Time(object):
"""Represents the time of day.
attributes: hour, minute, second
"""
def print_time(self):
print('%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second))
def increment(self, seconds):
seconds += self.time_to_int()
return int_to_time(seconds)
>>>start.print_time()
09:45:00
>>>end = start.increment(1337)
>>>end.print_time()
10:07:17
Confusing Error Message
>>>end=start.increment(1337,460)
TypeError: increment() takes exactly 2 arguments (3 given)
• This is because the subject is also considered as an
argument.
is_after method
• is_after method takes two Time objects as
parameters.
– name the first as self, name the second as other.
def is_after(self, other):
return self.time_to_int() > other.time_to_int()
>>>end.is_after(start)
True
The _ _init_ _ method
• The init method (short for initialization) is a
special method that get invoked when an object
is instantiated.
• Its full name is _ _ init_ _ (two underscores,
follows by init, then two more underscores)
class Time(object):
def __init__(self, hour=0, minute=0, second=0):
self.hour = hour
self.minute = minute
self.second = second
The _ _init_ _ method
>>>time=Time()
>>>time.print_time()
00:00:00
>>>time=Time(9)
>>>time.print_time()
09:00:00
>>>time=Time(9,45)
>>>time.print_time()
09:45:00
The _ _ str_ _ method
• The _ _str_ _ method is a special method that is
supposed to return a string representation of an
object.
class Time(object):
def __str__(self):
return '%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second)
>>>time=Time(9,45)
>>>print(time)
09:45:00
Try
• Write an init method for the Point class that
takes x and y as optional parameters and
assign them to the corresponding attributes.
• Write a str method for the Point class. Create
a Point object and print it.
Operator Overloading
def __add__(self,other):
seconds = self.time_to_int()+other.time_to_int()
return int_to_time(seconds)
>>>start = Time(9,45)
>>>duration = Time(1,35)
>>>print(start+duration)
11:20:00
Type-based Dispatch
def __add__(self,other):
if isinstance(other,Time):
return self.add_time(other)
else:
return self.increment(other)
def add_time(self,other):
seconds = self.time_to_int()+other.time_to_int()
return int_to_time(seconds)
def increment(self,seconds):
seconds += self.time_to_int()
return int_to_time(seconds)
>>>start = Time(9,45)
>>>duration = Time(1,35)
>>>print(start+duration)
11:20:00
>>>print(start+1337)
10:07:17
Type-based Dispatch
>>>print(start+1337)
10:07:17
>>>print(1337+start)
TypeError: unsupported operand type(s) for +: 'int' and 'instance'
Polymorphism
• Write functions that works correctly for
arguments with different types.
def histogram(s):
d = dict()
for c in s:
if c not in d:
d[c] = 1
else:
d[c] = d[c]+1
return d
>>> t = ['spam', 'egg', 'spam', 'spam', 'bacon', 'spam']
>>> histogram(t)
{'bacon': 1, 'egg': 1, 'spam': 4}
debugging
• Use hassttr
• Use _ _dict_ _
>>>p = Point(3,4)
>>>print(p.__dict__)
{'y':4, 'x':3}
def print_attributes(obj):
for attr in obj.__dict__:
print(attr,getattr(obj.attr))
Try
• Write an add method for the Point class.
• Write an add method for Points that works
with either a Point object or a tuple:
– If the second operand is a Point, the method
should return a new Point whose x coordiante is
the sum of the x coordinates of the operands, and
likewise for the y coordinates.
– If the second operand is a tuple, the method
should add the first element of the tuple to the x
coordinate and the second element to the y
coordinate, and return a new Point with the result.
Inheritance
Card Objects
• A deck
• Suits
– Spade ♠
– Hearts ♥
– Diamonds ♦
– Clubs ♣
• Ranks
– Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King
Use Integers to Encode
• Suits
–
–
–
–
Spade  3
Hearts  2
Diamonds  1
Clubs  0
• Ranks
–
–
–
–
–
Ace 1,
2, 3, 4, 5, 6, 7, 8, 9, 10,
Jack 11,
Queen 12,
King 13
Class Definition for Card
class Card(object):
"""Represents a standard playing card."""
def __init__(self, suit=0, rank=2):
self.suit = suit
self.rank = rank
Create a card with specific suit and rank
>>>queen_of_diamonds = Card(1,12)
Class Attributes
class Card(object):
"""Represents a standard playing card."""
# inside class Card:
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])
>>>card1 = Card(2,11)
>>>print(card1)
Jack of Hearts
# Each card has its own suit and rank, but there is only one copy
of suit_names and rank_names.
Comparing Cards
• Relational operators such as <,>,==, etc
depend on the comparison of values.
• Override the behavior of comparison by
__cmp__.
• __cmp__ take two parameters, self and other
– return a positive number if self > other
– return a negative number if self < other
– return 0 if self == other
• We choose to make suit more important.
– Any Spades > any Diamonds
Comparing Cards
# inside class Card:
def __cmp__(self, other):
# check the suits
if self.suit > other.suit: return 1
if self.suit < other.suit: return -1
# suits are the same... check ranks
if self.rank > other.rank: return 1
if self.rank < other.rank: return -1
# ranks are the same... it's a tie
return 0
# inside class Card:
def __cmp__(self, other):
t1 = self.suit, self.rank
t2 = other.suit, other.rank
return cmp(t1, t2)
Deck
• A deck is made up of cards.
• Each deck contain a list of cards as an attribute.
class Deck(object):
def __init__(self):
self.cards = []
for suit in range(4):
for rank in range(1, 14):
card = Card(suit, rank)
self.cards.append(card)
Printing the Deck
#inside class Deck:
def __str__(self):
res = []
for card in self.cards:
res.append(str(card))
return '\n'.join(res)
• This method demonstrates an efficient way to
accumulate a large string:
– building a list of strings and then using join.
• The built-in function str invokes the __str__
method on each card and returns the string
representation.
str.join
str.join(iterable)
Return a string which is the concatenation of
the strings in the iterable iterable. The
separator between elements is the string
providing this method.
>>>res=['a','b','c']
>>>print('\n'.join(res))
a
b
c
Printing the Deck
>>> deck = Deck()
>>> print deck
Ace of Clubs
2 of Clubs
3 of Clubs
...
10 of Spades
Jack of Spades
Queen of Spades
King of Spades
Add, Remove, Shuffle and Sort
import random
#inside class Deck:
def pop_card(self):
return self.cards.pop()
def add_card(self, card):
self.cards.append(card)
def shuffle(self):
random.shuffle(self.cards)
Try
• Write a Deck method named sort that uses
the list method sort to sort the cards in a
Deck.
• sort uses __cmp__ method we defined to
determine sort order.
sort in Python
# sorted function
>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
# list sort method
>>>a=[5,2,3,1,4]
>>>a.sort()
>>>a
[1, 2, 3, 4, 5]
Inheritance
• Inheritance is the ability to define a new class
that is a modified version of an existing class.
• The new class inherits the methods of the
existing class.
– The existing class is called the parent.
– The new class is called the child.
Hand
• Hand: the set of cards held by one player.
• A hand is similar to a deck:
– Both are made up of a set of cards.
– Both require operations like adding & removing
• A hand is also different from a deck:
– Some operations are only for a hand.
– Comparing two hands.
Child Class
class Hand(Deck):
"""Represents a hand of playing cards."""
# Hand also inherits __init__ from Deck, but the
init method for Hands should initialize cards with
an empty list.
def __init__(self, label=''):
self.cards = []
self.label = label
>>>hand=Hand('new hand')
>>>print(hand.cards)
[]
>>>print(hand.label)
new hand
Inherited Methods
>>> deck = Deck()
>>> card = deck.pop_card()
>>> hand.add_card(card)
>>> print hand
King of Spades
Move Cards
#inside class Deck:
def move_cards(self, hand, num):
for i in range(num):
hand.add_card(self.pop_card())
Try
• Write a Deck method called deal_hands that
– Takes two parameters
• the number of hands
• the number of cards per hand,
– Creates new Hand objects, deals the appropriate
number of cards per hand, and returns a list of
Hand objects.
Possible Hands in Poker
•
•
•
•
•
•
•
•
pair: two cards with the same rank
two pair: two pairs of cards with the same rank
three of a kind: three cards with the same rank
straight: five cards with ranks in sequence (aces can be
high or low, so Ace-2-3-4-5 is a straight and so is 10Jack-Queen-King-Ace, but Queen-King-Ace-2-3 is not.
flush: five cards with the same suit
full house: three cards with one rank, two cards with
another
four of a kind: four cards with the same rank
straight flush: five cards in sequence (as defined above)
and with the same suit
Try
•
•
•
•
Implement Card class
Implement Deck class
Implement Hand class
Implement Hand method for
– has_pair
– has_two_pair
– has_flush
• Write a main program that create a deck, shuffle
it, deal 7-cards hands see if you got pair, two_pair,
flush. Repeat the process to estimate the
probability.
PokerHand Class
class PokerHand(Hand):
def suit_hist(self):
"""Builds a histogram of the suits that appear in the hand.
Stores the result in attribute suits.
"""
self.suits = {}
for card in self.cards:
self.suits[card.suit] = self.suits.get(card.suit, 0) + 1
def has_flush(self):
"""Returns True if the hand has a flush, False otherwise.
Note that this works correctly for hands with more than 5 cards.
"""
self.suit_hist()
for val in self.suits.values():
if val >= 5:
return True
return False
This code deals seven 7-cards poker hands and check
to see if any of them contains a flush.
# make a deck
deck = Deck()
deck.shuffle()
# deal the cards and classify the hands
for i in range(7):
hand = PokerHand()
deck.move_cards(hand, 7)
hand.sort()
print hand
print hand.has_flush()
print ''
Try: Add methods to PokerHand
• Add methods to PokerHand class
– has_pair, has_twopair, etc
– Return True of False
– Your code should work correctly for "hands" that
contain any number of cards.
• Add a method
– classify
– Figures out the highest-value classification for a hand
and sets the label attribute accordingly.
• Write a function that shuffles a deck of cards,
divides it into hands, classify the hands, and
counts and number of times various classifications
appears. (each hand has 5 cards)
pass Statements
• The pass statement does nothing. It can be
used when a statement is required syntactically
but the program requires no action.
>>> while True:
...
pass # Busy-wait for keyboard interrupt (Ctrl+C)
...
>>> class MyEmptyClass:
...
pass
...
>>> def initlog(*args):
...
pass
# Remember to implement this!
...