Walrus Operator (:=)
1. Giới thiệu
Phần tiêu đề “1. Giới thiệu”Walrus Operator (:=) là một tính năng mới được thêm vào Python 3.8, còn gọi là Assignment Expression (biểu thức gán).
Tên gọi: Ký hiệu := giống hình con hải mã (walrus) nằm nghiêng nên được gọi là “Walrus Operator”.
Tác dụng:
- Gán giá trị cho biến TRONG một biểu thức
- Giảm code trùng lặp
- Tăng hiệu suất (tính toán 1 lần)
- Code ngắn gọn hơn
2. Cú pháp cơ bản
Phần tiêu đề “2. Cú pháp cơ bản”2.1 - So sánh với gán thông thường
Phần tiêu đề “2.1 - So sánh với gán thông thường”Gán thông thường:
# Gán rồi mới dùng (2 dòng)x = 10if x > 5: print(f"{x} lớn hơn 5")Walrus Operator:
# Gán và dùng trong cùng 1 biểu thức (1 dòng)if (x := 10) > 5: print(f"{x} lớn hơn 5")2.2 - Cú pháp
Phần tiêu đề “2.2 - Cú pháp”(variable := expression)Lưu ý: Thường cần dùng dấu ngoặc () để tránh lỗi cú pháp.
3. Ví dụ cơ bản
Phần tiêu đề “3. Ví dụ cơ bản”3.1 - Với câu lệnh if
Phần tiêu đề “3.1 - Với câu lệnh if”Trước Python 3.8:
length = len(my_list)if length > 10: print(f"List có {length} phần tử")Với Walrus Operator:
if (length := len(my_list)) > 10: print(f"List có {length} phần tử")3.2 - Với vòng lặp while
Phần tiêu đề “3.2 - Với vòng lặp while”Trước Python 3.8:
line = input("Nhập text (hoặc 'quit'): ")while line != "quit": print(f"Bạn nhập: {line}") line = input("Nhập text (hoặc 'quit'): ")Với Walrus Operator:
while (line := input("Nhập text (hoặc 'quit'): ")) != "quit": print(f"Bạn nhập: {line}")3.3 - Với list comprehension
Phần tiêu đề “3.3 - Với list comprehension”Trước Python 3.8:
# Tính toán 2 lần - không hiệu quảresults = [expensive_function(x) for x in data if expensive_function(x) > 0]Với Walrus Operator:
# Tính toán 1 lần - hiệu quả hơnresults = [y for x in data if (y := expensive_function(x)) > 0]4. Use Cases thực tế
Phần tiêu đề “4. Use Cases thực tế”4.1 - Đọc file
Phần tiêu đề “4.1 - Đọc file”Cách cũ:
file = open("data.txt", "r")line = file.readline()while line: print(line.strip()) line = file.readline()file.close()Với Walrus:
with open("data.txt", "r") as file: while (line := file.readline()): print(line.strip())4.2 - Input validation
Phần tiêu đề “4.2 - Input validation”Cách cũ:
while True: value = input("Nhập số dương: ") try: number = int(value) if number > 0: break print("Phải là số dương!") except ValueError: print("Không phải số!")
print(f"Bạn nhập: {number}")Với Walrus:
while True: try: if (number := int(input("Nhập số dương: "))) > 0: break print("Phải là số dương!") except ValueError: print("Không phải số!")
print(f"Bạn nhập: {number}")4.3 - Regex matching
Phần tiêu đề “4.3 - Regex matching”Cách cũ:
import re
match = re.search(r"(\w+)@(\w+\.\w+)", text)if match: print(f"Username: {match.group(1)}") print(f"Domain: {match.group(2)}")Với Walrus:
import re
if (match := re.search(r"(\w+)@(\w+\.\w+)", text)): print(f"Username: {match.group(1)}") print(f"Domain: {match.group(2)}")4.4 - Xử lý None/Empty
Phần tiêu đề “4.4 - Xử lý None/Empty”Cách cũ:
data = get_data()if data is not None: process(data)Với Walrus:
if (data := get_data()) is not None: process(data)4.5 - List comprehension với filter
Phần tiêu đề “4.5 - List comprehension với filter”Cách cũ - tính 2 lần:
numbers = [1, 2, 3, 4, 5]# squared() được gọi 2 lần cho mỗi số thỏa điều kiệnresult = [squared(x) for x in numbers if squared(x) > 10]
def squared(n): print(f"Computing {n}^2") return n ** 2Với Walrus - tính 1 lần:
numbers = [1, 2, 3, 4, 5]result = [y for x in numbers if (y := squared(x)) > 10]
def squared(n): print(f"Computing {n}^2") return n ** 24.6 - Nested if
Phần tiêu đề “4.6 - Nested if”Cách cũ:
user = get_user(user_id)if user: profile = get_profile(user) if profile: settings = get_settings(profile) if settings: print(settings)Với Walrus:
if (user := get_user(user_id)) and \ (profile := get_profile(user)) and \ (settings := get_settings(profile)): print(settings)5. Ví dụ nâng cao
Phần tiêu đề “5. Ví dụ nâng cao”Ví dụ 1: Parse data
Phần tiêu đề “Ví dụ 1: Parse data”data = [ "name:Alice", "age:25", "invalid", "city:Hanoi", "salary:1000"]
# Extract key-value pairsparsed = []for item in data: if (parts := item.split(":")) and len(parts) == 2: key, value = parts parsed.append({key: value})
print(parsed)# [{'name': 'Alice'}, {'age': '25'}, {'city': 'Hanoi'}, {'salary': '1000'}]Ví dụ 2: Batch processing
Phần tiêu đề “Ví dụ 2: Batch processing”def process_batch(items, batch_size=3): i = 0 while (batch := items[i:i+batch_size]): if not batch: # Empty batch break print(f"Processing batch: {batch}") i += batch_size if i >= len(items): break
data = [1, 2, 3, 4, 5, 6, 7, 8]process_batch(data)Ví dụ 3: Find first match
Phần tiêu đề “Ví dụ 3: Find first match”def find_first_match(items, condition): return next((item for item in items if condition(item)), None)
# Với Walrus - có thể dùng giá trị matchnumbers = [1, 2, 3, 4, 5, 6]if (result := find_first_match(numbers, lambda x: x > 3)): print(f"First match: {result}")Ví dụ 4: Memoization trong list comprehension
Phần tiêu đề “Ví dụ 4: Memoization trong list comprehension”def expensive_check(n): print(f"Checking {n}") return n % 2 == 0 and n > 5
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Không dùng walrus - gọi expensive_check 2 lầnresult = [(n, expensive_check(n)) for n in numbers if expensive_check(n)]
# Dùng walrus - gọi expensive_check 1 lầnresult = [(n, is_valid) for n in numbers if (is_valid := expensive_check(n))]print(result)Ví dụ 5: Stream processing
Phần tiêu đề “Ví dụ 5: Stream processing”import time
def get_sensor_data(): """Giả lập đọc dữ liệu từ sensor""" import random return random.randint(0, 100)
# Monitor sensor và alert nếu giá trị caoprint("Monitoring sensor (Ctrl+C to stop)...")try: while (value := get_sensor_data()) is not None: if value > 80: print(f"ALERT: High value detected: {value}") else: print(f"Normal: {value}") time.sleep(1)except KeyboardInterrupt: print("\nStopped monitoring")Ví dụ 6: Configuration loading
Phần tiêu đề “Ví dụ 6: Configuration loading”def load_config(env): configs = { "dev": {"debug": True, "host": "localhost"}, "prod": {"debug": False, "host": "example.com"} } return configs.get(env)
# Với walrusif (config := load_config("dev")): print(f"Loaded config: {config}") if config.get("debug"): print("Debug mode is ON")else: print("Config not found")Ví dụ 7: Filtering với transformation
Phần tiêu đề “Ví dụ 7: Filtering với transformation”# Lọc và transform cùng lúcnumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Chỉ lấy số chẵn và nhân 2, chỉ tính 1 lầnresult = [doubled for n in numbers if (doubled := n * 2) % 4 == 0]print(result) # [4, 8, 12, 16, 20]Ví dụ 8: Early termination
Phần tiêu đề “Ví dụ 8: Early termination”def process_until_threshold(data, threshold=1000): total = 0 processed = []
for item in data: if (total := total + item) > threshold: print(f"Threshold reached at {total}") break processed.append(item)
return processed
data = [100, 200, 300, 400, 500]result = process_until_threshold(data)print(result)Ví dụ 9: API response handling
Phần tiêu đề “Ví dụ 9: API response handling”def get_user_data(user_id): # Giả lập API call users = { 1: {"name": "Alice", "age": 25}, 2: {"name": "Bob", "age": 30} } return users.get(user_id)
# Chain checking với walrususer_id = 1if (user := get_user_data(user_id)) and (name := user.get("name")): print(f"Welcome, {name}!") if (age := user.get("age")) and age >= 18: print("You are an adult")else: print("User not found")Ví dụ 10: Cache with expiry
Phần tiêu đề “Ví dụ 10: Cache with expiry”import time
class CachedValue: def __init__(self, compute_func, ttl=5): self.compute_func = compute_func self.ttl = ttl self.cached = None self.cached_time = 0
def get(self): current_time = time.time() if (age := current_time - self.cached_time) > self.ttl: print(f"Cache expired (age: {age:.1f}s), recomputing...") self.cached = self.compute_func() self.cached_time = current_time else: print(f"Using cached value (age: {age:.1f}s)") return self.cached
# Sử dụngdef expensive_computation(): print("Computing...") return sum(range(1000000))
cached = CachedValue(expensive_computation, ttl=3)print(cached.get()) # Computetime.sleep(2)print(cached.get()) # Use cachetime.sleep(2)print(cached.get()) # Cache expired, recompute6. Khi KHÔNG nên dùng Walrus
Phần tiêu đề “6. Khi KHÔNG nên dùng Walrus”6.1 - Code khó đọc
Phần tiêu đề “6.1 - Code khó đọc”# TRÁNH - Quá phức tạpif (x := (y := (z := func()))) > 10: pass
# TỐT - Dễ đọc hơnz = func()y = zx = yif x > 10: pass6.2 - Chỉ gán đơn giản
Phần tiêu đề “6.2 - Chỉ gán đơn giản”# TRÁNH - Không cần thiếtx := 10 # SyntaxError! Phải trong expression
# TỐTx = 106.3 - Không có điều kiện
Phần tiêu đề “6.3 - Không có điều kiện”# TRÁNH - Dùng walrus không mang lại lợi íchresult = (x := expensive_function())
# TỐTresult = expensive_function()x = result7. Best Practices
Phần tiêu đề “7. Best Practices”1. Dùng dấu ngoặc
Phần tiêu đề “1. Dùng dấu ngoặc”# TỐT - Rõ ràngif (n := len(items)) > 10: pass
# CÓ THỂ LỖI - Không có ngoặcif n := len(items) > 10: # SyntaxError pass2. Đặt tên biến rõ ràng
Phần tiêu đề “2. Đặt tên biến rõ ràng”# TỐTif (user_count := len(users)) > 100: print(f"Too many users: {user_count}")
# TRÁNHif (x := len(users)) > 100: print(f"Too many users: {x}")3. Không lạm dụng
Phần tiêu đề “3. Không lạm dụng”# TRÁNH - Quá nhiều walrusif (a := func1()) and (b := func2(a)) and (c := func3(b)): pass
# TỐT - Dễ đọc hơna = func1()if a: b = func2(a) if b: c = func3(b)4. Sử dụng khi có lợi ích rõ ràng
Phần tiêu đề “4. Sử dụng khi có lợi ích rõ ràng”# TỐT - Tránh tính toán 2 lầnresult = [y for x in data if (y := transform(x)) is not None]
# TỐT - Giảm code lặpwhile (line := file.readline()): process(line)8. So sánh Before/After
Phần tiêu đề “8. So sánh Before/After”Ví dụ 1: Input loop
Phần tiêu đề “Ví dụ 1: Input loop”Before:
while True: user_input = input("Enter command: ") if user_input == "quit": break process(user_input)After:
while (user_input := input("Enter command: ")) != "quit": process(user_input)Ví dụ 2: Search in list
Phần tiêu đề “Ví dụ 2: Search in list”Before:
found = Nonefor item in items: if condition(item): found = item break
if found: print(f"Found: {found}")After:
if (found := next((item for item in items if condition(item)), None)): print(f"Found: {found}")Ví dụ 3: Dictionary get
Phần tiêu đề “Ví dụ 3: Dictionary get”Before:
value = config.get("key")if value is not None: process(value)After:
if (value := config.get("key")) is not None: process(value)9. Lưu ý quan trọng
Phần tiêu đề “9. Lưu ý quan trọng”9.1 - Chỉ hoạt động trong expression
Phần tiêu đề “9.1 - Chỉ hoạt động trong expression”# OK - Trong ifif (x := 10) > 5: pass
# OK - Trong whilewhile (x := get_value()) is not None: pass
# OK - Trong list comprehensionresult = [y for x in data if (y := transform(x))]
# ERROR - Standalonex := 10 # SyntaxError9.2 - Scope của biến
Phần tiêu đề “9.2 - Scope của biến”# Biến được tạo có scope của block chứa nóif (x := 10) > 5: print(x) # OK - x = 10
print(x) # OK - x vẫn tồn tại ngoài if9.3 - Python 3.8+
Phần tiêu đề “9.3 - Python 3.8+”Walrus operator chỉ hoạt động từ Python 3.8 trở lên:
import sysprint(sys.version) # Kiểm tra version
# Nếu < 3.8, sẽ gặp SyntaxError10. Tổng kết
Phần tiêu đề “10. Tổng kết”Ưu điểm:
- Code ngắn gọn hơn
- Giảm code lặp
- Tăng hiệu suất (tính 1 lần thay vì nhiều lần)
- Dễ đọc (trong nhiều trường hợp)
Nhược điểm:
- Có thể làm code khó hiểu nếu lạm dụng
- Chỉ hỗ trợ từ Python 3.8+
- Cần thời gian để làm quen
Khi nào nên dùng:
- Khi cần gán và kiểm tra điều kiện
- Khi muốn tránh tính toán 2 lần
- Khi muốn giảm code lặp trong vòng lặp
- Khi code vẫn dễ đọc sau khi dùng
Khi nào KHÔNG nên dùng:
- Khi làm code khó đọc
- Khi không mang lại lợi ích rõ ràng
- Khi team chưa quen với Python 3.8+
Bài giảng trên YouTube
Phần tiêu đề “Bài giảng trên YouTube”Cập nhật sau