Today: string find/slice practice, file-reading, crazycat example program

Problems today:

> str3 functions

Idea: Use var= to Shorten Code

Problems: int indexing, slicing, no loops

1. right_left()

'aabb' → 'bbbbaaaa'

We'll say the midpoint of a string is index len // 2, dividing the string into a left half before the midpoint and a right half starting at the midpoint. Given string s, return a new string made of 2 copies of right followed by 2 copies of left. So 'aabb' returns 'bbbbaaaa'.

Solution

def right_left(s):
    mid = len(s) // 2
    left = s[:mid]
    right = s[mid:]
    return right + right + left + left
    # Style comparison:
    # Without using any variables, the solution
    # is longer and not so readable:
    # return s[len(s) // 2:] + s[len(s) // 2:] + s[:len(s) // 2] + s[:len(s) // 2]

2. at_3()

This looks simple, but the details are tricky. Make a drawing.

Given string s. Find the first '@' within s. Return the len-3 substring immediately following the '@'. Except, if there is no '@' or there are not 3 chars after the @, return ''.

Solution

def at_3(s):
    at = s.find('@')
    if at == -1:
        return ''
    # Is at + 3 past end of string?
    # Could "or" combine with above
    if at + 3 >= len(s):
        return ''
    return s[at + 1:at + 4]
    # Working out >= above ... drawing!

3. parens

This is nice, realistic string problem with a little logic in it.

Recall: s.find(target, start_index) - start search at start_index vs. starting search at index 0.

Given string s. Look for a '(.....)' within s - look for the first '(' in s, then the first ')' after the '(', using the second start_index parameter of .find(). If both parens are found, return the chars between them, so 'xxx(abc)xxx' returns 'abc'. If no such pair of parens is found, return the empty string. Think about the input '))(abc)'.

Solution

def parens(s):
    left = s.find('(')
    if left == -1:
        return ''
    # Start search at left + 1:
    right = s.find(')', left + 1)
    if right == -1:
        return ''
    # Use slice to pull out chars between left/right
    return s[left + 1:right]

crazycat example

This code is complete, you can run it to see what it does.

crazycat.zip

Backslash Chars in a String

Use backslash \ to include special chars in a string

s = 'isn\'t'

\n  newline char
\\  backlash
\'  single quote
\"  double quote

What is a Text File?

hibye.txt Text Example

2 lines, each line has a '\n' at the end. The first line has a space, just ' ' in Python.

Hi and
bye

Here is what that file looks like in an editor that shows little gray marks for the space and \n

alt: hibye.txt chars, showing \n ending each line:>

<p>In Fact the contents of that file can be expressed as a Python string
<br><code>'Hi and\nbye\n'</code>

<p>How many chars are in that file (each \n is one char). Roman alphabet A-Z chars like this take up 1 byte per char. This comes to 11 chars. Look in your file-system explorer on your computer, get-info on the file. See if it's 11 bytes in size.


<h2>Primary: Function Black-Box Structure</h2>

<p>
<img src=python-program.png alt=

Secondary: Program Text Output Area

alt: standard output area, filled by calling print() from any function

print() function

>>> print('hello', 'there', '!')
hello there !
>>> print('hello', 123, '!')
hello 123 !
>>> print('hello', 123, '!', sep=':')
hello:123:!
>>> print(1, 2, 3)               # end='\n' the default
1 2 3
>>> print(1, 2, 3, end='xxx\n')  # end= what goes at end
1 2 3xxx
>>> print(1, 2, 3, end='')       # suppress the \n
1 2 3>>>

Data out of function: return vs. print

Return and print() are both ways to get data out of a function. We will be careful when specifying a function to say that it should "return" a value (most common), or it should "print" something to standard output. Usually the functions that do computation use return, so they can be tested.

Crazycat Program example

1. Try "ls" and "cat" in terminal

Open a terminal in the crazycat directory (see the Command guide for more information running in the terminal). Terminal commands - work in both Mac and Windows

ls - see list of filenames ("dir" on older Windows)

cat filename - see file contents

$ ls
alice.txt       crazycat.py     hibye.txt       poem.txt        quotes
$ cat poem.txt 
Roses Are Red
Violets Are Blue
This Does Not Rhyme
$

2. Run crazycat.py with filename

$ python3 crazycat.py poem.txt 
Roses Are Red
Violets Are Blue
This Does Not Rhyme
$ python3 crazycat.py hibye.txt 
Hi and
bye
$

3. Canonical File-Read Code

with open(filename, 'r') as f:
    for line in f:
        # use line in here

alt:file read loop, gets one line at a time from file

Here is the whole function to print the contents of a file. Why do we need end='' here? The line already has \n at its end, so get double spacing if print() adds its standard \n.

def print_file_plain(filename):
    with open(filename, 'r') as f:
        for line in f:
            # use line in here
            print(line, end='')

4. Run With -crazy option

def crazy_line(line):
    """
    Given a line of text, returns a "crazy" version of that line,
    where upper/lower case have all been swapped, so 'Hello'
    returns 'hELLO'.
    >>> crazy_line('Hello')
    'hELLO'
    >>> crazy_line('@xYz!')
    '@XyZ!'
    >>> crazy_line('')
    ''
    """
    result = ''
    for i in range(len(line)):
        char = line[i]
        if char.islower():
            result += char.upper()
        else:
            result += char.lower()
    return result

Here is command line to run with -crazy option

$ python3 crazycat.py -crazy poem.txt 
rOSES aRE rED
vIOLETS aRE bLUE
tHIS dOES nOT rHYME

Here print_file_crazy(), similar to print_file_plain() but passes each line through the crazy_line() function before printing.

def print_file_crazy(filename):
    """
    Given a filename, read all its lines and print them out
    in crazy form.
    """
    with open(filename, 'r') as f:
        for line in f:
            print(crazy_line(line), end='')