What should a search variable hold before you know whether a match exists? Set it to None. This gives the variable a value even when the loop finds nothing; leaving it unassigned would make a later lookup fail. section 5.4.5
Search when no urgent task exists
tasks = [{"title": "Homework", "urgent": False}] first_urgent_task = None for task in tasks: if task["urgent"]: first_urgent_task = task break print(first_urgent_task) # None The initial value remains in place because no task matches. section 5.4.5
None is also a useful default when a function needs a fresh list if an argument is omitted. Check with `is None`, then create the list inside the function. A list used directly as a default would be shared by later calls. section 6.1.3
Listing 6.3 Using None as the default value for mutable parameters
def complete_task(task, grouped_tasks=None):
task.status = "completed"
if grouped_tasks is None: #1
grouped_tasks = []
grouped_tasks.append(task.title)
return grouped_tasks
complete_task.__defaults__
# output: (None,)
Here, `grouped_tasks=None` lets each call that omits the argument create its own list before appending a task title. section 6.1.3
A function call can produce None without an explicit `return None`. If execution reaches the end of a function with no return statement, None is its return value. Assigning that call to a variable does not assign the list the function changed. section 6.2.2
def append_task(task, grouped_tasks):
grouped_tasks.append(task)
appended_no_return = append_task("Homework", [])
print(f"Appended: {appended_no_return}")
# output: Appended: None
`append_task` changes the supplied list but has no return statement, so printing the assigned return value produces `Appended: None`. section 6.2.2



