Nhập môn Tư duy Lập trình/Chuỗi và số — vì sao cộng hai kiểu lại nổ
4/7
Bài 4 / 7~9 phútMáy thực thi lệnh thế nàoMiễn phí lượt xem

Chuỗi và số — vì sao cộng hai kiểu lại nổ

Ô nhớ không chỉ giữ một giá trị, nó còn giữ một kiểu. Cộng chuỗi với số là chỗ người mới vấp sớm hơn ta tưởng — và là traceback đầu tiên bạn đọc trong khoá này.

TL;DR: Ô nhớ không chỉ giữ một giá trị, nó còn giữ một kiểu, và Python không tự ghép hai kiểu khác nhau lại với nhau. Cộng chuỗi "5" với số 3 không ra 8 như bạn tưởng — chương trình dừng lại bằng TypeError, và đây là traceback đầu tiên bạn đọc trong khoá này. Chuỗi "5" và số 5 nhìn giống hệt nhau trên màn hình nhưng là hai ô nhớ khác loại. Nút thắt hay gặp nhất: input() luôn trả về chuỗi, kể cả khi người dùng gõ toàn chữ số, nên muốn cộng được bạn phải tự đổi nó thành số bằng int(...) trước.

Bài trước, bạn đã thấy ô nhớ giữ một giá trị và tên biến trỏ đúng tới ô đó. Giờ thử đoạn này: bạn hỏi người dùng nhập tuổi bằng input(), rồi cộng thẳng với 1 để tính tuổi sang năm. Chương trình không chạy — nó dừng giữa chừng và in ra một khối chữ dài bạn chưa từng thấy.

Bài này giải thích vì sao: ô nhớ còn giữ thêm một thứ ngoài giá trị, và khi hai thứ đó không khớp nhau, Python thà dừng lại còn hơn đoán bừa.

1. Ô nhớ giữ thêm một thứ: kiểu

Nhớ lại Biến và phép gán

Bài trước bạn học: biến là cái tên gắn với một ô nhớ, và phép gán chép giá trị vào ô đó. Bài này thêm đúng một chi tiết: mỗi ô nhớ, ngoài giá trị, còn giữ một kiểu — nhãn nói ô đó đang chứa loại dữ liệu gì.

Nhớ lại tủ ký gửi ở bài 02: mỗi tủ có một tên nhãn và giữ một món đồ. Giờ hình dung thêm một chi tiết: tủ ký gửi ở một số cửa hàng có ngăn riêng cho hàng khô, ngăn riêng cho hàng đông lạnh — bỏ nhầm ngăn thì nhân viên từ chối nhận. Ô nhớ trong máy tính cũng vậy: nó không chỉ giữ một món đồ (giá trị), nó còn ghi rõ loại hàng được phép giữ trong ngăn đó (kiểu).

Tủ ký gửiÔ nhớ
Món đồ bỏ vào tủGiá trị, ví dụ 20 hoặc "5"
Loại ngăn: khô hay đông lạnhKiểu, ví dụ int hay str
Bỏ hàng khô vào ngăn đông lạnh, nhân viên từ chốiCộng str với int, Python từ chối bằng TypeError

Hai kiểu bạn đã chạm tới trong khoá: số nguyên (int) và chuỗi (str). Số là con số bạn tính toán, cộng trừ được. Chuỗi là một dãy ký tự nằm trong dấu ngoặc kép — kể cả khi ký tự đó là chữ số, nó vẫn chỉ là ký tự, không phải con số.

Một ô nhớ mang tên biến tuoi toả ra hai mũi tên: một tới giá trị 20, một tới kiểu số nguyên — ô nhớ giữ đồng thời cả giá trị lẫn kiểu

Ô nhớ ở trên giữ đủ hai thứ cùng lúc: giá trị 20 và kiểu int. Đến đây bạn chưa cần lo gì — vấn đề chỉ nảy ra khi hai ô nhớ khác kiểu bị đem cộng với nhau.

2. Predict — "5" + 3 in ra gì?

Thử đoán

Trước khi đọc tiếp: đoạn dưới đây gán tong = "5" + 3 rồi in tong. Đừng chạy thử, viết ra một con số hoặc một câu bạn tin chắc sẽ xảy ra.

tong = "5" + 3
print(tong)

Chương trình không in ra 8, và cũng không in ra "53". Nó dừng lại ngay tại dòng gán, và màn hình hiện đúng như sau:

Traceback (most recent call last):
  File "vidu.py", line 1, in <module>
    tong = "5" + 3
           ~~~~^~~
TypeError: can only concatenate str (not "int") to str

Bạn chưa cần đọc hết năm dòng này. Chỉ dòng cuối là đáng đọc lúc này: TypeError nói loại lỗi là lỗi về kiểu dữ liệu, và ngay sau đó Python gọi tên đúng hai kiểu vừa va nhau — str (chuỗi) và int (số). Nói cách khác: + giữa một chuỗi và một số không có nghĩa rõ ràng, nên Python không đoán hộ bạn, nó dừng chương trình lại.

Đây là traceback đầu tiên bạn gặp trong khoá. Cách đọc đầy đủ một traceback — từng dòng file, từng lệnh gọi — sẽ có một bài riêng sau; lúc này biết đọc dòng cuối là đủ.

3. "5"5 — giống trên màn hình, khác trong ô nhớ

Bạn gán x = "5"y = 5. Cả hai đều hiện ra đúng ký tự 5 khi bạn print, nhưng ô nhớ của chúng không giống nhau chút nào.

