Walrus Operator (:=)
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
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 = 10
if 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
(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
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
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
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ơn
results = [y for x in data if (y := expensive_function(x)) > 0]4. Use Cases thực tế
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
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
Cách cũ:
import re
text = "Email: alice@example.com"
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
text = "Email: alice@example.com"
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
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
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ện
result = [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
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
Ví dụ 1: Parse data
data = [
"name:Alice",
"age:25",
"invalid",
"city:Hanoi",
"salary:1000"
]
# Extract key-value pairs
parsed = []
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
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
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ị match
numbers = [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
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ần
result = [(n, expensive_check(n)) for n in numbers if expensive_check(n)]
# Dùng walrus - gọi expensive_check 1 lần
result = [(n, is_valid) for n in numbers if (is_valid := expensive_check(n))]
print(result)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ị cao
print("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
def load_config(env):
configs = {
"dev": {"debug": True, "host": "localhost"},
"prod": {"debug": False, "host": "example.com"}
}
return configs.get(env)
# Với walrus
if (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
# Lọc và transform cùng lúc
numbers = [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ần
result = [doubled for n in numbers if (doubled := n * 2) % 4 == 0]
print(result) # [4, 8, 12, 16, 20]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
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 walrus
user_id = 1
if (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
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ụng
def expensive_computation():
print("Computing...")
return sum(range(1000000))
cached = CachedValue(expensive_computation, ttl=3)
print(cached.get()) # Compute
time.sleep(2)
print(cached.get()) # Use cache
time.sleep(2)
print(cached.get()) # Cache expired, recompute6. Khi KHÔNG nên dùng Walrus
6.1 - Code khó đọc
# TRÁNH - Quá phức tạp
if (x := (y := (z := func()))) > 10:
pass
# TỐT - Dễ đọc hơn
z = func()
y = z
x = y
if x > 10:
pass6.2 - Chỉ gán đơn giản
# TRÁNH - Không cần thiết
x := 10 # SyntaxError! Phải trong expression
# TỐT
x = 106.3 - Không có điều kiện
# TRÁNH - Dùng walrus không mang lại lợi ích
result = (x := expensive_function())
# TỐT
result = expensive_function()
x = result7. Best Practices
1. Dùng dấu ngoặc
# TỐT - Rõ ràng
if (n := len(items)) > 10:
pass
# CÓ THỂ LỖI - Không có ngoặc
if n := len(items) > 10: # SyntaxError
pass2. Đặt tên biến rõ ràng
# TỐT
if (user_count := len(users)) > 100:
print(f"Too many users: {user_count}")
# TRÁNH
if (x := len(users)) > 100:
print(f"Too many users: {x}")3. Không lạm dụng
# TRÁNH - Quá nhiều walrus
if (a := func1()) and (b := func2(a)) and (c := func3(b)):
pass
# TỐT - Dễ đọc hơn
a = func1()
if a:
b = func2(a)
if b:
c = func3(b)4. Sử dụng khi có lợi ích rõ ràng
# TỐT - Tránh tính toán 2 lần
result = [y for x in data if (y := transform(x)) is not None]
# TỐT - Giảm code lặp
while (line := file.readline()):
process(line)8. So sánh Before/After
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
Before:
found = None
for 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
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
9.1 - Chỉ hoạt động trong expression
# OK - Trong if
if (x := 10) > 5:
pass
# OK - Trong while
while (x := get_value()) is not None:
pass
# OK - Trong list comprehension
result = [y for x in data if (y := transform(x))]
# ERROR - Standalone
x := 10 # SyntaxError9.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+
Walrus operator chỉ hoạt động từ Python 3.8 trở lên:
import sys
print(sys.version) # Kiểm tra version
# Nếu < 3.8, sẽ gặp SyntaxError10. 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
Cập nhật sau
Last updated on
Python