Bỏ qua để đến nội dung

Walrus Operator (:=)

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

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")
(variable := expression)

Lưu ý: Thường cần dùng dấu ngoặc () để tránh lỗi cú pháp.

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ử")

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}")

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]

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())

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}")

Cách cũ:

import re
text = "Email: [email protected]"
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: [email protected]"
if (match := re.search(r"(\w+)@(\w+\.\w+)", text)):
print(f"Username: {match.group(1)}")
print(f"Domain: {match.group(2)}")

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)

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 ** 2

Vớ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 ** 2

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)
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'}]
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)
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}")
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)
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")
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")
# 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]
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)
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")
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, recompute
# 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:
pass
# TRÁNH - Không cần thiết
x := 10 # SyntaxError! Phải trong expression
# TỐT
x = 10
# TRÁNH - Dùng walrus không mang lại lợi ích
result = (x := expensive_function())
# TỐT
result = expensive_function()
x = result
# 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
pass
# 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}")
# 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)
# 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)

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)

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}")

Before:

value = config.get("key")
if value is not None:
process(value)

After:

if (value := config.get("key")) is not None:
process(value)
# 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 # SyntaxError
# 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 if

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 SyntaxError

Ư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+

Cập nhật sau