Từ hộp Bạn gõ: 5 có hai mũi tên nét đứt toả xuống hai ô nhớ khác nhau: một ô nhớ X kiểu chuỗi giữ ký tự 5, một ô nhớ Y kiểu số nguyên giữ số 5 — cùng một thứ gõ trên bàn phím nhưng kết quả tuỳ cách chương trình nhận dữ liệu vào

Viết trong codePython đọc là gì
5 (kiểu int)một con số tính toán được
"5" (kiểu str)một ký tự 5, không phải con số

Sự khác biệt này nằm ở chính nghĩa của toán tử +: giữa hai số, nó cộng; giữa hai chuỗi, nó nối liền hai chuỗi lại. "5" + 3 đứng giữa hai nghĩa đó — một bên là chuỗi, một bên là số — nên Python từ chối đoán bạn muốn cộng hay muốn nối. Đó chính là traceback bạn vừa thấy.

4. input() trả về gì, kể cả khi bạn gõ số?

Trước khi nói tiếp, cần một định nghĩa: một hàm (function) là một khối việc có tên, có thể nhận đầu vào, và quan trọng nhất, nó trả về một giá trị cho chỗ gọi nó dùng tiếp. Khác với print(...) mà bài 01 gọi là một lệnh: lệnh chỉ thực hiện một hành động (in ra màn hình) rồi thôi, không đưa lại thứ gì để dùng tiếp. Hàm thì có — gọi hàm xong, bạn nhận về một giá trị, y hệt như kết quả của một phép tính.

Bài 02 đã nói: vế phải của phép gán được tính xong trước, rồi giá trị mới được chép vào ô nhớ. Một lời gọi hàm cũng đứng được ở vế phải theo đúng quy tắc đó — máy chạy xong hàm, lấy giá trị nó trả về, rồi mới gán. input(...) chính là một hàm như vậy.

Hàm input() luôn trả về một chuỗi, bất kể người dùng gõ chữ hay số.

tuoi_text = input("Ban bao nhieu tuoi? ")

Dù người dùng gõ 20, biến tuoi_text vẫn giữ chuỗi "20", không phải số 20. input() không tự đoán ý bạn — nó chỉ đưa lại nguyên văn những gì được gõ, dưới dạng ký tự.

Vấp sớm nhất

Đây là chỗ hầu hết người mới gặp TypeError lần đầu: lấy thẳng kết quả input() đem cộng với một số, quên mất nó vẫn còn là chuỗi.

Đoạn dưới muốn tính tuổi sang năm bằng cách cộng thêm 1. Một chỗ bị để trống — tự điền trước khi xem đáp án.

tuoi_text = input("Ban bao nhieu tuoi? ")
# tuoi_text la mot chuoi, du ban go toan chu so
tuoi_sang_nam = ____ + 1   # dien vao cho trong
print(tuoi_sang_nam)
Tự điền trước khi xem đáp án

Gợi ý: bạn cần đổi tuoi_text từ chuỗi sang số trước khi cộng được. Hàm nào trong bài này làm việc đó?

Đáp án:

tuoi_text = input("Ban bao nhieu tuoi? ")
tuoi_sang_nam = int(tuoi_text) + 1
print(tuoi_sang_nam)

int(...) đọc một chuỗi và trả về số nguyên tương ứng. Sau dòng đó, ô nhớ tuoi_sang_nam được tính từ một số thật, nên + 1 cộng bình thường, không còn trộn kiểu.

5. Liên hệ các bài khác

6. Tóm tắt

  • Thấy TypeError khi cộng hai thứ "trông như số"? Nghi ngay một bên đang là chuỗi — thường do quên int() sau input().
  • Đọc dòng cuối traceback trước tiên; nó nêu thẳng hai kiểu đang va chạm, chưa cần đọc mấy dòng phía trên.
  • Trước khi cộng/trừ bất cứ thứ gì lấy từ input(), tự hỏi: giá trị này đã qua int(...) chưa?
  • "5"5 viết gần giống nhau; khi debug, để ý dấu ngoặc kép để biết bạn đang cầm chuỗi hay số.
  • Hàm khác lệnh ở chỗ nó trả về một giá trị dùng tiếp được — đó là lý do input()int(...) đứng được ở vế phải phép gán.

7. Tự kiểm tra

Tự kiểm tra
0/6 câu đã trả lời
  1. Q1
    Vì sao 5 + 3 chạy được nhưng "5" + 3 lại dừng chương trình bằng lỗi?
  2. Q2
    Dòng cuối traceback TypeError: can only concatenate str (not "int") to str cho bạn biết điều gì, dù bạn chưa đọc hết các dòng phía trên?
  3. Q3
    Bạn gán x = "5" và y = 5. Hai ô nhớ này khác nhau ở đâu, dù cả hai đều hiện chữ số 5 như nhau?
  4. Q4
    Bạn viết ten_input = input(...), dù người dùng gõ toàn chữ số như 2005, ten_input mang kiểu gì?
  5. Q5
    Muốn cộng kết quả của input() với một số nguyên mà không bị TypeError, bạn cần làm gì trước khi cộng?
  6. Q6
    Bạn viết tuoi = 2026 - nam_sinh mà quên rằng nam_sinh vẫn còn là chuỗi từ input(). Chuyện gì xảy ra khi chạy dòng này?

Bài tiếp theo: Bảng trace

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Bảng trace — chạy chương trình bằng tay