EE1021 Programming for Engineers · Lecture 4 · 2026–2027
def calibrate_voltage(raw_voltage, offset=0.05):
return raw_voltage + offset
v1 = calibrate_voltage(4.92)
v2 = calibrate_voltage(5.08)
v3 = calibrate_voltage(4.97)
print(v1, v2, v3)What repeats? What would become difficult if the experiment produced 100 readings?
The same operation is repeated while only the data changes. A sequence lets one variable hold many related values.
| Type | Example | Ordered | Indexed | Mutable |
|---|---|---|---|---|
| string | “EE1021” | yes | yes | no |
| tuple | (“S1”, 4.92) | yes | yes | no |
| list | [4.92, 5.08, 4.97] | yes | yes | yes |
empty = ()
measurement = ("S1", 4.92)
single = (4.92,)
type(measurement) # tuple
type(single) # tuple
type((4.92)) # float
measurement[0] # "S1"
measurement[-1] # 4.92
measurement[0:1] # ("S1",)Parentheses improve readability, but the comma creates the tuple. Indexing and slicing follow the same rules used with strings.
The number of target variables must match the number of tuple elements.
def reading_limits(readings):
"""Return the minimum and maximum reading."""
return min(readings), max(readings)
low, high = reading_limits([4.92, 5.08, 4.97])
print("Range:", low, "to", high)The function returns one tuple; unpacking gives meaningful names to its two elements.
def describe_sample():
return "T2", 23.5
sensor, value = describe_sample()
value = value + 0.5
print(sensor, value)Predict the output. Does changing value modify the tuple returned by the function?
Check: T2 24.0. Unpacking binds two new names; the original tuple is immutable.
readings = [4.92, 5.08, 4.97, 5.11]
readings[0] # 4.92
readings[-1] # 5.11
readings[1:3] # [5.08, 4.97]
len(readings) # 4
readings[1] = 5.00
print(readings) # [4.92, 5.0, 4.97, 5.11]
mixed = ["S1", 4.92, True, [1, 2]] # any types, even another listPredict the result before running the code.
Check: [10, 21, 31, 40]. Slice assignment mutates the existing list.
Prefer direct iteration unless the index itself has a job.
readings = [4.92, 5.08]
readings.append(4.97) # adds ONE element at the end
print(readings) # [4.92, 5.08, 4.97]
readings.extend([5.11, 5.02]) # adds EACH element of another list
print(readings) # [4.92, 5.08, 4.97, 5.11, 5.02]readings.append(...) is a method: a function that belongs to the list, called with a dot.append adds 1 element; extend adds as many elements as the other list has.Predict both outputs.
Check: [1, 2, [3, 4]]: append adds the whole list as ONE element. [1, 2, 3, 4]: extend adds each element.
queue = ["group-01", "group-03"]
queue.insert(1, "group-02")
# queue is ["group-01", "group-02", "group-03"]
first = [1, 2]
second = [3, 4]
combined = first + second
# combined is [1, 2, 3, 4]
# first and second are unchangedqueue = ["g1", "g2", "g3", "g2"]
queue.remove("g2") # remove first matching value
processed = queue.pop(0) # remove by index and return it
del queue[-1] # remove by index, no returned valueAfter all three lines:
| Operation | Mutates? | Returns |
|---|---|---|
| append, extend, insert | yes | None |
| remove, sort, reverse | yes | None |
| pop | yes | removed element |
| plus, slicing, sorted | no | new list |
Raw is unchanged; ordered is [4.92, 4.97, 5.08]. Calling raw.sort() would mutate raw and return None.
Loop pattern
Equivalent comprehension
readings = [4.10, 4.92, 5.08, 5.70, 4.97]
valid = [value for value in readings if 4.75 <= value <= 5.25]
print(valid)Output: [4.92, 5.08, 4.97]
Use a regular loop when the logic would become difficult to read in one comprehension.
Write a comprehension that converts Celsius readings to Fahrenheit.
Mutation through either name is visible through both names.
original = [4.92, 5.08]
clone = original.copy() # original[:] also makes a shallow copy
clone.append(4.97)The outer lists are separate. A shallow copy does not recursively copy nested objects.
A function can change the caller’s list only by mutating it, never by assigning a new list to its parameter. To give back a new list, return it.
Check: [1, 2, 3] [1, 2, 3] [1, 2, 4]. Names a and b refer to one list; c refers to a separate list.
def clean_readings(readings, low=4.75, high=5.25):
"""Return valid readings without changing the input list."""
return [value for value in readings if low <= value <= high]
raw = [4.10, 4.92, 5.08, 5.70, 4.97]
valid = clean_readings(raw)
minimum, maximum = min(valid), max(valid)
print("Raw:", raw)
print("Valid:", valid)
print("Range:", minimum, "to", maximum)This combines a function, a list comprehension, and tuple unpacking without mutating the input.
Next lecture, dictionaries will give each value a meaningful key.