← Lập trình Python cơ bản

Bài 3 · Cơ bản · 20 phút· Cập nhật 11/06/2026

Biến, đối tượng & tham chiếu

Biên soạn bởi Nguyễn Anh Tuấn

Trong Python mọi thứ là object: biến là “nhãn” tham chiếu (reference) tới object; id()/is/==; mutable vs immutable và bẫy aliasing khi sao chép list.

Một sự thật nền tảng: trong Python, mọi thứ đều là object - int, str, list, thậm chí cả hàm cũng là object. Mỗi object có ba thứ: một danh tính (identity, do id() cho biết), một kiểu (type), và một giá trị:

type.py

print(type(5))
print(type("hi"))
print(type([1, 2]))

Kết quả khi chạy

<class 'int'>
<class 'str'>
<class 'list'>
  • int, str, list, dict… và cả hàm - tất cả là object trên “heap”.
  • Mỗi object có: identity (id), type (kiểu), value (giá trị).
  • type(x) cho biết kiểu; id(x) cho biết identity.

Đây là điểm khác C quan trọng nhất. Ở C, biến là một cái hộp chứa giá trị. Ở Python, biến là một cái nhãn được buộc vào một object. Lệnh a = [1, 2, 3] tạo object list rồi cho tên a trỏ tới nó. Bấm qua từng bước để xem nhãn & object:

Object trên heap
id=4001 list mutable
[1, 2, 3]
← tên: a
bước 1/3
Tạo một list mới, gắn tên a vào nó.
  • Gán (=) buộc một TÊN vào một object - không “bỏ giá trị vào hộp”.
  • b = a tạo thêm một nhãn trỏ tới CÙNG object (không sao chép).
  • id() là identity object; nhiều tên cùng object thì cùng id().

b = a không sao chép, đổi qua b sẽ thấy ở a - nếu object mutable như list (chọn chương trình “List: cùng object” ở công cụ trên):

aliasing.py

a = [1, 2, 3]
b = a            # b tro CUNG object voi a
b.append(4)      # doi list TAI CHO
print(a)         # a cung thay doi!

Kết quả khi chạy

[1, 2, 3, 4]

Muốn một bản sao riêng

Dùng b = a.copy() (hoặc list(a), a[:]) để tạo list MỚI. Khi đó đổi b không ảnh hưởng a.

Immutable (bất biến): int, float, bool, str, tuple - không đổi được, mọi “thay đổi” tạo object MỚI. Mutable (khả biến): list, dict, set - đổi tại chỗ. Vì int immutable, ví dụ dưới không có bẫy aliasing (chọn “Int: bất biến” ở công cụ):

rebind.py

x = 5
y = x            # y tro cung object voi x
y = y + 1        # int BAT BIEN -> tao object MOI (6)
print(x, y)      # x van la 5

Kết quả khi chạy

5 6
  • Immutable: int, float, bool, str, tuple - “sửa” = tạo object mới.
  • Mutable: list, dict, set - đổi tại chỗ, mọi nhãn cùng trỏ đều thấy.
  • Aliasing chỉ “gây bất ngờ” với object mutable.

Hai câu hỏi khác nhau: == hỏi “cùng giá trị?”, còn is hỏi “cùng MỘT object?” (cùng id). Hai list bằng nhau về giá trị vẫn là hai object khác nhau (chọn “is vs ==” ở công cụ để xem):

is_eq.py

p = [1, 2]
q = [1, 2]
print(p == q)    # True  - cung gia tri
print(p is q)    # False - khac object (khac id)

Kết quả khi chạy

True
False

Quy tắc vàng

So sánh giá trị thì luôn dùng ==. Chỉ dùng is cho None (vd if x is None:). Đừng dùng is với int hay str - kết quả phụ thuộc chi tiết cài đặt của CPython (bộ nhớ đệm int nhỏ), và Python sẽ cảnh báo bạn.

Bước 3 cho thấy b = a là CÙNG object → sửa a thì b đổi theo. Muốn một bản RIÊNG (độc lập), phải SAO CHÉP:

copy.py

a = [1, 2, 3]
b = a            # ALIAS: cung mot object
c = a[:]         # ban SAO (slice toan bo)
d = a.copy()     # ban SAO

a.append(99)
print(b)         # [1, 2, 3, 99]   <- doi theo a
print(c, d)      # [1, 2, 3] [1, 2, 3]   <- doc lap

Kết quả khi chạy

[1, 2, 3, 99]
[1, 2, 3] [1, 2, 3]

Bản nông (shallow) vs bản sâu (deep)

