= 5.0 a
Lesson 2
Overview
- Stuff I forgot to teach
- Type casting
- Nesting
- Nested loops
- Nested lists/tuples/dicts/etc.
- References and scope
- References
is
- Copying
- Scope
List index
PA1 Review
1. Stuff I forgot to teach
1.1 Type casting
Converting between types
Use the name of the type as a function to convert a variable to that type
a
5.0
int(a)
5
type(int(a))
int
str(a)
'5.0'
type(str(a))
str
= '123' a
a
'123'
int(a)
123
type(int(a))
int
float(a)
123.0
type(float(a))
float
2. Nesting
2.1 Nested loops
A loop inside a loop
NOTE 1: Be careful about indents!
NOTE 2: You can include if and/or while statements inside nested loops
for i in range(3):
for j in range(3):
print(f'i: {i}, j: {j}')
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
i: 1, j: 1
i: 1, j: 2
i: 2, j: 0
i: 2, j: 1
i: 2, j: 2
for i in range(3):
for j in range(3):
print(f'i: {i}, j: {j}')
print('outside of j loop')
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
outside of j loop
i: 1, j: 0
i: 1, j: 1
i: 1, j: 2
outside of j loop
i: 2, j: 0
i: 2, j: 1
i: 2, j: 2
outside of j loop
2.2 Nested lists/tuples/dicts/etc.
The elements of the main list are lists - access sublists using brackets, then elements of sublists using brackets
= [
nested_list 'a11', 'a12', 'a13'],
['a21', 'a22', 'a23', 'a24'],
['a31', 'a32', 'a33']
[
]print(nested_list)
[['a11', 'a12', 'a13'], ['a21', 'a22', 'a23', 'a24'], ['a31', 'a32', 'a33']]
print(nested_list[1])
['a21', 'a22', 'a23', 'a24']
print(nested_list[1][2])
a23
= [
nested_list 'a11', 'a12', 'a13'],
['a21', 'a22', 'a23', 'a24'],
['a31', 'a32', 'a33'],
['fjls',
134,
'a': {'c': [1, 2, 3], 'd': 123}, 'b': 2}
{
]print(nested_list)
[['a11', 'a12', 'a13'], ['a21', 'a22', 'a23', 'a24'], ['a31', 'a32', 'a33'], 'fjls', 134, {'a': {'c': [1, 2, 3], 'd': 123}, 'b': 2}]
print(nested_list[5])
{'a': {'c': [1, 2, 3], 'd': 123}, 'b': 2}
print(nested_list[5]['a'])
{'c': [1, 2, 3], 'd': 123}
print(nested_list[5]['a']['d'])
123
3. References and scope
What is the effect of modifying a variable?
3.1 References
= 3
a = a
b = 4
b print(f'a: {a}, b: {b}')
a: 3, b: 4
Variables that are lists (dictionaries) are actually references to where the list (dictionary) is stored in memory
= [1, 2, 3]
a = a
b 4)
b.append(print('a:', a)
print('b:', b)
a: [1, 2, 3, 4]
b: [1, 2, 3, 4]
print(id(a))
print(id(b))
4597738112
4597738112
Another variable with the same values does not use the same reference
= [1, 2, 3]
a = [1, 2, 3]
b 4)
b.append(print('a:', a)
print('b:', b)
a: [1, 2, 3]
b: [1, 2, 3, 4]
print(id(a))
print(id(b))
4597860864
4597853440
= {'a': 'hello', 'b': 'goodbye'}
a = a
b 'c'] = 'hellogoodbye'
b[print(a)
print(b)
{'a': 'hello', 'b': 'goodbye', 'c': 'hellogoodbye'}
{'a': 'hello', 'b': 'goodbye', 'c': 'hellogoodbye'}
List equality checks element-wise equality, not reference equality (warning for the future: NumPy has different behavior)
= [1, 2, 3]
a = [1, 2, 3]
b == b a
True
= [1, 2, 3]
a = [1, 2, 4]
b == b a
False
= [[1, 2, 3], [4, 5, 6]]
a = [[1, 2, 3], [4, 5, 6]]
b == b a
True
= [[1, 2, 3], [4, 5, 6]]
a = [[1, 2, 3], [4, 5, 7]]
b == b a
False
3.1.1 is
Use is
to check reference equality
= [[1, 2, 3], [4, 5, 6]]
a = [[1, 2, 3], [4, 5, 6]]
b is b a
False
= [[1, 2, 3], [4, 5, 6]]
a = a
b is b a
True
= 5
a = a
b is b a
True
= 5
a = 5
b is b a
True
3.1.2 Copying
= [1, 2, 3]
a = a.copy()
b 4)
b.append(print('a:', a)
print('b:', b)
a: [1, 2, 3]
b: [1, 2, 3, 4]
Sometimes .copy()
isn’t an option, or doesn’t do what you expect
In particular, nested variables that use references won’t be copied, so we use copy.deepcopy()
to recursively copy all nested variables
= [[1, 2, 3], [4, 5, 6]]
a = a.copy()
b 7, 8, 9])
b.append([0][0] = 0
b[print(a)
print(b)
[[0, 2, 3], [4, 5, 6]]
[[0, 2, 3], [4, 5, 6], [7, 8, 9]]
import copy
= [[1, 2, 3], [4, 5, 6]]
a = copy.deepcopy(a)
b 7, 8, 9])
b.append([0][0] = 0
b[print(a)
print(b)
[[1, 2, 3], [4, 5, 6]]
[[0, 2, 3], [4, 5, 6], [7, 8, 9]]
3.2 Scope
Defining new variables inside a function is a new, smaller scope
= 5
a
def a_to_ten():
= 10
a print('Inside function:', a)
print('Before function:', a)
a_to_ten()print('After function:', a)
Before function: 5
Inside function: 10
After function: 5
global a
= 5
a
def a_to_ten():
global a
print('Inside function:', a)
= 10
a print('Inside function:', a)
print('Before function:', a)
a_to_ten()print('After function:', a)
Before function: 5
Inside function: 5
Inside function: 10
After function: 10
Can still modify lists inside of functions (this is called in-place modification)
= [1, 2, 3]
a
def append_4(a):
4)
a.append(
print('Before function:', a)
append_4(a)print('After function:', a)
Before function: [1, 2, 3]
After function: [1, 2, 3, 4]
This does not depend on calling the function argument a
= [1, 2, 3]
a
def append_4(b):
4)
b.append(
print('Before function:', a)
append_4(a)print('After function:', a)
Before function: [1, 2, 3]
After function: [1, 2, 3, 4]
If we don’t define a
inside the function, it will use a
from outside the function’s scope
= [1, 2, 3]
a
def append_4():
4)
a.append(
print('Before function:', a)
append_4()print('After function:', a)
Before function: [1, 2, 3]
After function: [1, 2, 3, 4]
A variable defined inside a function is not accessible outside the function, unless it is returned
del a
def define_a():
= [1, 2, 3]
a
define_a() a
NameError: name 'a' is not defined
def define_a():
= [1, 2, 3]
a return a
= define_a()
a a
[1, 2, 3]
= [1, 2, 3]
a
def append_4():
= a.copy()
b 4)
b.append(
print('Before function:', a)
append_4()print('After function:', a)
b
Before function: [1, 2, 3]
After function: [1, 2, 3]
[[0, 2, 3], [4, 5, 6], [7, 8, 9]]
These issues don’t arise with strings
= 'hello'
a
def append_4(a):
+= 'goodbye'
a
print('Before function:', a)
append_4(a)print('After function:', a)
Before function: hello
After function: hello
= 'hello'
a += 'goodbye'
a a
'hellogoodbye'
If a variable name is overwritten inside a function, the original, unmodified variable still exists in the outer scope
= [1, 2, 3]
a def append_values_v1(a):
= np.concatenate([a, [4, 5, 6]])
a print('Inside function 1:', a)
def append_values_v2(a):
4, 5, 6])
a.extend([
print('Before function:', a)
append_values_v1(a)print('After function 1:', a)
append_values_v2(a)print('After function 2:', a)
Before function: [1, 2, 3]
NameError: name 'np' is not defined
= 'hello'
a
def append_4(a):
+= 'goodbye'
a return a
print('Before function:', a)
= append_4(a)
a2 print('After function:', a)
print('a2:', a2)
Before function: hello
After function: hello
a2: hellogoodbye
4. List index
Find the index of an element in a list - useful for PA2
= ['a', 'b', 'c']
list1 print(list1.index('a'))
print(list1.index('b'))
print(list1.index('c'))
0
1
2
If the element is not unique, it gives the first entry
= ['a', 'b', 'c', 'a']
list2 print(list2.index('a'))
0
To get all indices for a value
import numpy as np
print(np.nonzero((np.array(list2) == 'a').astype(int))[0])
print(np.where(np.array(list2) == 'a')[0])
[0 3]
[0 3]
5. PA1 review
Discussion and review my solution