Creating a list from scratch:
a = []
b = [1, 2, 3, 4, 5]
Creating a list from existing lists:
c = b + [20, 30]
d = c[:]
e = list(c)
Non-destructive or destructive?
The operations above are non-destructive.
L[2] = 6
L[1:3] = [9, 8]
L[2:4] = [] # Deleting elements
L[1:1] = [2, 3, 4, 5] # Inserting elements
L[len(L):] = [10, 11] # Appending
L[0:0] = range(-3, 0) # Prepending
Non-destructive or destructive?
All of the operations above are destructive.
append()
adds a single element to a list:
s = [2, 3]
t = [5, 6]
s.append(4)
s.append(t)
t = 0
extend()
adds all the elements in one list to a list:
s = [2, 3]
t = [5, 6]
s.extend(4) # π« Error: 4 is not an iterable!
s.extend(t)
t = 0
Try in PythonTutor. (After deleting the bad line)
Non-destructive or destructive?
append()
and extend()
are destructive.
pop()
removes and returns the last element:
s = [2, 3]
t = [5, 6]
t = s.pop()
remove()
removes the first element equal to the argument:
s = [6, 2, 4, 8, 4]
s.remove(4)
s.remove(9)
Non-destructive or destructive?
pop()
and remove()
are destructive.
Identity: exp0 is exp1
evaluates to True
if both exp0
and exp1
evaluate to the same object
Equality: exp0 == exp1
evaluates to True
if both exp0
and exp1
evaluate to objects containing equal values
list1 = [1,2,3]
list2 = [1,2,3]
are_equal = list1 == list2
identical = list1 is list2
Identical objects always have equal values.
a = ["apples", "bananas"]
b = ["apples", "bananas"]
c = a
if a == b == c:
print("All equal!")
a[1] = "oranges"
if a is c and a == c:
print("A and C are equal AND identical!")
if a == b:
print("A and B are equal!") # Nope!
if b == c:
print("B and C are equal!") # Nope!
Try this in your local friendly Python interpreter:
a = "orange"
b = "orange"
c = "o" + "range"
print(a is b)
print(a is c)
a = 100
b = 100
print(a is b)
print(a is 10 * 10)
print(a == 10 * 10)
a = 100000000000000000
b = 100000000000000000
print(a is b)
print(100000000000000000 is 100000000000000000)
Beware: is
may not act like you expect for strings/numbers!
Does this work? π Yes!
attendees = []
def mark_attendance(name):
attendees.append(name)
print("In attendance:", attendees)
mark_attendance("Emily")
mark_attendance("Cristiano")
mark_attendance("Samantha")
Does this work? πΏ No!
current = 0
def count():
current = current + 1
print("Count:", current)
count()
count()
UnboundLocalError: local variable 'current' referenced before assignment
Action | Global code | Local code |
---|---|---|
Access names that are bound in the global scope? | β Yes | β Yes |
Re-assign names that are bound in the global scope? | π« No (unless declared global ) |
π« No (unless declared global ) |
current = 0
def count():
current = current + 1 # π« Error!
print("Count:", current)
count()
count()
current = 0
def count():
global current
current = current + 1
print("Count:", current)
count()
count()
global
"Just because you can do something in a language, it doesn't mean you should." - Prof Fox
Re-assigning global variables inside functions can lead to more brittle and unpredictable code.
How about...
current = 0
def count(current):
current = current + 1
print("Count:", current)
return current
current = count(current)
current = count(current)
β¨β€οΈπ₯°πΌπβ¨
Does this work? π Yes!
def make_tracker(class_name):
attendees = []
def track_attendance(name):
attendees.append(name)
print(class_name, ": ", attendees)
return track_attendance
tracker = make_tracker("CS61A")
tracker("Emily")
tracker("Cristiano")
tracker("Julian")
Does this work? πΏ No!
def make_counter(start):
current = start
def count():
current = current + 1
print("Count:", current)
return count
counter = make_counter(30)
counter()
counter()
counter()
UnboundLocalError: local variable 'current' referenced before assignment
Can code inside functions... | |
---|---|
Access names that are bound in the enclosing function? | β Yes |
Re-assign names that are bound in the enclosing function? | π« No (unless declared nonlocal ) |
def make_counter(start):
current = start
def count():
current = current + 1 # π« Error!
print("Count:", current)
return count
counter = make_counter(30)
counter()
counter()
counter()
The nonlocal
declaration tells Python to look in the parent frame
for the name lookup.
def make_counter(start):
current = start
def count():
nonlocal current
current = current + 1
print("Count:", current)
return count
counter = make_counter(30)
counter()
counter()
counter()
nonlocal
The nonlocal
keyword was only added to Python 3,
so most code that might use it can be done in more Pythonic ways.
For the example, the counter can be done with a generator:
def make_counter(start):
current = start
while True:
current = current + 1
print("Count:", current)
yield
counter = make_counter(30)
next(counter)
next(counter)
β οΈ But we haven't learned about generators yet! Stay tuned! β οΈ
nonlocal
We could also use a mutable value like a list or dict:
def make_counter(start):
current = [0]
def count():
current[0] = 1
print("Count:", current[0])
return count
counter = make_counter(30)
counter()
counter()
counter()
nonlocal
We saw it earlier when making a pair data abstraction:
def pair(a, b):
def pair_func(which, v=None):
nonlocal a, b
if which == 0:
return a
elif which == 1:
return b
elif which == 2:
a = v
else:
b = v
return pair_func
def left(p):
return p(0)
def right(p):
return p(1)
def set_left(p, v):
p(2, v)
def set_right(p, v):
p(3, v)
aPair = pair(3, 2)
set_left(aPair, 5)
print(left(aPair))
nonlocal
But then we learned about tuples, lists, and dicts...
def pair(a, b):
return [a, b]
def left(p):
return p[0]
def right(p):
return p[1)
def set_left(p, v):
p[0] = v
def set_right(p, v):
p[1] = v
aPair = pair(3, 2)
set_left(aPair, 5)
print(left(aPair))
nonlocal
And we'll soon be learning how to use classes!
class Pair:
def __init__(left, right):
self.left = left
self.right = right
def set_left(left):
self.left = left
def set_right(right):
self.right = right
aPair = Pair(3, 2)
aPair.set_left(5)
print(aPair.left)
β οΈ You don't need to understand that code yet! Stay tuned! β οΈ
nonlocal
or global
Rarely! Once you finish this class, you will have many tools in your toolbox, and you will often find a way to write your code that doesn't need to re-assign names in parent scopes.
Action | Global code | Local code | Nested function code |
---|---|---|---|
Access names that are bound in the global scope? | β Yes | β Yes | β Yes |
Re-assign names that are bound in the global scope? | β Yes | π« No (unless declared global ) |
π« No (unless declared global ) |
Access names in enclosing function? | N/A | N/A | β Yes |
Re-assign names in enclosing function? | N/A | N/A | π« No (unless declared nonlocal )
|