aliases

Multiple variables that contain references to the same object.


In [1]:
"""Assignment between variables creates aliases."""
animal = "giraffe"
creature = animal

print("Is creature an alias of animal?", creature is animal)


Is creature an alias of animal? True

In [2]:
"""Assignment of the same value to different variables does not necessarily create aliases."""
weather_next_5_days       = ["Sunny", "Partly sunny", "Cloudy", "Sunny", "Sunny"]
weather_subsequent_5_days = ["Sunny", "Partly sunny", "Cloudy", "Sunny", "Sunny"]

if weather_subsequent_5_days is weather_next_5_days:
    is_weather_subsequent_alias_of_weather_next = "Yes."
else:
    is_weather_subsequent_alias_of_weather_next = "No."

if weather_subsequent_5_days == weather_next_5_days:
    same_forecast = "Yes."
else:
    same_forecast = "No."

print("Is weather_subsequent_5_days an alias of weather_next_5_days?",
    is_weather_subsequent_alias_of_weather_next, "\n")

print("Is the forecast for the next 5 days the same as the forecast for the subsequent 5 days?",
      same_forecast, "\n")

print("id(weather_next_5_days):      ", id(weather_next_5_days))
print("id(weather_subsequent_5_days):", id(weather_subsequent_5_days))


Is weather_subsequent_5_days an alias of weather_next_5_days? No. 

Is the forecast for the next 5 days the same as the forecast for the subsequent 5 days? Yes. 

id(weather_next_5_days):       4399029640
id(weather_subsequent_5_days): 4399029704

clone

To create a new object that has the same value as an existing object. Copying a reference to an object creates an alias but doesn’t clone the object.


In [3]:
"""Clone a list to get a new object with the same values."""
lst = [1, 2, 3]
alias = lst     # create an alias to lst
clone = lst[:]  # clone lst
print("Is alias an alias of lst?", alias is lst)
print("Is clone an alias of lst?", clone is lst)
print("Do lst, alias, and clone all have the same values?", lst == alias == clone)


Is alias an alias of lst? True
Is clone an alias of lst? False
Do lst, alias, and clone all have the same values? True

Warning: list slicing will create a shallow copy ("clone") of nested lists.


In [4]:
"""List slicing DOES NOT deeply copy nested objects."""
import time
delay = 2

hundreds = [100, 200, 300]
numbers = [1, hundreds]   # hundreds is nested
shallow_clone = numbers[:]
print("hundreds:", hundreds)
print("numbers:", numbers)
print("shallow_clone:", shallow_clone, "\n")
time.sleep(delay)

# verify that shallow_clone[0] == numbers[0]
print("shallow_clone[0] == numbers[0]:", shallow_clone[0] == numbers[0], "\n")
time.sleep(delay)

# modify the first element in numbers from 1 -> 10
numbers[0] = 10
print("numbers[0] = 10\n")
time.sleep(delay)

# test whether shallow_clone[0] also was modified
print("Was shallow_clone[0] also modified?",
      shallow_clone[0] == numbers[0], "\n")
time.sleep(delay)

# change the first element in the list of hundreds from 100 -> 500
numbers[1][0] = 500
print("numbers[1][0] = 500\n")
time.sleep(delay)

# test whether the list of hundreds in shallow_clone also has been modified
print("Was shallow_clone[1][0] also modified?",
      numbers[1][0] == shallow_clone[1][0], "\n")
time.sleep(delay)

# look at all of the variables
print("hundreds:", hundreds)
print("numbers:", numbers)
print("shallow_clone:", shallow_clone)


hundreds: [100, 200, 300]
numbers: [1, [100, 200, 300]]
shallow_clone: [1, [100, 200, 300]] 

shallow_clone[0] == numbers[0]: True 

numbers[0] = 10

Was shallow_clone[0] also modified? False 

numbers[1][0] = 500

Was shallow_clone[1][0] also modified? True 

hundreds: [500, 200, 300]
numbers: [10, [500, 200, 300]]
shallow_clone: [1, [500, 200, 300]]

delimiter

A character or string used to indicate where a string should be split.


In [5]:
"""By default, strings are split on whitespace."""
quote = "Beware of bugs in the above         code; I have only proved it correct, not tried it. -Donald Knuth"
words = quote.split()
print(words)


['Beware', 'of', 'bugs', 'in', 'the', 'above', 'code;', 'I', 'have', 'only', 'proved', 'it', 'correct,', 'not', 'tried', 'it.', '-Donald', 'Knuth']

In [6]:
"""Specify the delimiter to change how strings are split."""
quote = "Beware of bugs in the above code; I have only proved it correct, not tried it. -Donald Knuth"
delimiter = ';'
phrases = quote.split(delimiter)
print(phrases)


