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 OUTPUT INTERPRETER COMPILER OBJECT CODE 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.64.0, 14.5//3.73.0 • % – module=remainder after x/y: 1.5%0.40.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 = 'monty@python.org' >>>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! ...