Today - style, black box examples, boolean logic, string introduction, string loop, [Doctests]

Coding Style

Let's add a little more to our Py style rules. This is the same document referenced in week 1, but we'll look in more detail today.

Python Guide - look at the Style1 section.

Data Flow Picture

We want to divide our big program into a bunch of functions.

You have some files with data int them. You write code to lode the data into Python in one form. Then compute a transform into another form. Then a further computation produces an "answer" form. You can think of this as a sort of "data flow", from the original data to the final output. alt: data flows through program

"Black Box" Function Model

Black-box model of a function: function runs with parameters as input, returns a value. All the complexity of its code is sealed inside. Calling it is relatively simple - provide parameters, get back an answer.

function is black box with parameters in and result out

Strategic Story - Why Black Box?

Key goal: be able to work on one function at a time

For Divide and Conquer, want to be able to work on each function separately, one at a time. Need to keep the functions sealed off from each other for this to work - black box. You'll see how this all comes together as we work with functions, parameters, and return.

1. winnings1() Example

> winnings1()

score winnings
0      0
1      10
2      20
3      30
4      40
5      60  # 12x kicks in here
6      72
7      84
8      96
9      108
10     120

1. Input - Parameter score

def winnings1(score):
    ...

2. Output - return score * 10

def winnings1(score):
    return score * 10

Experiment 1 - winnings1()

You can type these in and hit the Run button as we go. The results are pretty easy to follow.

def winnings1(score):
    return score * 10

Output Format

if-return Pick-Off Strategy

Experiment 2

def winnings1(score):
    if score >= 5:
        return score * 12

Add if-logic to pick off the >= 5 case. It's only right half the time.

None Result

Experiment 3 - Almost

def winnings1(score):
    if score >= 5:
        return score * 12

    if score < 5:
        return score * 10

Add pick-off code for the other case. This is close to fully correct. It does return the right result in all cases, but the code is a little off.

Experiment 4 - Perfect

def winnings1(score):
    if score >= 5:
        return score * 12

    # If we get to this line, score < 5
    return score * 10

Observations

def winnings1(score):
    if score >= 5:
        return score * 12
        return score * 10

That does not work. The second return is under the control of the if. It never runs because the line above exits the function. In Python code, where a line is indented is very significant.


2. Winnings2 Example

Extra practice example.

> winnings2()

Say there are 3 cases, have a series of if-return to get them. Need to think about the order. Need to put the == 10 case early.

Winnings2: The int score number in the range 0..10 inclusive. Bonus! if score is 10 exactly, winnings is score * 15. If score is between 4 and 9 inclusive, winnings is score * 12. if score is 3 or less, winnings is score * 10. Given int score, compute and return the winnings.

Solution

def winnings2(score):
    if score == 10:
        return score * 15
  
    if score >= 4:  # score <= 9 is implicit
        return score * 12
    
    # All score >= 4 cases have been picked off.
    # so score < 4 here.
    return score * 10

Boolean Values

For more detail, see guide Python Boolean

Ways to Get a Boolean

Try this in the interpreter (or Hack Mode button at bottom experimental server)

>>> n = 5   # assign to n to start
>>> n == 6
False
>>> n == 5
True
>>> n != 6
True
>>> 
>>> n < 10
True
>>> n < 5  # < is strict
False
>>> 
>>> n <= 5  # less-or-equal
True
>>> 
>>> n > 0
True

Boolean Operators: and or not

Weather Examples - and or not

Say we have temp variable is a temperature, and is_raining is a Boolean indicating if it's raining or not. Here are some examples to demonstrate and or not:

# OR: either or both are true
# Say we don't want to go outside if
# if it's cold or raining.
if temp < 50 or is_raining:
    print('not going outside!')


# AND
# We want to go outside if it's snowing.
if temp < 32 and is_raining:
    print('yay snow!')


# AND + NOT
# Sunny and nice case:
if temp > 70 and not is_raining:
    print('Sunny and nice!')


# Style note: don't use == False or == True
# to form a test. See above how "is_raining"
# is used directly in the test, or with a "not"
# in front of it.

Numeric and Example

Suppose we have this code, and n holds an int value.

if n > 0 and n < 10:
   # get here if test is True

What must n be inside the if-statement? Try values like 0 1 2 3 . 8 9 10

You can work out that n must be a an int value in the range 1..9 inclusive.

3. is_teen(n) Example

> is_teen()

is_teen(n): Given an int n which is 0 or more. Return True if n is a "teenager", in the range 13..19 inclusive. Otherwise return False. Write a boolean test to pick off the teenager case.

Solution

def is_teen(n):
    # Use and to check
    if n >= 13 and n <= 19:
        return True

    return False

    # Future topic: possible to write this
    # as one-liner: return n >= 13 and n <= 19

4. Lottery Scratcher Example