['Beware of bugs in the above code', ' I have only proved it correct, not tried it. -Donald Knuth']

In [7]:
"""A delimiter can also be specified to join."""
quote = "Beware of bugs in the above code; I have only proved it correct, not tried it. -Donald Knuth"
delimiter = '-'
parts = quote.split(delimiter)
print(parts, "\n")
improved_quote = '\nby '.join(parts)
print(improved_quote)


['Beware of bugs in the above code; I have only proved it correct, not tried it. ', 'Donald Knuth'] 

Beware of bugs in the above code; I have only proved it correct, not tried it. 
by Donald Knuth

element

One of the values in a list (or other sequence). The bracket operator selects elements of a list.


In [8]:
"""Access an element of a list by using square brackets with an index."""
lst = [1, 2, 3]
print("The third element of lst:", lst[2])


The third element of lst: 3

index

An integer variable or value that indicates an element of a list.


In [9]:
"""By varying the index, you can access different elements of a list."""
lst = [1, 2, 3]
i = 0    # i holds the index value
print("The element of lst at index 0 is", lst[i])
i = 1
print("The element of lst at index 1 is", lst[i])


The element of lst at index 0 is 1
The element of lst at index 1 is 2

list

A collection of objects, where each object is identified by an index. Like other types str, int, float, etc. there is also a list type-converter function that tries to turn its argument into a list.


In [10]:
"""Create a list from a list literal."""
lst = [1, 2, 3]
print(lst)


[1, 2, 3]

In [11]:
"""Create a list using list()."""
lst = list(range(1, 4))
print(lst, type(lst))


[1, 2, 3] <class 'list'>

List comprehensions

List comphrensions take the form:

[expression for variable in sequence if condition]


In [12]:
"""Create a list from a list comprehension."""
lst = [i for i in range(1, 11) if i <= 3]
print(lst)


[1, 2, 3]

In the above example the general pattern:

[expression for variable in sequence if condition]

Becomes:

[i for i in range(1, 100) if i <= 3]

You can omit the condition when you want to take all of the values from the sequence.

A list comprehension without the optional if clause takes the form:

[expression for variable in sequence]


In [13]:
"""Create a list from a list comprehension without an if clause."""
lst = [i for i in range(1, 11)]
print(lst)


