Những chỗ Python khác C++
Chương 0 · Bài 0.1
Từ bài sau, mọi code đều là Python: pandas, NumPy, scikit-learn, PyTorch.
Vòng lặp, hàm, thuật toán từ C++ vẫn dùng như cũ.
Bài này học những chỗ dễ làm sai:
//, % với số âm; so sánh số thực.def passed_scores(values, threshold=5.0):
result = []
for value in values:
if value >= threshold:
result.append(value)
return result
: mở một khối.return ngang hàng for: chạy sau khi lặp xong.range(1, 5) lấy 1, 2, 3, 4.Trước khi nộp: Restart & Run all.
import torch
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print('device:', device)
Bật GPU đúng thì in cuda.
Hỏi nhanh
Trên Kaggle, mục Accelerator chỉ chọn được None. Lý do thường gặp nhất là gì?
Kaggle khóa GPU và Internet với tài khoản chưa xác minh số điện thoại. Code trong notebook không ảnh hưởng tới mục này.
Hỏi nhanh
Notebook có hai ô: trên là n = 3, dưới là total = sum(range(n + 1)). Hôm qua đã chạy cả hai. Hôm nay mở lại (kernel mới), chạy ô dưới trước. Ô dưới cho ra gì?
Kernel mới chưa có biến nào, kể cả n của hôm qua, và Python không tự coi biến chưa gán là 0. Restart & Run all trước khi nộp sẽ lộ đúng loại lỗi này.
print(7 / 2, 6 / 2)
print(7 // 2, -7 // 2)
print(7 % 3, -7 % 3)
Kết quả3.5 3.0
3 -4
1 2
// làm tròn xuống. Với b=0:
Số chia âm thì % không dương: 10 % -3 là −2.
int tăng độ chính xác theo nhu cầu; vẫn tốn thời gian và bộ nhớ khi số rất lớn.
Hỏi nhanh
print(-7 // 2, -7 % 2) in ra gì?
// của Python làm tròn xuống, nên −3.5 thành −4. Số dư là −7−(−4)⋅2=1. Ra -3 -1 là tính theo kiểu C++.
import math
print(0.1 + 0.2, 0.1 + 0.2 == 0.3)
print(math.isclose(0.1 + 0.2, 0.3,
rel_tol=1e-9, abs_tol=1e-12))
print(round(2.5), round(3.5), round(0.125, 2))
Kết quả0.30000000000000004 False
True
2 4 0.12
math.isclose, không dùng ==.round chọn mốc chẵn khi số nằm đúng giữa.a = [1, 2, 3]
b = a
b.append(4)
print(a, b, a is b)
Kết quả[1, 2, 3, 4] [1, 2, 3, 4] True
append, a += [5]: sửa list chung.a = a + [5]: tạo list mới, gán lại tên a.is: cùng đối tượng? ==: giá trị bằng nhau?Hỏi nhanh
def reset(scores):
scores.append(0)
scores = []
marks = [7, 8]
reset(marks)
print(marks)
Đoạn code in ra gì?
scores và marks cùng dán lên một list, nên append(0) sửa list của người gọi. scores = [] chỉ bóc nhãn scores sang list mới, marks vẫn ở list cũ.
grid = [[0] * 3] * 3
grid[0][0] = 9
print(grid)
grid = [[0] * 3 for _ in range(3)]
grid[0][0] = 9
print(grid)
Kết quả[[9, 0, 0], [9, 0, 0], [9, 0, 0]]
[[9, 0, 0], [0, 0, 0], [0, 0, 0]]
* 3 lặp lại tên của cùng một hàng, không tạo hàng mới.
Comprehension tạo một hàng mới ở mỗi lượt lặp.
copy() chỉ chép một tầng; deepcopy() chép mọi tầng.
Hỏi nhanh
Chạy a = [[1, 2], [3, 4]], b = a.copy(), b[0].append(9). Lúc này a là gì?
copy() chỉ tạo list ngoài mới. Các list con vẫn dùng chung, nên thêm 9 vào b[0] cũng là thêm vào a[0]. Muốn chép riêng cả list con thì dùng copy.deepcopy.
| Công việc | Cấu trúc |
|---|---|
| Giữ thứ tự, lấy bằng vị trí | list |
| Một bộ giá trị cố định | tuple |
| Tra cứu theo khóa | dict |
| Kiểm thuộc tập nhiều lần | set |
x in list: O(n) ở trường hợp xấu.
x in set: trung bình O(1), nhưng dựng set cũng tốn O(n).
Khóa của dict, phần tử của set phải hashable. (3, 4) được; list, hay tuple chứa list, thì không.
in trên set nhanhHỏi nhanh
Có 50 000 mã đã nộp bài. Cần kiểm 10 000 mã khác xem đã nộp chưa. Nên làm thế nào?
Với list, mỗi lần in dò từ đầu đến cuối, tổng cộng cỡ 5⋅108 phép so sánh. Set tra bằng bảng băm, mỗi lần trung bình O(1). Sắp xếp không giúp, vì in vẫn dò lần lượt.
a = [5, 3, 8, 1, 9, 2]
print(a[1:4], a[-1], a[-2:], a[::-1])
print([x * x for x in a if x % 2 == 1])
Kết quả[3, 8, 1] 2 [9, 2] [2, 9, 1, 8, 3, 5]
[25, 9, 1, 81]
enumerate: vị trí và phần tử.zip: phần tử cùng vị trí của nhiều dãy.sorted(..., key=...): sắp theo một giá trị.d.get(key, default): xử lý khóa chưa có.def add_score(x, history=[]):
history.append(x)
return history
print(add_score(1), add_score(2))
Kết quả[1, 2] [1, 2]
Hai lần gọi dùng chung list mặc định.
def add_score(x, history=None):
if history is None:
history = []
history.append(x)
return history
Hỏi nhanh
add_score(score, history=[]) gọi lần sau vẫn thấy điểm của lần trước. Sửa thế nào để mỗi lần gọi bắt đầu từ list rỗng?
Giá trị mặc định chỉ tạo một lần, lúc Python đọc dòng def, nên mọi lần gọi dùng chung một list. Mặc định None rồi tạo list trong hàm thì mỗi lần gọi có list riêng.
Tổng 0+1+⋯+(n−1):
S=2n(n−1).Với n=500000: 124 999 750 000.
Ba cách: vòng for, sum(range(n)), NumPy. Cả ba phải ra đúng số này.
Rồi mới đo bằng timeit.repeat, mỗi cách chạy nhiều lần.
for chậm nhấtsum và NumPy lặp bên trong code C nên nhanh hơn. Công thức n(n−1)/2 còn nhanh hơn cả ba.
Hỏi nhanh
Hai cách đều tính đúng. Cách A chạy 8 ms nhưng phải chuẩn bị dữ liệu thêm 5 ms. Cách B chạy 11 ms, không cần chuẩn bị. Chỉ chạy một lần thì cách nào nhanh hơn?
Phải cộng cả thời gian chuẩn bị: A tốn 5+8=13 ms, B tốn 11 ms. Cùng độ phức tạp chưa chắc chạy nhanh như nhau.
preds là dict ID → dự đoán.
with path.open(
"w", newline="", encoding="utf-8"
) as file:
writer = csv.writer(file)
writer.writerow(["id", "prediction"])
for student_id, prediction in preds.items():
writer.writerow(
[student_id, prediction]
)
with đóng file khi ra khỏi khối.encoding="utf-8".csv.reader: mọi ô là chuỗi, đổi kiểu trước khi tính.Hỏi nhanh
Đọc cột điểm bằng csv, ta được ["9", "10", "2"]. sorted trả về gì?
Các phần tử vẫn là chuỗi, được so từng ký tự từ trái sang. Vì '1' < '2' < '9' nên "10" đứng đầu. Muốn sắp theo số thì dùng key=float.
Bốn thông báo cuối của bốn traceback riêng:
Kết quảTypeError: can only concatenate str (not "int") to str
KeyError: 'Dũng'
IndexError: list index out of range
ValueError: invalid literal for int() with base 10: '7.5'
File ..., line ...: vị trí cần mở code.Hỏi nhanh
marks có 3 phần tử. Vòng for i in range(1, len(marks) + 1): print(marks[i]) chạy ra sao?
Chỉ số hợp lệ là 0, 1, 2. Vòng lặp bỏ qua phần tử đầu, rồi đến i = 3 thì vượt ra ngoài list. Duyệt thẳng phần tử hoặc dùng enumerate thì không mắc lỗi này.
class ScoreBook:
def __init__(self, names, marks):
self.names = list(names)
self.marks = list(marks)
def __len__(self):
return len(self.marks)
def __getitem__(self, i):
return self.names[i], self.marks[i]
__init__ chạy khi tạo đối tượng; self là chính đối tượng.len(book) gọi __len__, book[1] gọi __getitem__.super().__init__(...) để chạy phần khởi tạo của cha.Dataset và nn.Module ở chương 3 dùng đúng các hàm này.Hỏi nhanh
Xóa dòng super().__init__(factor) trong ShiftScaler.__init__, rồi gọi ShiftScaler(2, 1)([1, 2, 3]). Điều gì xảy ra?
Python chỉ chạy __init__ của class con. Dòng gán self.factor nằm trong __init__ của cha nên không chạy; tạo đối tượng vẫn được, nhưng forward của cha đọc self.factor thì báo AttributeError.
sum hoặc NumPy.encoding="utf-8", đọc lại file nộp.Luyện đề 1
Sau a = [1, 2], b = [1, 2], c = a, lệnh print(a == b, a is b, a is c) in ra gì?
== so nội dung, nên a == b đúng. is hỏi hai tên có cùng dán trên một đối tượng không. a và b là hai list riêng, còn c chỉ là tên thứ hai của a.
Luyện đề 2
Quen C++, một bạn viết [1, 2] + [3, 4] để cộng từng phần tử. Python cho kết quả gì?
Với list, + là phép nối, giống cộng hai chuỗi. Cộng từng cặp phần tử là việc của mảng NumPy ở bài 0.2.
Luyện đề 3
Có words = ["mèo", "chó", "mèo"]. Cần gán cho mỗi từ khác nhau một số 0, 1,… Chạy lại lần nào cũng phải ra đúng một bảng. Viết thế nào?
Với chuỗi, mỗi lần chạy Python có thể duyệt set theo một thứ tự khác. Sắp xếp rồi mới đánh số thì lần nào cũng ra cùng một bảng. sorted(words) không bỏ trùng, nên các số không liền nhau.
Phỏng theoUSA-NA-AIO 2026 · Vòng 1 · Part 5.1-5.2
//, % làm tròn xuống, khác C++ với số âm.
Gán không sao chép; cần list riêng thì copy().
Tính trên nhiều số thì dùng sum hoặc NumPy.
Làm 10 câu kiểm tra cuối bài trên trang bài học.
Chạy notebook: Restart & Run all, rồi thử đổi n mà không chạy lại ô dưới.