> scratcher()

An extra example for practice.

Lottery scratcher game: We have three icons called a, b, and c. Each is an int in the range 0..10 inclusive. If all three are the same, winnings is 100. Otherwise if 2 are the same, winnings is 50. Otherwise winnings is 0. Given a, b and c inputs, compute and return the winnings. Use and/or/== to make the tests.

Solution:

def scratcher(a, b, c):
    # 1. All 3 the same
    if a == b and b == c:
        return 100
    
    # 2. Pair the same (3 same picked off above)
    if a == b or b == c or a == c:
        return 50
    
    # 3. Run gets to here, nothing matched
    return 0


Strings

For more detail, see guide Python Strings

len() function

>>> len('Hello')
5
>>> s = 'Hello'   # Equivalently
>>> len(s)
5
>>> len('x')
1
>>> len('')
0

Empty String ''

Zero Based Indexing - Super Common

alt: the string 'Python' with 6 index numbers 0..5

String Square Bracket Index

>>> s = 'Python'
>>> s[0]
'P'
>>> s[1]
'y'
>>> s[4]
'o'
>>> s[5]
'n'
>>> s[6]
IndexError: string index out of range

String Immutable

>>> s = 'Python'
>>> s[0]        # read ok
'P'
>>> s[0] = 'X'  # write not ok
TypeError: 'str' object does not support item assignment

String +

>>> a = 'Hello'
>>> b = 'there'
>>> a + b
'Hellothere'
>>> a
'Hello'

String + Add At End Pattern

>>> a = 'Hi'
>>> a = a + '!'  # aka a += '!'
>>> a
'Hi!'
>>> a = a + '!'
>>> a
'Hi!!'
>>> a = a + '!'
>>> a
'Hi!!!'

for/i/range String Loop

# have string s
for i in range(len(s)):
    # access s[i] in here

String2 Example Functions

1. double_char

> double_char

Solution code

def double_char(s):
    result = ''
    for i in range(len(s)):
        result = result + s[i] + s[i]
    return result

String Boolean Tests - True or False

String Testing: 'a' vs. 'A'

We've used == already. 'a' and 'A' are different characters.

>>> 'a' == 'A'
False
>>> s = 'red'
>>> s == 'red'   # two equal signs
True
>>> s == 'Red'   # must match exactly
False

String Testing - in

>>> 'c' in 'abcd'
True
>>> 'bc' in 'abcd'
True
>>> 'bx' in 'abcd'
False
>>> 'A' in 'abcd'
False

String Character Class Tests

s.isdigit() - True if all chars in s are digits '0' '1' .. '9'

s.isalpha() - True for alphabetic word char, i.e. a-z A-Z. Each unicode alphabet has its own definition of what's alphabetic, e.g. 'Ω' below is alphabetic.

s.isalnum() - alphanumeric, just combines isalpha() and isdigit()

s.isspace() - True for whitespace char, e.g. space, tab, newline

>>> 'a'.isalpha()
True
>>> 'abc'.isalpha()  # works for multiple chars too
True
>>> 'Z'.isalpha()
True
>>> '@'.isalpha()
False
>>> '9'.isdigit()
True
>>> ' '.isspace()
True

2. digits_only

> digits_only

Solution code

def digits_only(s):
    result = ''
    for i in range(len(s)):
        if s[i].isdigit():
            result += s[i]
    return result

if Variation: if / else

if test-expr:
  Lines-A
else:
  Lines-B

else vs. not

Sometimes people sort of back into using else to do something if the test is False, like this:

if some_test:
     pass
     (do nothing here)
else:
     line I want to do

The correct way to do that is with not:

if not some_test:
    line I want to do

3. str_dx

> str_dx

Solution code

def str_dx(s):
    result = ''
    for i in range(len(s)):
        if s[i].isdigit():
            result += 'd'
        else:
            result += 'x'
    return result

Big Picture - Program, Functions, Testing

alt: program made of functions, each with tests

str1 project

Python Function - Pydoc

def digits_only(s):
    """
    Given a string s.
    Return a string made of all
    the chars in s which are digits,
    so 'Hi4!x3' returns '43'.
    Use a for/i/range loop.
    (this code is complete)
    >>> digits_only('Hi4!x3')
    '43'
    >>> digits_only('123')
    '123'
    >>> digits_only('')
    ''
    """
    result = ''
    for i in range(len(s)):
        if s[i].isdigit():
            result += s[i]
    return result

Python Function - Doctest

See lines like:

    >>> digits_only('Hi4!x3')
    '43'

Doctest - Important for Strategy

Divide and conquer - want to be able to divide your program up into separate functions, say A, B, and C. Want to work on one function at a time, including testing. Doctests make this really easy - just author the tests right next to where you write the code.

We'll use Doctests to drive the examples on wed and in section.