[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Notice especially the "punctuation" separating the various parts of the comprehension:

[... for ... in ... if ...]

Without the if clause:

[... for ... in ...]

These patterns are functionally equivalent to the more familiar list accumulation pattern.


In [14]:
"""Create a list by accumulating values."""
lst = []
for i in range(1, 10):
    if i <= 3:
        lst.append(i)
print(lst)


[1, 2, 3]

In [15]:
"""Create a list by accumulating values without if condition."""
lst = []
for i in range(1, 11):
    lst.append(i)
print(lst)


[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

You can translate many for loops into list comprehensions:

lst = []                           
for i in range(1, 10):          becomes          lst = [i for i in range(1, 10) if i <= 3]       
    if i <= 3:
        lst.append(i)
lst = []                           
for i in range(1, 10):          becomes          lst = [i for i in range(1, 10)]       
    lst.append(i)
doubled = []                           
for i in range(1, 10):          becomes          doubled = [i * 2 for i in range(1, 10)]       
    doubled.append(i * 2)

Generally:

lst = []
for variable in sequence:       becomes          lst = [expression for variable in sequence if condition]
    if condition:
        lst.append(expression)

list traversal

The sequential accessing of each element in a list.


In [16]:
"""List traversal using a for loop."""
lst = [1, 2, 3]
for i in lst:
    print(i)


1
2
3

In [17]:
"""List traversal using a for loop and an index."""
lst = [1, 2, 3]
for i in range(len(lst)):
    print(lst[i])


1
2
3

modifier

A function which changes its arguments inside the function body. Only mutable types can be changed by modifiers.


In [18]:
import time
delay = 3

def title_modifier(lst):
    """This function will change the list passed to it."""
    for i in range(len(lst)):
        lst[i] = ' '.join([word[0].upper() + word[1:].lower() for word in lst[i].split()])


places = ["new york", "kansas city", "los angeles", "seattle"]

print("Places before being modified:", places, "\n")
time.sleep(delay)

print("Calling title_modifier(places)...", "\n")
return_value = title_modifier(places)
time.sleep(delay)

print("The return value of title_modifier(places) is", return_value, "\n")
time.sleep(delay)

print("Places after being modified:", places, "\n")


Places before being modified: ['new york', 'kansas city', 'los angeles', 'seattle'] 

Calling title_modifier(places)... 

The return value of title_modifier(places) is None 

Places after being modified: ['New York', 'Kansas City', 'Los Angeles', 'Seattle'] 


In [19]:
import time
delay = 3

def title_modifier(lst):
    """This function will change the list passed to it."""
    for i in range(len(lst)):
        place = lst[i]
        words = place.split()
        title_cased_words = []
        for word in words:
            first_letter = word[0]
            rest_of_word = word[1:]
            title_cased_words.append(first_letter.upper() + rest_of_word.lower())
        lst[i] = ' '.join(title_cased_words)

places = ["new york", "kansas city", "los angeles", "seattle"]

print("Places before being modified:", places, "\n")
time.sleep(delay)

print("Calling title_modifier(places)...", "\n")
return_value = title_modifier(places)
time.sleep(delay)

print("The return value of title_modifier(places) is", return_value, "\n")
time.sleep(delay)

print("Places after being modified:", places, "\n")


Places before being modified: ['new york', 'kansas city', 'los angeles', 'seattle'] 

Calling title_modifier(places)... 

The return value of title_modifier(places) is None 

Places after being modified: ['New York', 'Kansas City', 'Los Angeles', 'Seattle'] 

mutable data type

A data type in which the elements can be modified. All mutable types are compound types. Lists are mutable data types; strings are not.


In [20]:
"""Lists are mutable."""
cities = ["Albuquerque", "Chicago", "Paris"]

print("Cities before changing cities[1]:", cities)

cities[1] = "Tokyo"

print("Cities after changing cities[1]:", cities)


Cities before changing cities[1]: ['Albuquerque', 'Chicago', 'Paris']
Cities after changing cities[1]: ['Albuquerque', 'Tokyo', 'Paris']

In [21]:
"""Strings are not mutable"""
cities = "Albuquerque Chicago Paris"

print("Cities as a string:", cities)

cities[1] = "Tokyo"

print("This will never print because it is an error to try to change part of a string")


Cities as a string: Albuquerque Chicago Paris
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-21-2e3d125b1f9a> in <module>()
      4 print("Cities as a string:", cities)
      5 
----> 6 cities[1] = "Tokyo"
      7 
      8 print("This will never print because it is an error to try to change part of a string")

TypeError: 'str' object does not support item assignment

nested list

A list that is an element of another list.


In [22]:
"""Lists of lists (nested lists).

The format of hourly_forecasts is a list of days:

hourly_forecasts = [day1, day2, ..., dayN]

Each day has a name and a list of conditions associated with that day for the hours of 1-3 pm:

day = [name, [conditions_at_1pm, conditions_at_2pm, conditions_at_3pm]]

Therefore, hourly_forecasts is, in part, a list of lists of lists.
"""
hourly_forecasts = [["Monday", ["Sunny", "Sunny", "Partly Cloudy"]],
                   ["Tuesday", ["Cloudy", "Cloudy", "Cloudy"]],
                   ["Wednesday", ["Partly Cloudy", "Sunny", "Sunny"]]]
for day in hourly_forecasts:
    name = day[0]
    hourly_conditions = day[1]
    i = 1
    for conditions in hourly_conditions:
        print("The conditions at " + str(i) + "pm on " + name + " are forecast to be " + conditions)
        i += 1


The conditions at 1pm on Monday are forecast to be Sunny
The conditions at 2pm on Monday are forecast to be Sunny
The conditions at 3pm on Monday are forecast to be Partly Cloudy
The conditions at 1pm on Tuesday are forecast to be Cloudy
The conditions at 2pm on Tuesday are forecast to be Cloudy
The conditions at 3pm on Tuesday are forecast to be Cloudy
The conditions at 1pm on Wednesday are forecast to be Partly Cloudy
The conditions at 2pm on Wednesday are forecast to be Sunny
The conditions at 3pm on Wednesday are forecast to be Sunny

object

A thing to which a variable can refer.

In an assignment such as:

lst = [1, 2, 3]

lst is the variable and [1, 2, 3] is the object to which lst refers.

pattern

A sequence of statements, or a style of coding something that has general applicability in a number of different situations. Part of becoming a mature Computer Scientist is to learn and establish the patterns and algorithms that form your toolkit. Patterns often correspond to your “mental chunking”.


In [23]:
"""Direct iteration."""
months = ["January", "February", "March",
          "April", "May", "June", "July",
          "August", "September", "October",
          "November", "December"]
for month in months:
    print(month)


January
February
March
April
May
June
July
August
September
October
November
December

Boiled down to its essential characteristics, direct iteration takes the form:

for element in sequence:
    expression(element)

Where expression typically uses the value in element, for example, to do arithmetic, print, etc.


In [24]:
"""Indexed iteration."""
months = ["January", "February", "March",
          "April", "May", "June", "July",
          "August", "September", "October",
          "November", "December"]
for i in range(len(month)):
    print(months[i])


January
February
March
April
May
June
July
August

Indexed iteration often takes the form:

for index in range(len(sequence)):
    expression(sequence[index])

The example uses range(len(sequence)) to get each index, and sequence[index] to get each element.


In [25]:
"""List accumulation."""
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
squares = []
for i in numbers:
    squares.append(i ** 2)
print(squares)


[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144]

List accumulation is a convenient for creating a new list from an old list by applying a transformation to each element in the old list and appending the result to the new list. It takes the form:

result = []
for element in sequence:
    result.append(expression(element))

Using list accumulation in a function and returning the accumulated value can help make a function pure.

pure function

A function which has no side effects. Pure functions only make changes to the calling program through their return values.


In [26]:
import time
delay = 3

def title_pure_function(lst):
    """This function will create a new list and return it
    instead of changing the list passed into it.
    """
    places_titled = []
    for i in range(len(lst)):
        place = lst[i]
        words = place.split()
        title_cased_words = []
        for word in words:
            first_letter = word[0]
            rest_of_word = word[1:]
            title_cased_words.append(first_letter.upper() + rest_of_word.lower())
        titled = ' '.join(title_cased_words)
        places_titled.append(titled)
    return places_titled
        
places = ["new york", "kansas city", "los angeles", "seattle"]

print("Places:", places, "\n")
time.sleep(delay)

print("Calling title_pure_function(places)...", "\n")
return_value = title_pure_function(places)
time.sleep(delay)

print("The return value of title_pure_function(places) is", return_value, "\n")
time.sleep(delay)

print("Places:", places, "\n")


Places: ['new york', 'kansas city', 'los angeles', 'seattle'] 

Calling title_pure_function(places)... 

The return value of title_pure_function(places) is ['New York', 'Kansas City', 'Los Angeles', 'Seattle'] 

Places: ['new york', 'kansas city', 'los angeles', 'seattle'] 

sequence

Any of the data types that consist of an ordered collection of elements, with each element identified by an index.


In [27]:
"""Lists are sequences."""
lst = [1, 2, 3]

print("lst:    ", lst)
print("lst[0]: ", lst[0])


lst:     [1, 2, 3]
lst[0]:  1

In [28]:
"""Tuples are sequences."""
t = (1, 2, 3)

print("t:    ", t)
print("t[0]: ", t[0])


t:     (1, 2, 3)
t[0]:  1

side effect

A change in the state of a program made by calling a function that is not a result of reading the return value from the function. Side effects can only be produced by modifiers.


In [29]:
def change_forecast(forecast):
    """This modifier function changes the value of the
    forecast without returning anything.
    """
    for i in range(len(forecast)):
        forecast[i] = "Cloudy"

forecast = ["Sunny", "Sunny", "Sunny"]
print("The forecast is", forecast)

return_value = change_forecast(forecast)

print("The return_value from calling change_forecast(forecast) is", return_value)

print("The forecast is", forecast)


The forecast is ['Sunny', 'Sunny', 'Sunny']
The return_value from calling change_forecast(forecast) is None
The forecast is ['Cloudy', 'Cloudy', 'Cloudy']

tuple

A sequential collection of items, similar to a list. Any python object can be an element of a tuple. However, unlike a list, tuples are immutable.


In [30]:
"""Create a tuple from a comma-separated list of values."""
vehicle = (2000, "Ford", "Ranger")
print(vehicle)


(2000, 'Ford', 'Ranger')

In [31]:
"""Parentheses are not required."""
vehicle = 2000, "Ford", "Ranger"
print(vehicle)


(2000, 'Ford', 'Ranger')

In [32]:
"""You can create a tuple from a list."""
vehicle = tuple([2000, "Ford", "Ranger"])
print(vehicle)


(2000, 'Ford', 'Ranger')

In [33]:
"""Tuples are immutable."""
vehicle = (2000, "Ford", "Ranger")
vehicle[2] = "F-150"


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-33-e926e1e8352f> in <module>()
      1 """Tuples are immutable."""
      2 vehicle = (2000, "Ford", "Ranger")
----> 3 vehicle[2] = "F-150"

TypeError: 'tuple' object does not support item assignment

In [34]:
"""Values in tuples can be "unpacked" into multiple variables."""
vehicle = (2000, "Ford", "Ranger")
year, make, model = vehicle  # tuple unpacking
print("Year:", year)
print("Make:", make)
print("Model:", model)
print(vehicle)


Year: 2000
Make: Ford
Model: Ranger
(2000, 'Ford', 'Ranger')