DHDataHọc
TheoryLesson 5/304 min read+10 XP

Ép kiểu

Bạn sẽ học được

  • Bốn hàm ép kiểu cơ bản và kết quả chính xác của chúng.
  • Vì sao int("3.14") lỗi nhưng int(3.14) chạy.
  • bool() trên chuỗi gây bất ngờ lớn nhất trong Python.
  • Bảng giá trị "falsy" đầy đủ.
  • Cách xử lý ValueError khi dữ liệu đầu vào không sạch.

Khái niệm cốt lõi

Ép kiểu là chuyển giá trị từ kiểu này sang kiểu khác bằng cách gọi hàm trùng tên kiểu:

code
print(int("42") + 1)
print(float("3.14") * 2)
print(str(42) + "!")
print(bool(1), bool(0))
code
43
6.28
42!
True False

Bốn hàm int(), float(), str(), bool() là công cụ hằng ngày. Nhưng mỗi hàm có quy tắc riêng, và ba trong số đó thường xuyên gây bất ngờ.

int()

int() có hai việc: cắt phần thập phân của số, và đọc chuỗi thành số nguyên.

code
print(int(3.99))
print(int(-3.99))
print(int("42"))
print(int("  42  "))
print(int(True), int(False))
code
3
-3
42
42
1 0

Hai điểm quan trọng:

Một, int() cắt về phía 0, không làm tròn xuống. int(-3.99) ra -3, trong khi -3.99 // 1 ra -4.0. Nếu cần làm tròn, dùng round() hoặc math.floor() tuỳ ý định.

Hai, chuỗi có khoảng trắng hai đầu vẫn đọc được, nhưng chuỗi có dấu thập phân thì không:

code
int("3.14")
code
ValueError: invalid literal for int() with base 10: '3.14'

Muốn đọc "3.14" thành số, phải qua float trước:

code
print(int(float("3.14")))
code
3

Đây là lỗi cực phổ biến khi đọc CSV: cột số có giá trị "3.0" từ Excel, int() báo lỗi. Cách xử lý đúng là int(float(x)) hoặc dùng Decimal/pandas để đọc cả cột.

int() còn nhận cơ số thứ hai:

code
print(int("1010", 2))
print(int("ff", 16))
code
10
255

float()

float() đọc chuỗi hoặc số thành số thực:

code
print(float("3.14"))
print(float("1e3"))
print(float(42))
print(float("inf"), float("-inf"))
code
3.14
1000.0
42.0
inf -inf

float() chấp nhận ký hiệu khoa học, inf, -inf, nan. Với dữ liệu thật, nan (Not a Number) xuất hiện từ phép chia 0/0 hoặc từ cột trống trong file. So sánh nan == nan luôn False; dùng math.isnan().

Ép float sang int làm mất phần thập phân, không làm tròn. Nếu cần làm tròn, round():

code
print(round(3.5), round(4.5), round(2.5))
code
4 4 2

Kết quả thứ ba gây tranh cãi: Python dùng làm tròn ngân hàng (round half to even), nên 2.5 thành 2, 3.5 thành 4. Đây là hành vi có chủ đích, giảm sai lệch tích luỹ khi cộng nhiều số đã làm tròn.

str()

str() chuyển gần như mọi thứ thành chuỗi hiển thị:

code
print(str(42), str(3.14), str(True), str(None))
print(str([1, 2]), str({"a": 1}))
code
42 3.14 True None
[1, 2] {'a': 1}

str() dùng cho hiển thị, không dùng để tuần tự hoá dữ liệu. Muốn lưu cấu trúc để đọc lại, dùng json.dumps() (chương 5): str() tạo ra chuỗi mà eval() mới đọc lại được — và eval() trên dữ liệu không tin cậy là lỗ hổng bảo mật.

bool() — cái bẫy lớn nhất

bool() trả False cho một danh sách giá trị "rỗng", True cho phần còn lại:

Giá trịbool()
0, 0.0, 0jFalse
"" (chuỗi rỗng)False
[], (), {}, set()False
NoneFalse
FalseFalse
Mọi giá trị khácTrue

Bất ngờ nằm ở chuỗi:

code
print(bool("0"))
print(bool("False"))
print(bool(" "))
print(bool(""))
code
True
True
True
False

Chuỗi "False" là chuỗi không rỗng, nên True. Chuỗi "0" cũng vậy. Đây là lý do đọc biến môi trường hoặc ô CSV dạng "false" rồi dùng trực tiếp trong if sẽ sai. Phải so sánh tường minh:

code
raw = "false"
is_enabled = raw.strip().lower() in {"true", "1", "yes"}
print(is_enabled)
code
False

Tương tự, bool(" ") là True vì khoảng trắng vẫn là ký tự. Muốn coi chuỗi chỉ có khoảng trắng là rỗng, dùng bool(raw.strip()).

Xử lý ValueError

Ép kiểu có thể thất bại. Trong mã production, bắt lỗi thay vì để chương trình dừng:

code
def to_int(value: str) -> int | None:
    try:
        return int(value)
    except (TypeError, ValueError):
        return None

for raw in ["42", "3.14", "abc", ""]:
    print(repr(raw), "→", to_int(raw))
code
'42' → 42
'3.14' → None
'abc' → None
'' → None

try/except là cách Python xử lý lỗi dự kiến được. Chỉ bắt đúng loại lỗi (ValueError, TypeError), không dùng except: trần — nó nuốt cả lỗi lập trình và làm bug khó tìm.

Thực hành

  1. Chạy int("42") + 1, float("3.14") * 2, str(42) + "!".
  2. Thử int("3.14"), đọc thông báo, rồi sửa bằng int(float("3.14")).
  3. So sánh int(-3.99) và -3.99 // 1. Giải thích khác biệt.
  4. Kiểm tra bool("0"), bool("False"), bool(" "), bool("").
  5. Viết hàm to_int như trên và thử với bốn giá trị.
  6. Đọc "ff" ở cơ số 16 và "1010" ở cơ số 2.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
int("3.14") lỗiChuỗi có dấu thập phânint(float(x))
int("") lỗiChuỗi rỗngKiểm tra rỗng trước
bool("False") ra TrueChuỗi không rỗngSo sánh với tập giá trị
float("1,5") lỗiDấu phẩy thập phân kiểu Việt NamThay , bằng . trước
Làm tròn sai chiềuint() cắt về 0Dùng round() hoặc math.floor()
round(2.5) ra 2Làm tròn ngân hàngDùng Decimal nếu cần luật khác
Ứng dụng crash vì dữ liệu bẩnKhông bắt ValueErrortry/except đúng loại

Tóm tắt

  • int() cắt về 0; không đọc được chuỗi có dấu thập phân.
  • float() nhận ký hiệu khoa học, inf, nan.
  • str() để hiển thị, không phải để tuần tự hoá.
  • bool() chỉ False với giá trị rỗng — "0" và "False" đều True.
  • Bắt ValueError khi dữ liệu đến từ bên ngoài.

Use ← → to move between lessons