Data Types
None Type
Representing Absence
You're searching for a user by email. If found, return the user object. If not
found, what do you return? Not an empty string (that's a valid email). Not zero.
None is Python's way of saying "no value here."
None vs empty values
None is different from empty string, zero, or empty list.
# None is different from "empty" or "zero" values
empty_string = ""
zero = 0
empty_list = []
nothing = None
print("=== Type Comparison ===")
print(f"type(''): {type(empty_string)}")
print(f"type(0): {type(zero)}")
print(f"type([]): {type(empty_list)}")
print(f"type(None): {type(nothing)}")
print("\n=== Truthiness ===")
print(f"bool(''): {bool(empty_string)}") # False (falsy)
print(f"bool(0): {bool(zero)}") # False (falsy)
print(f"bool([]): {bool(empty_list)}") # False (falsy)
print(f"bool(None): {bool(nothing)}") # False (falsy)
print("\n=== Identity ===")
print(f"'' is None: {empty_string is None}") # False
print(f"0 is None: {zero is None}") # False
print(f"[] is None: {empty_list is None}") # False
print(f"None is None: {nothing is None}") # True
# Meaningful difference
score_not_taken = None
score_zero = 0 # Student took test, got 0
print(f"\nNot taken: {score_not_taken}")
print(f"Got zero: {score_zero}")
# None is different from "empty" or "zero" values
empty_string = ""
zero = 0
empty_list = []
nothing = None
print("=== Type Comparison ===")
print(f"type(''): {type(empty_string)}")
print(f"type(0): {type(zero)}")
print(f"type([]): {type(empty_list)}")
print(f"type(None): {type(nothing)}")
print("\n=== Truthiness ===")
print(f"bool(''): {bool(empty_string)}") # False (falsy)
print(f"bool(0): {bool(zero)}") # False (falsy)
print(f"bool([]): {bool(empty_list)}") # False (falsy)
print(f"bool(None): {bool(nothing)}") # False (falsy)
print("\n=== Identity ===")
print(f"'' is None: {empty_string is None}") # False
print(f"0 is None: {zero is None}") # False
print(f"[] is None: {empty_list is None}") # False
print(f"None is None: {nothing is None}") # True
# Meaningful difference
score_not_taken = 0
score_zero = 0 # Student took test, got 0
print(f"\nNot taken: {score_not_taken}")
print(f"Got zero: {score_zero}")
empty_string ← (empty), zero ← 0, empty_list ← [], nothing ← None
1# None is different from "empty" or "zero" values2empty_string→ (empty) = ""3zero→ 0 = 04empty_list→ [] = []5nothing→ None = None67print("=== Type Comparison ===")8print(f"type(''): {type(empty_string(empty))}")9print(f"type(0): {type(zero0)}")10print(f"type([]): {type(empty_list[])}")11print(f"type(None): {type(nothingNone)}")1213print("\n=== Truthiness ===")14print(f"bool(''): {bool(empty_string(empty))}") # False (falsy)15print(f"bool(0): {bool(zero0)}") # False (falsy)16print(f"bool([]): {bool(empty_list[])}") # False (falsy)17print(f"bool(None): {bool(nothingNone)}") # False (falsy)1819print("\n=== Identity ===")20print(f"'' is None: {empty_string(empty) is None}") # False21print(f"0 is None: {zero0 is None}") # False22print(f"[] is None: {empty_list[] is None}") # False23print(f"None is None: {nothingNone is None}") # True2425# Meaningful difference26score_not_taken→ None = None #@score_not_taken=027score_zero→ 0 = 0 # Student took test, got 028print(f"\nNot taken: {score_not_takenNone}")29print(f"Got zero: {score_zero0}")output=== Type Comparison === type(''): <class 'str'> type(0): <class 'int'> type([]): <class 'list'> type(None): <class 'NoneType'> === Truthiness === bool(''): False bool(0): False bool([]): False bool(None): False === Identity === '' is None: False 0 is None: False [] is None: False None is None: True Not taken: None Got zero: 0
empty_string ← (empty), zero ← 0, empty_list ← [], nothing ← None
1# None is different from "empty" or "zero" values2empty_string→ (empty) = ""3zero→ 0 = 04empty_list→ [] = []5nothing→ None = None67print("=== Type Comparison ===")8print(f"type(''): {type(empty_string(empty))}")9print(f"type(0): {type(zero0)}")10print(f"type([]): {type(empty_list[])}")11print(f"type(None): {type(nothingNone)}")1213print("\n=== Truthiness ===")14print(f"bool(''): {bool(empty_string(empty))}") # False (falsy)15print(f"bool(0): {bool(zero0)}") # False (falsy)16print(f"bool([]): {bool(empty_list[])}") # False (falsy)17print(f"bool(None): {bool(nothingNone)}") # False (falsy)1819print("\n=== Identity ===")20print(f"'' is None: {empty_string(empty) is None}") # False21print(f"0 is None: {zero0 is None}") # False22print(f"[] is None: {empty_list[] is None}") # False23print(f"None is None: {nothingNone is None}") # True2425# Meaningful difference26score_not_taken→ 0 = 027score_zero→ 0 = 0 # Student took test, got 028print(f"\nNot taken: {score_not_taken0}")29print(f"Got zero: {score_zero0}")output=== Type Comparison === type(''): <class 'str'> type(0): <class 'int'> type([]): <class 'list'> type(None): <class 'NoneType'> === Truthiness === bool(''): False bool(0): False bool([]): False bool(None): False === Identity === '' is None: False 0 is None: False [] is None: False None is None: True Not taken: 0 Got zero: 0
Use None when there's truly no value, not just an empty or zero value.
Optional return values
Functions can return None to indicate "no result found".
def find_index(items, target):
"""Find target in list, return index or None if not found."""
for i, item in enumerate(items):
if item == target:
return i
return None
def get_user_name(user_id):
"""Look up user, return name or None if not found."""
users = {1: "Alice", 2: "Bob", 3: "Charlie"}
return users.get(user_id) # dict.get returns None for missing keys
# Using functions with optional returns
numbers = [10, 20, 30, 40, 50]
target = 30
result = find_index(numbers, target)
if result is not None:
print(f"Found {target} at index {result}")
else:
print(f"{target} not found")
# Looking up users
user_id = 2
name = get_user_name(user_id)
if name is not None:
print(f"User {user_id}: {name}")
else:
print(f"User {user_id} not found")
def find_index(items, target):
"""Find target in list, return index or None if not found."""
for i, item in enumerate(items):
if item == target:
return i
return None
def get_user_name(user_id):
"""Look up user, return name or None if not found."""
users = {1: "Alice", 2: "Bob", 3: "Charlie"}
return users.get(user_id) # dict.get returns None for missing keys
# Using functions with optional returns
numbers = [5, 10, 15, 20]
target = 30
result = find_index(numbers, target)
if result is not None:
print(f"Found {target} at index {result}")
else:
print(f"{target} not found")
# Looking up users
user_id = 2
name = get_user_name(user_id)
if name is not None:
print(f"User {user_id}: {name}")
else:
print(f"User {user_id} not found")
def find_index(items, target):
"""Find target in list, return index or None if not found."""
for i, item in enumerate(items):
if item == target:
return i
return None
def get_user_name(user_id):
"""Look up user, return name or None if not found."""
users = {1: "Alice", 2: "Bob", 3: "Charlie"}
return users.get(user_id) # dict.get returns None for missing keys
# Using functions with optional returns
numbers = [10, 20, 30, 40, 50]
target = 25
result = find_index(numbers, target)
if result is not None:
print(f"Found {target} at index {result}")
else:
print(f"{target} not found")
# Looking up users
user_id = 2
name = get_user_name(user_id)
if name is not None:
print(f"User {user_id}: {name}")
else:
print(f"User {user_id} not found")
def find_index(items, target):
"""Find target in list, return index or None if not found."""
for i, item in enumerate(items):
if item == target:
return i
return None
def get_user_name(user_id):
"""Look up user, return name or None if not found."""
users = {1: "Alice", 2: "Bob", 3: "Charlie"}
return users.get(user_id) # dict.get returns None for missing keys
# Using functions with optional returns
numbers = [10, 20, 30, 40, 50]
target = 30
result = find_index(numbers, target)
if result is not None:
print(f"Found {target} at index {result}")
else:
print(f"{target} not found")
# Looking up users
user_id = 1
name = get_user_name(user_id)
if name is not None:
print(f"User {user_id}: {name}")
else:
print(f"User {user_id} not found")
def find_index(items, target):
"""Find target in list, return index or None if not found."""
for i, item in enumerate(items):
if item == target:
return i
return None
def get_user_name(user_id):
"""Look up user, return name or None if not found."""
users = {1: "Alice", 2: "Bob", 3: "Charlie"}
return users.get(user_id) # dict.get returns None for missing keys
# Using functions with optional returns
numbers = [10, 20, 30, 40, 50]
target = 30
result = find_index(numbers, target)
if result is not None:
print(f"Found {target} at index {result}")
else:
print(f"{target} not found")
# Looking up users
user_id = 99
name = get_user_name(user_id)
if name is not None:
print(f"User {user_id}: {name}")
else:
print(f"User {user_id} not found")
numbers ← [10, 20, 30, 40, 50], target ← 30
13# Using functions with optional returns14numbers→ [10, 20, 30, 40, 50] = [10, 20, 30, 40, 50] #@numbers=[5, 10, 15, 20]15target→ 30 = 30 #@target=25, 301617result = find_index(numbers[10, 20, 30, 40, 50], target30)18if result is not None:def find_index(items, target):
1def find_index(items[10, 20, 30, 40, 50], target30):2 """Find target in list, return index or None if not found."""3 for i, item in enumerate(items):for i, item in enumerate(items):
pass 1 of 32"""Find target in list, return index or None if not found."""3for i0, item10 in enumerate(items[10, 20, 30, 40, 50]):4 if item == target:5 return iAll 3 passes — pass 1 is the card above pass iitemtarget1 0 10 — 2 1 20 — 3 2 30 30 if item == target:
3for i, item in enumerate(items):4 if item30 == target30:5 return i26return None #?return_noneresult ← 2
17result→ 2 = find_index(numbers[10, 20, 30, 40, 50], target30)18if result is not None:if result is not None:
17result = find_index(numbers, target)18if result2 is not None:19 print(f"Found {target30} at index {result2}")20else:outputFound 30 at index 2user_id ← 2
23# Looking up users24user_id→ 2 = 2 #@user_id=1, 9925name = get_user_name(user_id2)26if name is not None:users ← {1: 'Alice', 2: 'Bob', 3: 'Charlie'}
8def get_user_name(user_id2):9 """Look up user, return name or None if not found."""10 users→ {1: 'Alice', 2: 'Bob', 3: 'Charlie'} = {1: "Alice", 2: "Bob", 3: "Charlie"}11 return users{1: 'Alice', 2: 'Bob', 3: 'Charlie'}.get(user_id2) # dict.get returns None for missing keysname ← Bob
24user_id = 2 #@user_id=1, 9925name→ Bob = get_user_name(user_id2)26if name is not None:if name is not None:
25name = get_user_name(user_id)26if nameBob is not None:27 print(f"User {user_id2}: {nameBob}")28else:outputUser 2: Bob
numbers ← [5, 10, 15, 20], target ← 30
13# Using functions with optional returns14numbers→ [5, 10, 15, 20] = [5, 10, 15, 20]15target→ 30 = 301617result = find_index(numbers[5, 10, 15, 20], target30)18if result is not None:def find_index(items, target):
1def find_index(items[5, 10, 15, 20], target30):2 """Find target in list, return index or None if not found."""3 for i, item in enumerate(items):for i, item in enumerate(items):
pass 1 of 42"""Find target in list, return index or None if not found."""3for i0, item5 in enumerate(items[5, 10, 15, 20]):4 if item == target:5 return iAll 4 passes — pass 1 is the card above pass iitem1 0 5 2 1 10 3 2 15 4 3 20 return None
5 return i6return Noneresult ← None
17result→ None = find_index(numbers[5, 10, 15, 20], target30)18if result is not None:else:
18if result is not None:19 print(f"Found {target} at index {result}")20else:21 print(f"{target30} not found")output30 not founduser_id ← 2
23# Looking up users24user_id→ 2 = 225name = get_user_name(user_id2)26if name is not None:users ← {1: 'Alice', 2: 'Bob', 3: 'Charlie'}
8def get_user_name(user_id2):9 """Look up user, return name or None if not found."""10 users→ {1: 'Alice', 2: 'Bob', 3: 'Charlie'} = {1: "Alice", 2: "Bob", 3: "Charlie"}11 return users{1: 'Alice', 2: 'Bob', 3: 'Charlie'}.get(user_id2) # dict.get returns None for missing keysname ← Bob
24user_id = 225name→ Bob = get_user_name(user_id2)26if name is not None:if name is not None:
25name = get_user_name(user_id)26if nameBob is not None:27 print(f"User {user_id2}: {nameBob}")28else:outputUser 2: Bob
numbers ← [10, 20, 30, 40, 50], target ← 25
13# Using functions with optional returns14numbers→ [10, 20, 30, 40, 50] = [10, 20, 30, 40, 50]15target→ 25 = 251617result = find_index(numbers[10, 20, 30, 40, 50], target25)18if result is not None:def find_index(items, target):
1def find_index(items[10, 20, 30, 40, 50], target25):2 """Find target in list, return index or None if not found."""3 for i, item in enumerate(items):for i, item in enumerate(items):
pass 1 of 52"""Find target in list, return index or None if not found."""3for i0, item10 in enumerate(items[10, 20, 30, 40, 50]):4 if item == target:5 return iAll 5 passes — pass 1 is the card above pass iitem1 0 10 2 1 20 3 2 30 4 3 40 5 4 50 return None
5 return i6return Noneresult ← None
17result→ None = find_index(numbers[10, 20, 30, 40, 50], target25)18if result is not None:else:
18if result is not None:19 print(f"Found {target} at index {result}")20else:21 print(f"{target25} not found")output25 not founduser_id ← 2
23# Looking up users24user_id→ 2 = 225name = get_user_name(user_id2)26if name is not None:users ← {1: 'Alice', 2: 'Bob', 3: 'Charlie'}
8def get_user_name(user_id2):9 """Look up user, return name or None if not found."""10 users→ {1: 'Alice', 2: 'Bob', 3: 'Charlie'} = {1: "Alice", 2: "Bob", 3: "Charlie"}11 return users{1: 'Alice', 2: 'Bob', 3: 'Charlie'}.get(user_id2) # dict.get returns None for missing keysname ← Bob
24user_id = 225name→ Bob = get_user_name(user_id2)26if name is not None:if name is not None:
25name = get_user_name(user_id)26if nameBob is not None:27 print(f"User {user_id2}: {nameBob}")28else:outputUser 2: Bob
numbers ← [10, 20, 30, 40, 50], target ← 30
13# Using functions with optional returns14numbers→ [10, 20, 30, 40, 50] = [10, 20, 30, 40, 50]15target→ 30 = 301617result = find_index(numbers[10, 20, 30, 40, 50], target30)18if result is not None:def find_index(items, target):
1def find_index(items[10, 20, 30, 40, 50], target30):2 """Find target in list, return index or None if not found."""3 for i, item in enumerate(items):for i, item in enumerate(items):
pass 1 of 32"""Find target in list, return index or None if not found."""3for i0, item10 in enumerate(items[10, 20, 30, 40, 50]):4 if item == target:5 return iAll 3 passes — pass 1 is the card above pass iitemtarget1 0 10 — 2 1 20 — 3 2 30 30 if item == target:
3for i, item in enumerate(items):4 if item30 == target30:5 return i26return Noneresult ← 2
17result→ 2 = find_index(numbers[10, 20, 30, 40, 50], target30)18if result is not None:if result is not None:
17result = find_index(numbers, target)18if result2 is not None:19 print(f"Found {target30} at index {result2}")20else:outputFound 30 at index 2user_id ← 1
23# Looking up users24user_id→ 1 = 125name = get_user_name(user_id1)26if name is not None:users ← {1: 'Alice', 2: 'Bob', 3: 'Charlie'}
8def get_user_name(user_id1):9 """Look up user, return name or None if not found."""10 users→ {1: 'Alice', 2: 'Bob', 3: 'Charlie'} = {1: "Alice", 2: "Bob", 3: "Charlie"}11 return users{1: 'Alice', 2: 'Bob', 3: 'Charlie'}.get(user_id1) # dict.get returns None for missing keysname ← Alice
24user_id = 125name→ Alice = get_user_name(user_id1)26if name is not None:if name is not None:
25name = get_user_name(user_id)26if nameAlice is not None:27 print(f"User {user_id1}: {nameAlice}")28else:outputUser 1: Alice
numbers ← [10, 20, 30, 40, 50], target ← 30
13# Using functions with optional returns14numbers→ [10, 20, 30, 40, 50] = [10, 20, 30, 40, 50]15target→ 30 = 301617result = find_index(numbers[10, 20, 30, 40, 50], target30)18if result is not None:def find_index(items, target):
1def find_index(items[10, 20, 30, 40, 50], target30):2 """Find target in list, return index or None if not found."""3 for i, item in enumerate(items):for i, item in enumerate(items):
pass 1 of 32"""Find target in list, return index or None if not found."""3for i0, item10 in enumerate(items[10, 20, 30, 40, 50]):4 if item == target:5 return iAll 3 passes — pass 1 is the card above pass iitemtarget1 0 10 — 2 1 20 — 3 2 30 30 if item == target:
3for i, item in enumerate(items):4 if item30 == target30:5 return i26return Noneresult ← 2
17result→ 2 = find_index(numbers[10, 20, 30, 40, 50], target30)18if result is not None:if result is not None:
17result = find_index(numbers, target)18if result2 is not None:19 print(f"Found {target30} at index {result2}")20else:outputFound 30 at index 2user_id ← 99
23# Looking up users24user_id→ 99 = 9925name = get_user_name(user_id99)26if name is not None:users ← {1: 'Alice', 2: 'Bob', 3: 'Charlie'}
8def get_user_name(user_id99):9 """Look up user, return name or None if not found."""10 users→ {1: 'Alice', 2: 'Bob', 3: 'Charlie'} = {1: "Alice", 2: "Bob", 3: "Charlie"}11 return users{1: 'Alice', 2: 'Bob', 3: 'Charlie'}.get(user_id99) # dict.get returns None for missing keysname ← None
24user_id = 9925name→ None = get_user_name(user_id99)26if name is not None:else:
26if name is not None:27 print(f"User {user_id}: {name}")28else:29 print(f"User {user_id99} not found")outputUser 99 not found
None as a return value means the operation didn't produce a result.
Default parameter values
Use None as a default when you can't use a mutable default.
# BAD: Mutable default argument (don't do this!)
def bad_append(item, items=[]):
items.append(item)
return items
# GOOD: Use None as default, create new list inside
def good_append(item, items=None):
if items is None:
items = []
items.append(item)
return items
# Demonstrate the problem
print("=== Bad function (shared list!) ===")
result1 = bad_append(1)
print(f"First call: {result1}")
result2 = bad_append(2)
print(f"Second call: {result2}") # Contains both! Bug!
print("\n=== Good function (fresh list each time) ===")
result3 = good_append(1)
print(f"First call: {result3}")
result4 = good_append(2)
print(f"Second call: {result4}") # Only has 2
# Practical example: optional timestamp
from datetime import datetime
def log_message(message, timestamp=None):
if timestamp is None:
timestamp = datetime(2025, 1, 15, 10, 30)
print(f"[{timestamp}] {message}")
log_message("Hello")
log_message("Custom time", datetime(2024, 1, 1, 12, 0))
print("=== Bad function (shared list!) ===")
13# Demonstrate the problem14print("=== Bad function (shared list!) ===")15result1 = bad_append(1)16print(f"First call: {result1}")output=== Bad function (shared list!) ===items ← [1]
pass 1 of 21# BAD: Mutable default argument (don't do this!)2def bad_append(item1, items[]=[]): #?mutable_default3 items→ [1].append(item1)4 return items[1]result1 ← [1]
14print("=== Bad function (shared list!) ===")15result1→ [1] = bad_append(1)16print(f"First call: {result1[1]}")17result2 = bad_append(2)18print(f"Second call: {result2}") # Contains both! Bug!outputFirst call: [1]items ← [1, 2]
pass 2 of 21# BAD: Mutable default argument (don't do this!)2def bad_append(item2, items[1]=[]): #?mutable_default3 items→ [1, 2].append(item2)4 return items[1, 2]result2 ← [1, 2]
16print(f"First call: {result1}")17result2→ [1, 2] = bad_append(2)18print(f"Second call: {result2[1, 2]}") # Contains both! Bug!1920print("\n=== Good function (fresh list each time) ===")21result3 = good_append(1)22print(f"First call: {result3}")outputSecond call: [1, 2] === Good function (fresh list each time) ===def good_append(item, items=None):
pass 1 of 26# GOOD: Use None as default, create new list inside7def good_append(item1, itemsNone=NoneNone):8 if items is None:9 items = []items ← []
pass 1 of 27def good_append(item, items=None):8 if itemsNone is None:9 items→ [] = []10 items.append(item)items ← [1]
9 items = []10items→ [1].append(item1)11return items[1]result3 ← [1]
20print("\n=== Good function (fresh list each time) ===")21result3→ [1] = good_append(1)22print(f"First call: {result3[1]}")23result4 = good_append(2)24print(f"Second call: {result4}") # Only has 2outputFirst call: [1]def good_append(item, items=None):
pass 2 of 26# GOOD: Use None as default, create new list inside7def good_append(item2, itemsNone=NoneNone):8 if items is None:9 items = []items ← []
pass 2 of 27def good_append(item, items=None):8 if itemsNone is None:9 items→ [] = []10 items.append(item)items ← [2]
9 items = []10items→ [2].append(item2)11return items[2]result4 ← [2]
22print(f"First call: {result3}")23result4→ [2] = good_append(2)24print(f"Second call: {result4[2]}") # Only has 22526# Practical example: optional timestamp27from datetime import datetime2829def log_message(message, timestamp=None):30 if timestamp is None:31 timestamp = datetime(2025, 1, 15, 10, 30)32 print(f"[{timestamp}] {message}")3334log_message("Hello")35log_message("Custom time", datetime(2024, 1, 1, 12, 0))outputSecond call: [2]def log_message(message, timestamp=None):
pass 1 of 229def log_message(messageHello, timestampNone=NoneNone):30 if timestamp is None:31 timestamp = datetime(2025, 1, 15, 10, 30)timestamp ← 2025-01-15 10:30:00
29def log_message(message, timestamp=None):30 if timestampNone is None:31 timestamp→ 2025-01-15 10:30:00 = datetime(2025, 1, 15, 10, 30)32 print(f"[{timestamp}] {message}")print(f"[{timestamp}] {message}")
31 timestamp = datetime(2025, 1, 15, 10, 30)32print(f"[{timestamp2025-01-15 10:30:00}] {messageHello}")output[2025-01-15 10:30:00] Hellolog_message("Hello")
34log_message("Hello")35log_message("Custom time", datetime(2024, 1, 1, 12, 0))def log_message(message, timestamp=None):
pass 2 of 229def log_message(messageCustom time, timestamp2024-01-01 12:00:00=NoneNone):30 if timestamp is None:31 timestamp = datetime(2025, 1, 15, 10, 30)32 print(f"[{timestamp2024-01-01 12:00:00}] {messageCustom time}")output[2024-01-01 12:00:00] Custom timelog_message("Custom time", datetime(2024, 1, 1, 12, 0))
34log_message("Hello")35log_message("Custom time", datetime(2024, 1, 1, 12, 0))
Never use [] or {} as default parameters - use None instead.
Checking for None
Use is None or is not None for explicit None checks.
value = None
# Preferred: identity check with 'is'
if value is None:
print("value is None")
else:
print(f"value is: {value}")
# Also valid for 'not None'
if value is not None:
print("value exists")
else:
print("value is missing")
# Why 'is' instead of '=='?
print("\n=== is vs == ===")
x = None
print(f"x is None: {x is None}") # True - identity
print(f"x == None: {x == None}") # True - equality
# 'is' is faster and clearer for None
# == can be overridden by custom classes
# Common pattern: guard clause
def process(data):
if data is None:
print("No data to process")
return
print(f"Processing: {data}")
process(None)
process("some data")
# Truthiness vs explicit None check
name = ""
# These behave differently!
if name: # Checks truthiness - fails for empty string
print(f"Truthy: {name}")
if name is not None: # Checks for None specifically - passes for ""
print(f"Not None: '{name}'")
value = "hello"
# Preferred: identity check with 'is'
if value is None:
print("value is None")
else:
print(f"value is: {value}")
# Also valid for 'not None'
if value is not None:
print("value exists")
else:
print("value is missing")
# Why 'is' instead of '=='?
print("\n=== is vs == ===")
x = None
print(f"x is None: {x is None}") # True - identity
print(f"x == None: {x == None}") # True - equality
# 'is' is faster and clearer for None
# == can be overridden by custom classes
# Common pattern: guard clause
def process(data):
if data is None:
print("No data to process")
return
print(f"Processing: {data}")
process(None)
process("some data")
# Truthiness vs explicit None check
name = ""
# These behave differently!
if name: # Checks truthiness - fails for empty string
print(f"Truthy: {name}")
if name is not None: # Checks for None specifically - passes for ""
print(f"Not None: '{name}'")
value = 0
# Preferred: identity check with 'is'
if value is None:
print("value is None")
else:
print(f"value is: {value}")
# Also valid for 'not None'
if value is not None:
print("value exists")
else:
print("value is missing")
# Why 'is' instead of '=='?
print("\n=== is vs == ===")
x = None
print(f"x is None: {x is None}") # True - identity
print(f"x == None: {x == None}") # True - equality
# 'is' is faster and clearer for None
# == can be overridden by custom classes
# Common pattern: guard clause
def process(data):
if data is None:
print("No data to process")
return
print(f"Processing: {data}")
process(None)
process("some data")
# Truthiness vs explicit None check
name = ""
# These behave differently!
if name: # Checks truthiness - fails for empty string
print(f"Truthy: {name}")
if name is not None: # Checks for None specifically - passes for ""
print(f"Not None: '{name}'")
value = None
# Preferred: identity check with 'is'
if value is None:
print("value is None")
else:
print(f"value is: {value}")
# Also valid for 'not None'
if value is not None:
print("value exists")
else:
print("value is missing")
# Why 'is' instead of '=='?
print("\n=== is vs == ===")
x = None
print(f"x is None: {x is None}") # True - identity
print(f"x == None: {x == None}") # True - equality
# 'is' is faster and clearer for None
# == can be overridden by custom classes
# Common pattern: guard clause
def process(data):
if data is None:
print("No data to process")
return
print(f"Processing: {data}")
process(None)
process("some data")
# Truthiness vs explicit None check
name = "Alice"
# These behave differently!
if name: # Checks truthiness - fails for empty string
print(f"Truthy: {name}")
if name is not None: # Checks for None specifically - passes for ""
print(f"Not None: '{name}'")
value = None
# Preferred: identity check with 'is'
if value is None:
print("value is None")
else:
print(f"value is: {value}")
# Also valid for 'not None'
if value is not None:
print("value exists")
else:
print("value is missing")
# Why 'is' instead of '=='?
print("\n=== is vs == ===")
x = None
print(f"x is None: {x is None}") # True - identity
print(f"x == None: {x == None}") # True - equality
# 'is' is faster and clearer for None
# == can be overridden by custom classes
# Common pattern: guard clause
def process(data):
if data is None:
print("No data to process")
return
print(f"Processing: {data}")
process(None)
process("some data")
# Truthiness vs explicit None check
name = None
# These behave differently!
if name: # Checks truthiness - fails for empty string
print(f"Truthy: {name}")
if name is not None: # Checks for None specifically - passes for ""
print(f"Not None: '{name}'")
value ← None
1value→ None = None #@value="hello", 0if value is None:
3# Preferred: identity check with 'is'4if valueNone is None:5 print("value is None")6else:outputvalue is Noneelse:
10if value is not None:11 print("value exists")12else:13 print("value is missing")outputvalue is missingx ← None
15# Why 'is' instead of '=='?16print("\n=== is vs == ===")17x→ None = None18print(f"x is None: {xNone is None}") # True - identity19print(f"x == None: {xNone == None}") # True - equality2021# 'is' is faster and clearer for None22# == can be overridden by custom classes2324# Common pattern: guard clause25def process(data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {data}")3031process(None)32process("some data")output === is vs == === x is None: True x == None: Truedef process(data):
pass 1 of 224# Common pattern: guard clause25def process(dataNone):26 if data is None:27 print("No data to process")if data is None:
25def process(data):26 if dataNone is None:27 print("No data to process")28 return29 print(f"Processing: {data}")outputNo data to processprocess(None)
31process(None)32process("some data")def process(data):
pass 2 of 224# Common pattern: guard clause25def process(datasome data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {datasome data}")outputProcessing: some dataname ← (empty)
31process(None)32process("some data")3334# Truthiness vs explicit None check35name→ (empty) = "" #@name="Alice", Noneif name is not None: # Checks for None specifically - passes for ""
39 print(f"Truthy: {name}")40if name(empty) is not None: # Checks for None specifically - passes for ""41 print(f"Not None: '{name(empty)}'")outputNot None: ''
value ← hello
1value→ hello = "hello"else:
4if value is None:5 print("value is None")6else:7 print(f"value is: {valuehello}")outputvalue is: helloif value is not None:
9# Also valid for 'not None'10if valuehello is not None:11 print("value exists")12else:outputvalue existsx ← None
15# Why 'is' instead of '=='?16print("\n=== is vs == ===")17x→ None = None18print(f"x is None: {xNone is None}") # True - identity19print(f"x == None: {xNone == None}") # True - equality2021# 'is' is faster and clearer for None22# == can be overridden by custom classes2324# Common pattern: guard clause25def process(data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {data}")3031process(None)32process("some data")output === is vs == === x is None: True x == None: Truedef process(data):
pass 1 of 224# Common pattern: guard clause25def process(dataNone):26 if data is None:27 print("No data to process")if data is None:
25def process(data):26 if dataNone is None:27 print("No data to process")28 return29 print(f"Processing: {data}")outputNo data to processprocess(None)
31process(None)32process("some data")def process(data):
pass 2 of 224# Common pattern: guard clause25def process(datasome data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {datasome data}")outputProcessing: some dataname ← (empty)
31process(None)32process("some data")3334# Truthiness vs explicit None check35name→ (empty) = ""if name is not None: # Checks for None specifically - passes for ""
39 print(f"Truthy: {name}")40if name(empty) is not None: # Checks for None specifically - passes for ""41 print(f"Not None: '{name(empty)}'")outputNot None: ''
value ← 0
1value→ 0 = 0else:
4if value is None:5 print("value is None")6else:7 print(f"value is: {value0}")outputvalue is: 0if value is not None:
9# Also valid for 'not None'10if value0 is not None:11 print("value exists")12else:outputvalue existsx ← None
15# Why 'is' instead of '=='?16print("\n=== is vs == ===")17x→ None = None18print(f"x is None: {xNone is None}") # True - identity19print(f"x == None: {xNone == None}") # True - equality2021# 'is' is faster and clearer for None22# == can be overridden by custom classes2324# Common pattern: guard clause25def process(data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {data}")3031process(None)32process("some data")output === is vs == === x is None: True x == None: Truedef process(data):
pass 1 of 224# Common pattern: guard clause25def process(dataNone):26 if data is None:27 print("No data to process")if data is None:
25def process(data):26 if dataNone is None:27 print("No data to process")28 return29 print(f"Processing: {data}")outputNo data to processprocess(None)
31process(None)32process("some data")def process(data):
pass 2 of 224# Common pattern: guard clause25def process(datasome data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {datasome data}")outputProcessing: some dataname ← (empty)
31process(None)32process("some data")3334# Truthiness vs explicit None check35name→ (empty) = ""if name is not None: # Checks for None specifically - passes for ""
39 print(f"Truthy: {name}")40if name(empty) is not None: # Checks for None specifically - passes for ""41 print(f"Not None: '{name(empty)}'")outputNot None: ''
value ← None
1value→ None = Noneif value is None:
3# Preferred: identity check with 'is'4if valueNone is None:5 print("value is None")6else:outputvalue is Noneelse:
10if value is not None:11 print("value exists")12else:13 print("value is missing")outputvalue is missingx ← None
15# Why 'is' instead of '=='?16print("\n=== is vs == ===")17x→ None = None18print(f"x is None: {xNone is None}") # True - identity19print(f"x == None: {xNone == None}") # True - equality2021# 'is' is faster and clearer for None22# == can be overridden by custom classes2324# Common pattern: guard clause25def process(data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {data}")3031process(None)32process("some data")output === is vs == === x is None: True x == None: Truedef process(data):
pass 1 of 224# Common pattern: guard clause25def process(dataNone):26 if data is None:27 print("No data to process")if data is None:
25def process(data):26 if dataNone is None:27 print("No data to process")28 return29 print(f"Processing: {data}")outputNo data to processprocess(None)
31process(None)32process("some data")def process(data):
pass 2 of 224# Common pattern: guard clause25def process(datasome data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {datasome data}")outputProcessing: some dataname ← Alice
31process(None)32process("some data")3334# Truthiness vs explicit None check35name→ Alice = "Alice"if name: # Checks truthiness - fails for empty string
37# These behave differently!38if nameAlice: # Checks truthiness - fails for empty string39 print(f"Truthy: {nameAlice}")40if name is not None: # Checks for None specifically - passes for ""outputTruthy: Aliceif name is not None: # Checks for None specifically - passes for ""
39 print(f"Truthy: {name}")40if nameAlice is not None: # Checks for None specifically - passes for ""41 print(f"Not None: '{nameAlice}'")outputNot None: 'Alice'
value ← None
1value→ None = Noneif value is None:
3# Preferred: identity check with 'is'4if valueNone is None:5 print("value is None")6else:outputvalue is Noneelse:
10if value is not None:11 print("value exists")12else:13 print("value is missing")outputvalue is missingx ← None
15# Why 'is' instead of '=='?16print("\n=== is vs == ===")17x→ None = None18print(f"x is None: {xNone is None}") # True - identity19print(f"x == None: {xNone == None}") # True - equality2021# 'is' is faster and clearer for None22# == can be overridden by custom classes2324# Common pattern: guard clause25def process(data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {data}")3031process(None)32process("some data")output === is vs == === x is None: True x == None: Truedef process(data):
pass 1 of 224# Common pattern: guard clause25def process(dataNone):26 if data is None:27 print("No data to process")if data is None:
25def process(data):26 if dataNone is None:27 print("No data to process")28 return29 print(f"Processing: {data}")outputNo data to processprocess(None)
31process(None)32process("some data")def process(data):
pass 2 of 224# Common pattern: guard clause25def process(datasome data):26 if data is None:27 print("No data to process")28 return29 print(f"Processing: {datasome data}")outputProcessing: some dataname ← None
31process(None)32process("some data")3334# Truthiness vs explicit None check35name→ None = None
None in collections
Lists and dicts can contain None values - useful for optional fields.
# None in a list
scores = [85, None, 92, None, 78] # Some students didn't take test
print(f"Scores: {scores}")
print(f"Length: {len(scores)}") # 5 elements (None counts!)
# Filter out None values
valid_scores = [s for s in scores if s is not None]
print(f"Valid scores: {valid_scores}")
print(f"Average: {sum(valid_scores) / len(valid_scores)}")
# Count None values
none_count = scores.count(None)
print(f"Missing: {none_count}")
# None in dict (optional fields)
person = {
"name": "Alice",
"email": "alice@example.com",
"phone": None
}
print(f"\nPerson: {person}")
# Check if field exists vs is None
print(f"'phone' in person: {'phone' in person}") # True (key exists)
print(f"person['phone'] is None: {person['phone'] is None}") # True (value is None)
print(f"'fax' in person: {'fax' in person}") # False (key doesn't exist)
# Safe access with .get()
fax = person.get('fax') # Returns None for missing key
print(f"person.get('fax'): {fax}")
scores ← [85, None, 92, None, 78], valid_scores ← [85, 92, 78]
1# None in a list2scores→ [85, None, 92, None, 78] = [85, None, 92, None, 78] # Some students didn't take test3print(f"Scores: {scores[85, None, 92, None, 78]}")4print(f"Length: {len(scores[85, None, 92, None, 78])}") # 5 elements (None counts!)56# Filter out None values7valid_scores→ [85, 92, 78] = [s for s in scores[85, None, 92, None, 78] if s is not None]8print(f"Valid scores: {valid_scores[85, 92, 78]}")9print(f"Average: {sum(valid_scores[85, 92, 78]) / len(valid_scores)}")1011# Count None values12none_count→ 2 = scores[85, None, 92, None, 78].count(None)13print(f"Missing: {none_count2}")1415# None in dict (optional fields)16person→ {'name': 'Alice', 'email': 'alice@example.com', 'phone': None} = {17 "name": "Alice",18 "email": "alice@example.com",19 "phone": None #?optional_field20}2122print(f"\nPerson: {person{'name': 'Alice', 'email': 'alice@example.com', 'phone': None}}")2324# Check if field exists vs is None25print(f"'phone' in person: {'phone' in person{'name': 'Alice', 'email': 'alice@example.com', 'phone': None}}") # True (key exists)26print(f"person['phone'] is None: {person['phone']None is None}") # True (value is None)27print(f"'fax' in person: {'fax' in person{'name': 'Alice', 'email': 'alice@example.com', 'phone': None}}") # False (key doesn't exist)2829# Safe access with .get()30fax→ None = person{'name': 'Alice', 'email': 'alice@example.com', 'phone': None}.get('fax') # Returns None for missing key31print(f"person.get('fax'): {faxNone}")outputScores: [85, None, 92, None, 78] Length: 5 Valid scores: [85, 92, 78] Average: 85.0 Missing: 2 Person: {'name': 'Alice', 'email': 'alice@example.com', 'phone': None} 'phone' in person: True person['phone'] is None: True 'fax' in person: False person.get('fax'): None
None in a list is a valid element, different from the element not existing.
Exercise: none_patterns.py
Explore common None patterns: Optional, guard clauses, null coalescing