Lecture 5: Strings plus objects

Objectives for today

Indexing and Slicing Strings

Python has some very sophisticated indexing functionality, termed “slicing”, in which we define the slice as “lower bound” : “upper bound” [: “stride”], with an inclusive lower bound and an exclusive upper bound.

>>> s = "Good morning"
>>> len(s)
12
>>> s[0:4]
'Good'
>>> s[0:7]
'Good mo'
>>> s[1:7:2]
'odm'
>>> s[-1]
'g'
>>> s[:4]
'Good'
>>> s[4:]
' morning'
>>> s[::]
'Good morning'

Objects

Some functions can be invoked with parameters of different types, for example, len can be used with strings or other sequences, e.g.,

>>> len('Hello, world!')
13
>>> len(range(5))
5

An alternate approach is to invoke different functions on a specific value. Specifically we invoke a method on an object. A method then is a function specific to one type of object, e.g., specific to strings or integers.

Methods are invoked or called with the object.method syntax. For example:

>>> s = "Hi CS150"
>>> s.lower()
'hi cs150'
>>> s.upper()
'HI CS150'

This approach to organizing our code is called “object-oriented programming” (OOP), because the data and the functions are contained within/associated with objects. And for the purposes of this course all objects of a specific type, e.g., all string objects, have the same set of methods.

Methods are very similar to the functions we have been working with: they can accept additional parameters (in addition to the object on which they were invoked, also termed the “receiver”) and you can obtain their docstrings with help, e.g.,

>>> help(s.lower)
>>> help(str.lower)

But notice that we need to specify the receiver object (value) itself, or the type (class) of the receiver.

We will learn some more about OOP later in the semester (and you will learn a lot more in CSCI201); for now we want to get started using methods, particularly string methods.

So how can we learn more about the methods for an object?

The dir function will return a list of all the methods available for an object, say a string s:

>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

And because it is good practice, many of the methods have descriptive names. e.g. find

>>> help(s.find)
Help on built-in function find:

find(...) method of builtins.str instance
    S.find(sub[, start[, end]]) -> int
    
    Return the lowest index in S where substring sub is found,
    such that sub is contained within S[start:end].  Optional
    arguments start and end are interpreted as in slice notation.
    
    Return -1 on failure.

>>> ALPHABET = "abcdefghijklmnopqrstuvwxyz"
>>> ALPHABET.find("a")
0
>>> ALPHABET.find("z")
25

How can we apply a sequence of method calls? Recall that we can use the return value from one function call as the argument to another, e.g.,

>>> from random import randint
>>> len(range(randint(1, 10)))
7

The equivalent pattern for method calls would be the following. This kind of method calling is often called “chaining”:

>>> s.lower().capitalize()
'Hi cs150'

What about the receiver? The receiver can be any expression, it is not required to be a variable or literal. For example:

>>> ("gI".replace("g","h") + " CS" + str(300//2)).swapcase()
'Hi cs150'

Here we are invoking the swapcase method on the expression inside the parentheses. The expression inside the parentheses is evaluated first to produce the string "hI CS150", and then the swapcase method is invoked on that string to produce the final result. A good strategy for evaluating these types of expressions is to work step-by-step keeping in mind the order in which Python evaluates expressions. In general, Python evaluates expressions from left to right while observing precedence ordering of PEMDAS (parentheses, exponentiation, multiplication, division, addition, subtraction). Remember that a set of parentheses are themselves a (single) expression.

Peer instruction questions (String Methods) [1] (Section A, Section B)

Strings are immutable

>>> s = "Sally"
>>> t = s.lower()
>>> t
'sally'
>>> s
'Sally'

Strings in Python are immutable, that is they can’t be changed. The string methods that look like they are changing the string are actually creating a new string object.

Python Tutor

>>> s = "test"
>>> s.upper()
'TEST'
>>> s
'test'
>>> a = "hi"
>>> b = a
>>> a = "bye"
>>> b
'hi'

Previewing booleans

Earlier we saw that strings have a method startswith. We would guess that method will indicate if the string starts with the supplied parameter:

>>> s = "hello"
>>> s.startswith('h')
True
>>> s.startswith('b')
False

What are True and False? They are not strings because they’re not enclosed in quotes, nor are they integers or floating point numbers. They are values of a new type we haven’t yet encountered, bools, which is short for Boolean. Booleans are a type that can only take on two values True and False. Booleans and Boolean logic are a key programming tool, particularly for decision-making, that we will start learning about next week.

Summary

  1. Methods (on strings)
  2. Quiz on Wednesday on strings. Complete the Practice Problems 3 (as practice, not to turn in) and the Quiz 3 practice.
  3. Be prepared to ask questions and work on Lab 3 on Wednesday. Complete the prelab beforehand.