a[:] / .copy() chỉ sao chép tầng ngoài. Nếu list LỒNG list, các list con vẫn DÙNG CHUNG: sao chép nông rồi sửa phần lồng thì bản sao cũng đổi. Cần độc lập hoàn toàn → copy.deepcopy(x).
  • b = a chia sẻ object; muốn bản riêng: a[:], a.copy(), list(a), dict(d), set(s).
  • Sao chép nông chỉ tách tầng ngoài; dữ liệu lồng nhau vẫn dùng chung.
  • copy.deepcopy() sao chép sâu mọi tầng - dùng khi cấu trúc lồng nhau.

Câu hỏi thường gặp

Không. Trong C, biến là một ô nhớ CHỨA giá trị. Trong Python, biến là một TÊN (nhãn) được “buộc” vào một object nằm trên heap. a = 5 không bỏ số 5 vào ô a; nó tạo object int 5 rồi cho tên a trỏ tới object đó. Nhiều tên có thể cùng trỏ một object.

Bài “Biến & kiểu dữ liệu” - khoá Lập trình C cơ bản →

id(x) trả về identity (danh tính) của object - một số duy nhất, không đổi trong suốt đời object. Ở CPython nó chính là địa chỉ bộ nhớ. Hai tên trỏ cùng object thì id() bằng nhau; đó là điều toán tử is kiểm tra.

== hỏi “hai object có CÙNG GIÁ TRỊ không?”. is hỏi “có phải CÙNG MỘT object không?” (cùng id). Hai list [1,2][1,2] thì ==True nhưng isFalse (hai object khác nhau, tình cờ bằng giá trị). Quy tắc: so sánh giá trị thì dùng ==; chỉ dùng is cho None (x is None).

CPython tạo sẵn và dùng lại (cache) các int nhỏ từ −5 đến 256, nên 256 is 256 ra True. Nhưng đây là CHI TIẾT CÀI ĐẶT, không nên dựa vào - Python còn cảnh báo nếu bạn dùng is với int/str trực tiếp. Muốn so giá trị, LUÔN dùng ==.

Object immutable KHÔNG đổi được sau khi tạo: int, float, bool, str, tuple. Mọi “thay đổi” thực ra tạo object MỚI. Object mutable đổi được tại chỗ: list, dict, set. Phân biệt này quyết định chuyện chia sẻ object có gây bất ngờ hay không.

b = a chỉ tạo thêm một NHÃN cho cùng list. Muốn một bản SAO riêng: b = a.copy() hoặc b = list(a) hoặc b = a[:]. Khi đó đổi b không ảnh hưởng a. (Lồng nhau sâu thì cần copy.deepcopy.)

Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.

Tick những điều em tự tin làm được sau khi học bài này. 0/6

Trả lời vài câu để chắc rằng em đã nắm bài.

Câu 1/3 Điểm: 0

Trong Python, một biến (tên) thực chất là gì?

  1. 1

    Bẫy aliasing

    Đoán kết quả rồi chạy: a = [1, 2, 3]; b = a; b.append(4); print(a). Vì sao?

    Hoàn thành khi: [1, 2, 3, 4] - ab cùng trỏ MỘT list; append đổi list tại chỗ nên a cũng thấy.

  2. 2

    Số thì an toàn

    Chạy: x = 5; y = x; y = y + 1; print(x, y). Vì sao x không đổi?

    Hoàn thành khi: 5 6 - int immutable; y + 1 tạo object MỚI và buộc y vào nó, x vẫn trỏ object 5.

  3. 3

    is hay ==

    Với p = [1, 2]q = [1, 2], dự đoán p == qp is q. Dùng công cụ Bước 5 để kiểm.

    Hoàn thành khi: p == qTrue (cùng giá trị); p is qFalse (khác object).

  4. 4

    Sao chép thật

    Sửa bài “aliasing” để đổi b KHÔNG ảnh hưởng a.

    Hoàn thành khi: Dùng b = a.copy() (hoặc list(a) / a[:]); khi đó b.append(4) không đổi a.

  5. 5

    Mọi thứ là object

    In type của 5, "hi", [1, 2] và của chính hàm print. Nhận xét?

    Hoàn thành khi: type(5)=int, type('hi')=str, type([1,2])=list, type(print)=builtin_function_or_method - đến hàm cũng là object.

  6. 6

    Chỉ is với None

    Vì sao nên viết if x is None thay vì if x == None?

    Hoàn thành khi: None là object DUY NHẤT (singleton); is so identity nhanh & đúng tuyệt đối, không phụ thuộc __eq__ của x.