Today: strings, loops, string functions, Doctests
Week-3 Thoughts
- Pace has been pretty high
We'll slow down a bit now concepts/day
- HW2 lesson: y, pixel_left, .. diagram
Don't do it in your head
Wrong 49 times, right 1 time .. fine
Understand that last one!
- Something fun on Friday
String Reminder
-See guide: Python-String
- Sequence of characters in quotes
'Python'
- len() function
- Square brackets
+
to put strings together
- Indexed: 0, 1, 2, ... len-1
- Strings are immutable
str int Conversion
- What is '123' vs. 123?
- '123' is string
- 123 is in
- str(8) returns '8'
- int('8') returns 8
Loop Over Every Char in a String?
- Python is very consistent
- Zero-based indexing
- Linear collection of elements
- Index each element at: 0, 1, 2, ... len-1
- String works this way
- Images worked this way too
- Collections to learn later .. work this way too
Loop Over Every Char
- Loop over index numbers
for i in range(len(s)):
- Use variable name
i
for the index numbers
- This is so common, it's idiomatic
- Use
s[i]
to access char in loop
# have string s
for i in range(len(s)):
# use s[i] in here
Example 1. double_char
(these work for everyone now, sorry for the snafu)
>
1.double_char
- Idiomatic loop look at every char
- Initialize
result
variable before loop
- Update result in loop with = and +
- Return result at end
- Could use
+=
shortcut
- Q: does it work on empty string input?
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' 'A'
We've used ==
already. 'a'
and 'A'
are different characters.
>>> s = 'red'
>>> s == 'red' # two equal signs
True
>>> s == 'Red' # must match exactly
False
String Testing - in
- The word in - test if a substring appears in a string
- Mnemonic: same word inside for-loop, like Python wants to introduce very few new words
- Chars must match exactly - upper/lower are considered different
>>> 'c' in 'abcd'
True
>>> 'bc' in 'abcd'
True
>>> 'bx' in 'abcd'
False
>>> 'A' in 'abcd'
False
String Character Class Tests
- String is made of characters
- Categorize characters into major classes:
- 1. Alphabetic -
'a' 'Z'
.. used to make words
- 2. Digits -
'0' '2'
.. used to make numbers
- 3. Space - space, tab, newline - aka "whitespace"
- There are noun.verb tests for the above 3, returning boolean True or False
- For empty string return False (a weird edge case)
- All the other chars form a miscellaneous class -
'$' '%' ';'
... any char not in the first 3 categories
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
Example 2. digits_only
>
2.digits_only
- Return string of only digits
- So 'ab4xx2' returns '42'
- Use the standard range loop
- If logic inside the loop
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:
Lines-A
else:
Lines-B
- See guide: Python-if
- Regular if - do the body lines, or do nothing
- if/else variant:
Choose between 2 actions
Always do one of them
- Run lines-A if test is True
- Run lines-B if test is False
- Style: use if/else to choose 1 of 2 actions
- Avoid using "else" if it is not needed
The regular "if" we've seen is most common
Example 3. str_dx
>
3.str_dx
- Return string where every digit changes to 'd'
And every other char changed to 'x'
- So 'ab42z' returns 'xxddx'
- Use if/else
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
- Big Picture
- Program Made of many functions
- Decomposition
- Best Practice:
Test a function in isolation
Then later functions can call it
- We did this with Bit sometimes
- Fits with Divide and Conquer strategy
- Fits with Black Box model of a function
Inputs and Outputs - test with these
- Today: tech for doing this in Python!
str1 project
- Download str1.zip project
- Expand to get "str1" folder
- Open the folder in PyCharm
Python Function - Pydoc
- See str_dx() below
- See triple-quote description at top
- This is known as "Pydoc" of the function
What inputs fn takes
What it promises to return
Black box model
"Contract" idea
def str_dx(s):
"""
Given string s.
Returns a string where every digit is changed to 'd',
and all other chars are changed to 'x'.
(this code + tests are complete)
>>> str_dx('Hi4!x3')
'xxdxxd'
>>> str_dx('123')
'ddd'
>>> str_dx('')
''
"""
Python Function - Doctest
See lines like:
>>> str_dx('Hi4!x3')
'xxdxxd'
- That syntax spells out one test of the str_dx() function
- Looks like a fn call
- Input between the parens
- Output on the next line
- In PyCharm:
Right click on the test
Select "Run Doctest ..."
- Output:
Process finished with exit code 0
That means it worked perfectly
This message could be more fun about it!
- Otherwise get error output
- Experiment: try putting in a bug, run Doctest, fix the bug
- Protip: ctrl-r runs most recent test
Avoid having to re-click every time
String Test: Upper / Lower Case
- Lowercase 'a'
- Uppercase 'A'
- Test functions return boolean True/False
s.isupper(), s.islower()
- True for uppercase / lowercase alphabetic.
- False for characters like '9' and '$' which do not have upper/lower versions.
>>> 'a'.islower()
True
>>> 'A'.islower()
False
>>> 'A'.isupper()
True
>>> '9'.isupper()
False
String Change Upper/Lower Case
s.lower()
- returns a new version of s where each char is converted to its lowercase form, so 'A' becomes 'a'. Chars like '$' are unchanged. The original s is unchanged - a good example of strings being immutable. Each unicode alphabet includes its own rules about upper/lower case.
s.upper()
- returns an uppercase version of s
>>> s = 'Python123'
>>> s.lower()
'python123'
>>> s.upper()
'PYTHON123'
>>> s
'Python123'
Programming With Doctest
- Have a few Doctests for a function
- Work on the code
- Type ctrl-r to test your fn as you go
- This is a fantastic workflow
- 1. Function has tests right in the code
- 2. You work on fn, run tests until it works
Doctest Example 2, crazy_str()
Write code for this problem. Use the Doctests as you go.
def crazy_str(s):
"""
Given string s. Return a string where every lowercase char
in s is converted to uppercase, and every other char
is converted to lowercase.
>>> crazy_str('Hello')
'hELLO'
>>> crazy_str('abc2@Z')
'ABC2@z'
>>> crazy_str('')
''
"""
pass