{} là dict, không phải set!
Hiện tượng lạ
# Tạo set với các phần tử
my_set = {1, 2, 3}
print(type(my_set)) # <class 'set'>
# Vậy tạo set rỗng cũng dùng {}?
empty = {}
print(type(empty)) # <class 'dict'> <- What the Python?!Tại sao {1, 2, 3} là set nhưng {} lại là dict?! 🤔
Giải thích
Dictionary có trước, Set có sau
Python được tạo ra năm 1991, Dictionary (dict) đã có từ đầu và sử dụng cú pháp {}.
Set (set) chỉ được thêm vào Python 2.4 (năm 2004), 13 năm sau!
# Dictionary - Có từ Python 1.0 (1991)
my_dict = {"key": "value"}
# Set - Thêm vào Python 2.4 (2004)
my_set = {1, 2, 3}{} đã được “chiếm chỗ” bởi dict
Khi set được thêm vào, {} đã được dùng cho dict rỗng. Để tránh breaking changes (phá vỡ code cũ), Python giữ nguyên {} = dict rỗng.
Cách tạo set và dict rỗng
Dictionary rỗng
# Cách 1: Dùng {}
empty_dict = {}
print(type(empty_dict)) # <class 'dict'>
# Cách 2: Dùng dict()
empty_dict = dict()
print(type(empty_dict)) # <class 'dict'>
# Cả 2 cách đều OKSet rỗng
# KHÔNG THỂ dùng {}
empty_set = {} # ❌ Đây là dict, không phải set!
# PHẢI dùng set()
empty_set = set()
print(type(empty_set)) # <class 'set'> ✅
# Đây là CÁCH DUY NHẤT để tạo set rỗngSo sánh Set và Dict
Cú pháp với elements
# Set - chỉ có values
my_set = {1, 2, 3}
print(my_set) # {1, 2, 3}
# Dict - có key:value pairs
my_dict = {"a": 1, "b": 2, "c": 3}
print(my_dict) # {'a': 1, 'b': 2, 'c': 3}
# Python phân biệt bằng dấu :
# - Không có : → Set
# - Có : → DictCú pháp rỗng
# Set rỗng - PHẢI dùng constructor
empty_set = set()
# Dict rỗng - Dùng {} hoặc constructor
empty_dict = {}
empty_dict = dict() # Cũng OKCác trường hợp dễ nhầm
1. Khởi tạo set rỗng
# SAI - Vô tình tạo dict
def process_unique_items():
seen = {} # ❌ Đây là dict!
seen.add(1) # AttributeError: 'dict' object has no attribute 'add'
# ĐÚNG
def process_unique_items():
seen = set() # ✅
seen.add(1) # OK!2. Default value trong function
# SAI - Default là dict, không phải set
def track_ids(new_ids, existing={}): # ❌ {} là dict
existing.add(new_ids) # Lỗi!
# ĐÚNG
def track_ids(new_ids, existing=None):
if existing is None:
existing = set() # ✅
existing.add(new_ids)3. Type annotation
from typing import Set, Dict
# Nếu dùng type hints, dễ phát hiện lỗi
def get_unique_items() -> Set[int]:
return {} # ❌ Type checker sẽ báo lỗi: Expected Set, got Dict
# ĐÚNG
def get_unique_items() -> Set[int]:
return set() # ✅Tại sao cần set rỗng?
Use case 1: Tracking unique items
def find_unique_chars(text):
unique = set() # Bắt đầu với set rỗng
for char in text:
unique.add(char)
return unique
print(find_unique_chars("hello")) # {'h', 'e', 'l', 'o'}Use case 2: Filtering duplicates
def remove_duplicates(items):
seen = set() # Set rỗng để track
result = []
for item in items:
if item not in seen:
seen.add(item)
result.append(item)
return result
print(remove_duplicates([1, 2, 2, 3, 1, 4])) # [1, 2, 3, 4]Use case 3: Set operations từ rỗng
# Bắt đầu với set rỗng, sau đó union
all_tags = set()
for article in articles:
all_tags = all_tags.union(article.tags)
# Hoặc
all_tags = set()
for article in articles:
all_tags |= set(article.tags) # Union assignmentCách debug và phát hiện
1. Kiểm tra type
mystery = {}
# Debug
print(f"Type: {type(mystery)}") # <class 'dict'>
print(f"Is dict: {isinstance(mystery, dict)}") # True
print(f"Is set: {isinstance(mystery, set)}") # False2. Lỗi khi dùng set methods
data = {}
# Nếu vô tình tạo dict thay vì set
data.add(1) # AttributeError: 'dict' object has no attribute 'add'
# → Lỗi này cho biết data là dict, không phải set!3. Dùng IDE/Linter
# PyCharm, VS Code với Pylint sẽ cảnh báo
unique_items: Set[int] = {} # Warning: Expected Set, got dictSo sánh đầy đủ
| Mục đích | Cú pháp | Type | Ví dụ |
|---|---|---|---|
| Set rỗng | set() | set | s = set() |
| Set có phần tử | {1, 2, 3} | set | s = {1, 2, 3} |
| Dict rỗng | {} hoặc dict() | dict | d = {} |
| Dict có phần tử | {"a": 1} | dict | d = {"a": 1} |
Set comprehension vs Dict comprehension
# Set comprehension - Không có :
squares_set = {x**2 for x in range(5)}
print(squares_set) # {0, 1, 4, 9, 16}
print(type(squares_set)) # <class 'set'>
# Dict comprehension - Có :
squares_dict = {x: x**2 for x in range(5)}
print(squares_dict) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
print(type(squares_dict)) # <class 'dict'>
# Comprehension rỗng?
# - Set: {x for x in []} → set() (rỗng)
# - Dict: {x: x for x in []} → {} (rỗng)Bảng tổng hợp methods
Set methods (không có trong dict)
s = set()
s.add(1) # ✅ Set có
s.remove(1) # ✅ Set có
s.discard(1) # ✅ Set có
s.union({2, 3}) # ✅ Set cóDict methods (không có trong set)
d = {}
d["key"] = "value" # ✅ Dict có
d.get("key") # ✅ Dict có
d.keys() # ✅ Dict có
d.values() # ✅ Dict có
d.items() # ✅ Dict cóBest Practices
✅ Nên làm
# 1. Luôn dùng set() cho set rỗng
unique_items = set() # ✅ Rõ ràng
# 2. Dùng type hints
from typing import Set
def get_tags() -> Set[str]:
return set() # ✅
# 3. Kiểm tra type khi debug
if not isinstance(data, set):
raise TypeError(f"Expected set, got {type(data)}")❌ Tránh làm
# 1. Dùng {} cho set rỗng
unique_items = {} # ❌ Nhầm lẫn!
# 2. Không kiểm tra type
def process(data):
data.add(1) # ❌ Nếu data là dict sẽ lỗi
# 3. Mixed types không rõ ràng
def create_collection(is_unique):
return set() if is_unique else {} # ❌ ConfusingLịch sử Python - Tại sao như vậy?
Timeline
- 1991: Python ra đời, dict dùng
{} - 2004: Python 2.4 - Set được thêm vào
- Vấn đề:
{}đã được dùng cho dict rỗng - Giải pháp:
- Giữ
{}= dict rỗng (backward compatibility) - Dùng
set()cho set rỗng - Dùng
{1, 2, 3}cho set có phần tử (phân biệt bằng không có:)
- Giữ
Lý do không thay đổi
# Nếu thay đổi {} thành set:
# - Hàng triệu dòng code cũ sẽ bị hỏng
# - Dict rỗng phải dùng dict() (không tiện)
# - Breaking change quá lớnTóm tắt
| Cú pháp | Kiểu | Ghi chú |
|---|---|---|
{} | dict | Dict rỗng |
{1, 2, 3} | set | Set với phần tử |
{"a": 1} | dict | Dict với key:value |
set() | set | Set rỗng (cách duy nhất) |
dict() | dict | Dict rỗng (giống {}) |
Ghi nhớ
{}là dict, không phải set!
- ✅ Set rỗng:
set()(cách DUY NHẤT)- ✅ Dict rỗng:
{}hoặcdict()- 🔍 Python phân biệt set/dict bằng dấu
:
- Không có
:→ Set:{1, 2, 3}- Có
:→ Dict:{"a": 1, "b": 2}
Tip: Nhớ rằng dictionary “có trước” nên “chiếm” được {}!
Last updated on
Python