Phòng học Karinoya

Chứng chỉ · Lab luyện thi Cloud / AI / Python

Kiểu dữ liệu và chuỗi ký tự trong Python

Bạn có thể đọc câu hỏi và lời giải bằng Tiếng Việt. Bài giảng (bài viết giải thích) chỉ có bản tiếng Nhật.

Xem bản tiếng Nhật (có bài giảng) →

Câu 1 | Giáo trình chính

Sách giáo trình chính thức của kỳ thi chứng chỉ kỹ sư cơ bản Python 3 (Python 3 エンジニア認定基礎試験) là cuốn nào?

  1. 『Pythonチュートリアル』(Hướng dẫn Python) do nhà xuất bản O'Reilly Japan (オライリー・ジャパン) phát hành
  2. 『あたらしいデータ分析の教科書』(Giáo trình phân tích dữ liệu kiểu mới) do nhà xuất bản Shoeisha (翔泳社) phát hành
  3. 『Pythonデータ分析 実践ハンドブック』(Cẩm nang thực hành phân tích dữ liệu Python) do nhà xuất bản Impress (インプレス) phát hành
  4. 『Python実践レシピ』(Công thức thực hành Python) do nhà xuất bản Gijutsu-Hyoron-sha (技術評論社) phát hành
Đáp ánA. 『Pythonチュートリアル』(Hướng dẫn Python) do nhà xuất bản O'Reilly Japan (オライリー・ジャパン) phát hành

Giáo trình chính của kỳ thi cơ bản là 『Pythonチュートリアル』(Hướng dẫn Python) của O'Reilly Japan, trang chính thức ghi rằng từ ngày 1 tháng 9 năm 2021 kỳ thi tương ứng với bản in lần thứ 4 của sách này. Phạm vi ra đề cũng được công bố theo số chương và tên chương của cuốn sách này, nên việc ôn luyện là bám theo trọng số của từng chương trong sách. 『Python実践レシピ』(O'Reilly Japan tương tự nhưng do Gijutsu-Hyoron-sha phát hành) là giáo trình của kỳ thi thực hành, 『Pythonによるあたらしいデータ分析の教科書』của Shoeisha là giáo trình của kỳ thi phân tích dữ liệu, còn 『Pythonデータ分析 実践ハンドブック』của Impress là giáo trình của kỳ thi thực hành phân tích dữ liệu; cả ba đều là giáo trình của các kỳ thi khác do cùng một hiệp hội tổ chức. Nếu nhầm giáo trình thì sẽ mất công học mãi những nội dung ngoài phạm vi, nên cần kiểm tra kỹ ngay từ đầu.

Câu 2 | Chương có nhiều câu hỏi nhất

Trong phạm vi ra đề của kỳ thi cơ bản, chương nào trong giáo trình chính có số câu hỏi ra nhiều nhất?

  1. Chương 5 "Cấu trúc dữ liệu" (データ構造)
  2. Chương 4 "Công cụ cấu trúc điều khiển" (制御構造ツール)
  3. Chương 3 "Phần nhập môn nhẹ nhàng" (気楽な入門編)
  4. Chương 8 "Lỗi và ngoại lệ" (エラーと例外)
Đáp ánB. Chương 4 "Công cụ cấu trúc điều khiển" (制御構造ツール)

Nhiều nhất là Chương 4 "Công cụ cấu trúc điều khiển", với 9 trên tổng số 40 câu. Tiếp theo là Chương 5 "Cấu trúc dữ liệu" với 7 câu, rồi đến Chương 3 "Phần nhập môn nhẹ nhàng" với 6 câu, còn Chương 8 "Lỗi và ngoại lệ" có 4 câu. Chỉ riêng ba chương 3, 4, 5 đã chiếm 22 câu, vượt quá một nửa tổng số. Vì vậy việc phân bổ nhiều thời gian học hơn cho ba chương này là hợp lý, đặc biệt nếu để chương 4 vốn nói về câu lệnh for và định nghĩa hàm sang sau cùng thì sẽ khó bù lại kịp.

Câu 3 | Tỷ lệ theo chương

Trong phạm vi ra đề của kỳ thi cơ bản, Chương 4 "Công cụ cấu trúc điều khiển" chiếm tỷ lệ bao nhiêu?

  1. 17,5%
  2. 10,0%
  3. 22,5%
  4. 15,0%
Đáp ánC. 22,5%

Chương 4 "Công cụ cấu trúc điều khiển" chiếm 9 trên 40 câu, tính ra tỷ lệ là 22,5%. Đây là con số lớn nhất theo từng chương, tiếp theo là Chương 5 "Cấu trúc dữ liệu" với 7 câu, tương ứng 17,5%. 15,0% tương ứng với 6 câu của Chương 3 "Phần nhập môn nhẹ nhàng", còn 10,0% tương ứng với 4 câu của cả Chương 8 "Lỗi và ngoại lệ" lẫn Chương 10 "Vòng quanh thư viện chuẩn" (標準ライブラリめぐり). Cách phân bổ này không phải là con số vận hành thực tế mà được công bố chính thức như một phần của phạm vi ra đề, nên có thể dùng làm căn cứ cho kế hoạch học tập.

Câu 4 | Phân bổ Chương 13

Trong phạm vi ra đề của kỳ thi cơ bản, Chương 13 "Tiếp theo là gì?" (次はなに?) được phân bổ bao nhiêu câu hỏi?

  1. 0 câu
  2. 1 câu
  3. 4 câu
  4. 2 câu
Đáp ánA. 0 câu

Chương 13 "Tiếp theo là gì?" có 0 câu, là chương duy nhất trong phạm vi ra đề hoàn toàn không có câu hỏi nào. Vì nội dung chương này chỉ hướng dẫn bước đi tiếp theo trong việc học, nên bị loại khỏi phạm vi của một kỳ thi kiểm tra ngữ pháp. Các chương được phân bổ 1 câu là Chương 1, 2, 7, 11, 12, 14; 2 câu là Chương 6 và Chương 9; còn 4 câu là Chương 8 "Lỗi và ngoại lệ" và Chương 10 "Vòng quanh thư viện chuẩn". Ngoài Chương 13, mọi chương khác đều có từ 1 câu trở lên, nên với cả những chương được phân bổ ít cũng nên chuẩn bị ở mức lướt qua các thuật ngữ.

Câu 5 | sep của print

Khi chạy chương trình sau, chuỗi nào được hiển thị trên màn hình?

print('Python', 3, 'exam', sep='/')
  1. Python3exam
  2. Python 3 exam
  3. Python/3/exam/
  4. Python/3/exam
Đáp ánD. Python/3/exam

print() nối các đối số nhận được bằng đối số từ khóa sep. Giá trị mặc định của sep là một dấu cách đơn, nên nếu không chỉ định thì kết quả sẽ là Python 3 exam, nhưng ở đây đã chỉ định sep='/' nên các phần được nối bằng dấu gạch chéo, cho ra Python/3/exam. Python3exam là kết quả khi sep là chuỗi rỗng. Việc chọn Python/3/exam/ với dấu gạch chéo ở cuối là do hiểu nhầm rằng sep được thêm vào sau mỗi phần tử, nhưng thực tế sep chỉ được chèn giữa các đối số với nhau. Ký tự thêm ở cuối là vai trò của end.

Câu 6 | end của print

Khi thực thi 3 dòng sau theo thứ tự từ trên xuống, điều gì hiện ra trên màn hình?

print('A', end='')print('B', end='')print('C')
  1. Hiển thị AB và C trên 2 dòng
  2. Hiển thị ABC trên 1 dòng duy nhất
  3. Hiển thị A B C trên 1 dòng duy nhất
  4. Hiển thị A, B, C trên 3 dòng
Đáp ánB. Hiển thị ABC trên 1 dòng duy nhất

print() gắn đối số từ khóa end vào cuối phần hiển thị. Giá trị mặc định là ký tự xuống dòng, nên nếu không chỉ định thì mỗi lần gọi print() sẽ xuống dòng. Ở đây, hai lệnh đầu đã đặt end='' để ngăn xuống dòng nên A và B được hiển thị nối tiếp nhau, còn lệnh print() cuối cùng dùng end mặc định nên xuống dòng, kết quả là hiển thị ABC trên 1 dòng duy nhất. Trường hợp A B C có dấu cách xen giữa là khi truyền 3 đối số cho 1 lệnh print(); trường hợp chia thành 3 dòng là khi hoàn toàn không chỉ định end; trường hợp chia thành 2 dòng là khi chỉ bỏ end='' ở một trong hai lệnh.

Câu 7 | Hiển thị sau khi giải nén

Kết quả hiển thị của chương trình sau, khi giải nén (unpack) danh sách để hiển thị, là gì?

nums = [10, 20, 30]print(*nums, sep=', ')
  1. Hiển thị 10, 20, 30
  2. Hiển thị 102030
  3. Hiển thị [10, 20, 30]
  4. Hiển thị 10 20 30
Đáp ánA. Hiển thị 10, 20, 30

print(*nums) có dấu hoa thị sẽ truyền từng phần tử của danh sách như các đối số riêng biệt cho print(). Do đó 3 đối số được nối bằng sep, và vì sep=', ' nên kết quả là 10, 20, 30. Nếu viết print(nums) không có dấu hoa thị, danh sách được truyền như 1 đối số duy nhất nên [10, 20, 30] có cả dấu ngoặc vuông sẽ hiển thị nguyên vẹn. Nếu không chỉ định sep thì dùng dấu cách mặc định cho ra 10 20 30, còn nếu sep='' thì các số dính liền nhau thành 102030.

Câu 8 | Ký hiệu chú thích

Đâu là mô tả đúng về chú thích (comment) trong Python?

  1. Phần nằm giữa /* và */ bị bỏ qua như một chú thích
  2. Từ # đến cuối dòng bị bỏ qua như một chú thích
  3. Từ -- đến cuối dòng bị bỏ qua như một chú thích
  4. Từ // đến cuối dòng bị bỏ qua như một chú thích
Đáp ánB. Từ # đến cuối dòng bị bỏ qua như một chú thích

Chú thích trong Python được viết bằng dấu thăng (#), và phần từ đó đến cuối dòng sẽ bị bỏ qua. Nếu đặt ở đầu dòng thì cả dòng đó là chú thích, còn nếu đặt ở giữa dòng thì phần từ đó trở đi là chú thích. Python không có cú pháp riêng để gộp nhiều dòng thành một khối chú thích, nên phải đặt dấu thăng ở từng dòng. Cách viết bao quanh bằng ba dấu nháy kép đôi khi được dùng thay cho chú thích khối, nhưng về mặt cú pháp đó là một chuỗi ký tự (string literal); nếu đặt ở đầu hàm hay lớp thì nó được giữ lại khi chạy như docstring. Hai dấu gạch chéo (//) trong Python là toán tử chia lấy phần nguyên (floor division), còn cặp /* */ hay hai dấu gạch ngang (--) là cách viết chú thích của các ngôn ngữ khác.

Câu 9 | Thụt lề

Khi lưu chương trình sau vào tệp rồi chạy, điều gì sẽ xảy ra?

if True:    print('one')        print('two')
  1. Chỉ two được hiển thị rồi xử lý kết thúc
  2. Chỉ one được hiển thị rồi xử lý kết thúc
  3. one và two được hiển thị theo thứ tự đó
  4. Xảy ra lỗi IndentationError và dừng lại
Đáp ánD. Xảy ra lỗi IndentationError và dừng lại

Python biểu diễn khối lệnh bằng độ sâu thụt lề, nên các câu lệnh cùng thuộc một khối phải có độ thụt lề giống nhau. Ở đây chỉ dòng thứ 3 bị thụt sâu hơn, hơn nữa dòng ngay trước đó không phải là dòng mở khối mới (dòng kết thúc bằng dấu hai chấm), nên xảy ra IndentationError: unexpected indent. Lỗi này được phát hiện ngay trong bước phân tích cú pháp trước khi thực thi, nên print ở dòng thứ 2 cũng không được chạy, thậm chí one cũng không được hiển thị. Nếu cần thụt lề mà không thụt thì báo lỗi expected an indented block, còn nếu giữa chừng độ thụt không khớp thì báo unindent does not match any outer indentation level, đây là các thông báo khác nhau nhưng đều thuộc loại IndentationError.

Câu 10 | Bảng mã ký tự

Đâu là mô tả đúng về bảng mã ký tự (encoding) của tệp mã nguồn Python 3?

  1. Mặc định là Shift_JIS, có thể thay đổi nếu khai báo
  2. Mặc định là UTF-8, có thể viết trực tiếp tiếng Nhật
  3. Mặc định là EUC-JP, có thể viết trực tiếp tiếng Nhật
  4. Mặc định là ASCII, cần khai báo để đưa tiếng Nhật vào
Đáp ánB. Mặc định là UTF-8, có thể viết trực tiếp tiếng Nhật

Tệp mã nguồn Python 3 mặc định được đọc dưới dạng UTF-8, nên có thể viết trực tiếp tiếng Nhật cả trong chú thích lẫn trong chuỗi ký tự. Ở Python 2, mặc định là ASCII nên khi muốn đưa tiếng Nhật vào phải viết khai báo coding ở dòng thứ nhất hoặc thứ hai của tệp, nhưng ở Python 3 thì khai báo này không còn cần thiết. Việc viết khai báo chỉ cần thiết khi muốn đọc một tệp được lưu bằng bảng mã khác UTF-8. Ngoài ra, tên định danh (tên biến, tên hàm) cũng có thể dùng tiếng Nhật, nhưng không được khuyến khích xét về mặt dễ đọc.

Câu 11 | Kiểu của phép chia

Chương trình sau xử lý phép chia giữa hai số nguyên, hiển thị giá trị ở dòng 1 và tên kiểu ở dòng 2. Tổ hợp nào là đúng?

print(9 / 3)print(type(9 / 3).__name__)
  1. Giá trị là 3.0, kiểu là int
  2. Giá trị là 3.0, kiểu là float
  3. Giá trị là 3, kiểu là int
  4. Giá trị là 3, kiểu là float
Đáp ánB. Giá trị là 3.0, kiểu là float

Dấu gạch chéo đơn / là phép chia thật (true division), kết quả luôn là float. Kể cả khi chia hết cũng không ngoại lệ, nên 9 / 3 không phải là 3 mà là 3.0, và kiểu cũng là float. Nếu nghĩ rằng phép tính giữa hai số nguyên sẽ cho ra int thì sẽ chọn nhầm 3 và int; khi muốn có int thì cần dùng phép chia lấy phần nguyên 9 // 3, lúc đó mới cho ra 3 và int. Các tổ hợp giá trị và kiểu không khớp nhau như 3 với float, hay 3.0 với int, không thể phát sinh từ biểu thức này.

Câu 12 | Chia lấy phần nguyên với số âm

Kết quả hiển thị của chương trình sau, xếp liền các phép chia lấy phần nguyên, là gì?

print(9 // 4)print(-9 // 4)
  1. Dòng 1 là 2.0, dòng 2 là -3.0
  2. Dòng 1 là 2, dòng 2 là -3
  3. Dòng 1 là 2.25, dòng 2 là -2.25
  4. Dòng 1 là 2, dòng 2 là -2
Đáp ánB. Dòng 1 là 2, dòng 2 là -3

Hai dấu gạch chéo // là phép chia lấy phần nguyên (floor division), làm tròn thương xuống theo hướng nhỏ hơn trên trục số. 9 / 4 là 2.25 nên 9 // 4 là 2; -9 / 4 là -2.25 nên -9 // 4 không phải -2 mà là -3. Điểm mấu chốt là không làm tròn về phía 0; việc chọn -2 là do nghĩ theo kiểu làm tròn về phía 0 như trong C. 2.25 và -2.25 là giá trị khi dùng / thay vì //, còn 2.0 và -3.0 là giá trị khi trộn float vào toán hạng như viết 9.0 // 4; nếu cả hai vế đều là int thì kết quả cũng là int.

Câu 13 | divmod

divmod(-11, 3) trả về giá trị nào?

  1. (-4, 1)
  2. (-3, -2)
  3. (-4, -2)
  4. (-3, 2)
Đáp ánA. (-4, 1)

divmod(a, b) trả về tuple (a // b, a % b). Vì // của Python làm tròn xuống, nên -11 // 3 là -4, và dấu của số dư được đặt theo dấu của số chia nên -11 % 3 là 1. Vậy kết quả là (-4, 1). Nếu tính theo cách làm tròn về phía 0 sẽ ra thương -3, dư -2 và chọn nhầm (-3, -2), nhưng Python không dùng cách này. Thương và số dư luôn giữ quan hệ số chia nhân thương cộng số dư bằng giá trị ban đầu, nên 3 nhân -4 cộng 1 bằng -11, có thể kiểm chứng. Theo quan hệ này, cả (-3, 2) lẫn (-4, -2) đều không trả về -11.

Câu 14 | Làm tròn xuống của int

Kết quả hiển thị của chương trình sau, so sánh hướng làm tròn xuống, là gì?

print(int(-2.7))print(-2.7 // 1)
  1. Dòng 1 là -2, dòng 2 là -2.0
  2. Dòng 1 là -3, dòng 2 là -3.0
  3. Dòng 1 là -2, dòng 2 là -3.0
  4. Dòng 1 là -3, dòng 2 là -2.0
Đáp ánC. Dòng 1 là -2, dòng 2 là -3.0

Hàm dựng sẵn int() bỏ phần thập phân và làm tròn về phía 0, nên int(-2.7) không phải là -3 mà là -2. Trong khi đó // làm tròn xuống theo hướng nhỏ hơn, nên -2.7 // 1 nghiêng về phía -3, và vì toán hạng có float nên kết quả cũng là float -3.0. Với số dương thì cả int(2.7) lẫn 2.7 // 1 đều nghiêng về phía 2 nên không thấy khác biệt, nhưng với số âm thì hướng làm tròn tách ra. Cả hai đều không phải làm tròn kiểu tứ xả ngũ nhập (round half up), nên khi muốn -2.7 trở thành -3 phải dùng round(-2.7).

Câu 15 | Sai số của số thập phân

Kết quả hiển thị của chương trình sau, đưa ra 2 giá trị boolean trên 2 dòng, là gì?

print(0.1 + 0.7 == 0.8)print(round(0.1 + 0.7, 2) == 0.8)
  1. Dòng 1 là False, dòng 2 là False
  2. Dòng 1 là True, dòng 2 là True
  3. Dòng 1 là True, dòng 2 là False
  4. Dòng 1 là False, dòng 2 là True
Đáp ánD. Dòng 1 là False, dòng 2 là True

float lưu số thập phân dưới dạng nhị phân nên các số như 0.1 hay 0.7 vốn gọn gàng trong hệ thập phân lại không thể biểu diễn chính xác. Thực tế khi hiển thị 0.1 + 0.7 sẽ ra 0.7999999999999999, khác một chút so với 0.8, nên dòng 1 là False. Dòng 2 dùng round() để làm tròn đến 2 chữ số thập phân trước khi so sánh, nên cả hai vế đều thành 0.8 và kết quả là True. Đây không phải là khiếm khuyết riêng của Python mà là tính chất của số thực dấu phẩy động nhị phân theo chuẩn IEEE 754; 0.1 + 0.2 ra 0.30000000000000004, hay 1.1 + 2.2 ra 3.3000000000000003 cũng vì cùng lý do này. Nên tránh so sánh bằng trực tiếp giữa hai giá trị float, mà cần làm tròn đến số chữ số cần thiết trước khi so sánh, hoặc so sánh giá trị tuyệt đối của hiệu với một sai số cho phép, hay dùng mô-đun decimal trong các trường hợp cần độ chính xác nghiêm ngặt như tiền tệ.

Câu 16 | Làm tròn của round

Kết quả hiển thị của chương trình sau, xếp các giá trị đã làm tròn trên 1 dòng, là gì?

print(round(1.5), round(2.5), round(3.5))
  1. 2 2 4
  2. 2 3 4
  3. 1 3 3
  4. 1 2 3
Đáp ánA. 2 2 4

round() của Python làm tròn theo quy tắc làm tròn đến số chẵn gần nhất (round half to even), còn gọi là làm tròn kiểu ngân hàng. Chỉ khi giá trị đúng bằng một nửa thì mới nghiêng về phía số chẵn gần đó, nên 1.5 thành 2, 2.5 thành 2, 3.5 thành 4, và kết quả hiển thị là 2 2 4. Nếu làm tròn kiểu tứ xả ngũ nhập như học ở trường sẽ ra 2 3 4, nhưng Python không dùng cách này. 1 2 3 là giá trị khi đơn giản bỏ phần thập phân, còn 1 3 3 là giá trị khi nghiêng về phía số lẻ, cả hai đều không đúng với hành vi thực tế. Lưu ý round() khi bỏ đối số thứ hai sẽ trả về int, còn khi chỉ định số chữ số như round(2.5, 0) thì trả về float 2.0.

Câu 17 | bool là int

Kết quả hiển thị của chương trình sau, xử lý giá trị boolean như số, là gì?

print(True + True)print(isinstance(True, int))
  1. Dòng 1 là True, dòng 2 là True
  2. Dòng 1 là 2, dòng 2 là False
  3. Dòng 1 là 2, dòng 2 là True
  4. Dòng 1 là True, dòng 2 là False
Đáp ánC. Dòng 1 là 2, dòng 2 là True

bool là kiểu kế thừa từ int, nên True được dùng trực tiếp trong phép tính số học như số nguyên 1, còn False như số nguyên 0. True + True là 1 cộng 1 bằng 2, và isinstance(True, int) trả về True. Việc nghĩ dòng 1 là True là do nhầm + với phép hoặc logic (or), nhưng phép hoặc logic của Python là or, và True or True cũng cho True. issubclass(bool, int) cũng là True, và sum([True, False, True]) bằng 2 cũng vì cùng lý do này. Tính chất này thường được dùng để đếm số lượng phần tử thỏa điều kiện.

Câu 18 | Độ chính xác của số nguyên

Đâu là mô tả đúng về kiểu int trong Python 3?

  1. Không có giới hạn số chữ số, có thể xử lý giá trị lớn tùy theo bộ nhớ cho phép
  2. Giá trị vượt quá 32 bit có thể bị tràn và trở thành giá trị âm
  3. Khi số chữ số vượt giới hạn thì OverflowError sẽ được phát sinh
  4. Vượt quá 64 bit sẽ tự động được chuyển sang float để tính toán
Đáp ánA. Không có giới hạn số chữ số, có thể xử lý giá trị lớn tùy theo bộ nhớ cho phép

int của Python 3 có độ chính xác tùy ý (arbitrary precision), không có giới hạn số chữ số. 3 ** 40 cho ra chính xác 12157665459056928801, vượt phạm vi số nguyên 64 bit có dấu, nhưng kiểu vẫn là int. Các giá trị như 2 ** 1000 cũng xử lý được; nếu đếm số chữ số bằng len(str(2 ** 1000)) sẽ ra 302 chữ số. Vì không phải là số nguyên độ dài cố định như trong C, nên không xảy ra tràn số thành âm, cũng không tự chuyển sang float. OverflowError xuất hiện khi vượt quá phạm vi mà float có thể biểu diễn, chứ không phát sinh chỉ vì số chữ số của int tăng lên. Tuy nhiên số chữ số càng nhiều thì thời gian tính toán và bộ nhớ dùng càng tăng.

Câu 19 | Chỉ số của bytes

Chương trình sau xử lý chuỗi byte (bytes), kết quả hiển thị ở dòng 2 và dòng 3 là gì?

b = 'abc'.encode()print(b[0])print(b[0:1])
  1. Dòng 1 là b'a', dòng 2 là b'a'
  2. Dòng 1 là 97, dòng 2 là b'a'
  3. Dòng 1 là 97, dòng 2 là 97
  4. Dòng 1 là a, dòng 2 là b'a'
Đáp ánB. Dòng 1 là 97, dòng 2 là b'a'

Khi truy cập bytes bằng chỉ số nguyên, giá trị byte ở vị trí đó được trả về dưới dạng int. Byte đầu tiên của b'abc', là kết quả mã hóa UTF-8 của 'abc', chính là điểm mã (code point) của 'a', nên là 97. Ngược lại, khi lấy ra bằng slice thì trả về bytes có độ dài 1, nên b[0:1] là b'a'. Với str thì cả s[0] lẫn s[0:1] đều cho cùng một str là 'a', nên đây chính là điểm đối lập giữa str và bytes. Việc hiển thị ra b'a' thay vì a là vì print() hiển thị bytes dưới dạng repr; khi muốn chuyển về chuỗi ký tự thì dùng b.decode().

Câu 20 | Hệ đếm và ký tự

Kết quả hiển thị của chương trình sau, xếp liền phép đổi hệ đếm và đổi bảng mã ký tự, là gì?

print(int('1010', 2), bin(10), hex(255), ord('A'))
  1. 10 0b1010 0xff 65
  2. 10 0b1010 0xFF 65
  3. 10 0b1010 0xff 97
  4. 1010 0b1010 0xff 65
Đáp ánA. 10 0b1010 0xff 65

Đối số thứ hai của int() là cơ số (base), nên int('1010', 2) đọc chuỗi theo hệ nhị phân và trả về 10. Việc nghĩ rằng kết quả vẫn là 1010 là do bỏ sót việc chỉ định cơ số. bin() trả về chuỗi có tiền tố 0b nên là '0b1010', còn hex() trả về chuỗi chữ thường có tiền tố 0x nên là '0xff'. Không có chuyện trả về dạng chữ hoa 0xFF; khi muốn có dạng chữ hoa thì dùng format(255, 'X'). ord() trả về điểm mã của ký tự, chữ hoa A là 65. 97 là điểm mã của chữ thường a; đổi ngược lại có thể viết chr(65) để ra 'A'.

Câu 21 | Slice

Kết quả hiển thị của chương trình sau, xử lý slice trên chuỗi ký tự, là gì?

s = 'Pythonic'print(s[2:5])print(s[-3:])
  1. Dòng 1 là tho, dòng 2 là nic
  2. Dòng 1 là tho, dòng 2 là ic
  3. Dòng 1 là thon, dòng 2 là nic
  4. Dòng 1 là yth, dòng 2 là nic
Đáp ánA. Dòng 1 là tho, dòng 2 là nic

s[2:5] lấy ra phần bắt đầu từ vị trí chỉ số 2 cho đến trước vị trí chỉ số 5. Vị trí chỉ số 2 của 'Pythonic' là t, nên lấy 3 ký tự t, h, o thành tho. Vì vị trí kết thúc không được bao gồm nên không thành thon. Nếu đếm chỉ số từ 1 và hiểu là "từ ký tự thứ 2" thì sẽ chọn nhầm yth, tức là s[1:4]. s[-3:] dùng chỉ số âm nên đếm từ cuối, lấy từ ký tự thứ 3 tính từ cuối làm điểm bắt đầu cho đến hết, nên là nic. Nếu hiểu -3 là "đến ký tự thứ 3 tính từ cuối" thì sẽ chọn nhầm ic, tức là s[-2:]. Nắm vững hai điểm: chỉ số bắt đầu từ 0, và chỉ số âm đếm từ cuối với vị trí cuối cùng là -1, sẽ không còn nhầm lẫn.

Câu 22 | Ngoài phạm vi và in

Kết quả hiển thị của chương trình sau, đưa ra 2 kết quả phán đoán, là gì?

s = 'Python'print(s[100:200] == '')print('py' in s)
  1. Dòng 1 là True, dòng 2 là False
  2. Dòng 1 là True, dòng 2 là True
  3. Dòng 1 là False, dòng 2 là True
  4. Dòng 1 là False, dòng 2 là False
Đáp ánA. Dòng 1 là True, dòng 2 là False

Slice dù có phạm vi vượt quá độ dài của chuỗi cũng không gây ra ngoại lệ, mà chỉ trả về phần trùng nhau. 'Python' chỉ có 6 ký tự nên s[100:200] trả về chuỗi rỗng, và so sánh với chuỗi rỗng cho ra True. Nếu dùng chỉ số đơn s[100] thì sẽ là IndexError, đây là điểm đối lập cần lưu ý. Toán tử in ở dòng 2 kiểm tra sự tồn tại của chuỗi con, nhưng có phân biệt chữ hoa chữ thường, nên chữ thường 'py' không nằm trong 'Python' và cho ra False. Nếu là 'Py' hoặc 'th' thì sẽ là True.

Câu 23 | Chuỗi bất biến

Khi ghi chương trình sau vào tệp rồi chạy, điều gì sẽ xảy ra?

s = 'abc's[0] = 'z'print(s)
  1. Xảy ra TypeError, không thể gán được
  2. Hiển thị sau khi đã thay thành zbc
  3. Xảy ra IndexError, không thể gán được
  4. Hiển thị nguyên vẹn abc, không thay đổi
Đáp ánA. Xảy ra TypeError, không thể gán được

str là kiểu bất biến (immutable), tức là không thể thay đổi nội dung sau khi đã tạo ra, nên không thể dùng chỉ số để thay đổi từng ký tự riêng lẻ. s[0] = 'z' sẽ gây ra TypeError: 'str' object does not support item assignment, và xử lý dừng lại ngay đó nên print cũng không được thực thi. IndexError xuất hiện khi chỉ số nằm ngoài phạm vi, nhưng ở đây 0 nằm trong phạm vi hợp lệ nên đó là chuyện khác. Khi muốn thay đổi nội dung, cần tạo chuỗi mới rồi ghép lại như s = 'z' + s[1:]. Danh sách (list) là kiểu khả biến (mutable) nên có thể dùng đúng cách viết này để thay đổi phần tử.

Câu 24 | Giá trị trả về của phương thức

Kết quả hiển thị của chương trình sau, gọi các phương thức chuỗi ký tự, là gì?

s = 'exam's.upper()print(s)print(s.replace('e', 'E'))
  1. Dòng 1 là EXAM, dòng 2 là Exam
  2. Dòng 1 là EXAM, dòng 2 là EXAM
  3. Dòng 1 là exam, dòng 2 là exam
  4. Dòng 1 là exam, dòng 2 là Exam
Đáp ánD. Dòng 1 là exam, dòng 2 là Exam

Các phương thức của chuỗi ký tự không thay đổi chuỗi gốc mà tạo ra và trả về một chuỗi mới. s.upper() ở dòng 2 tạo ra 'EXAM' nhưng không gán vào đâu cả nên giá trị đó bị bỏ đi, s vẫn giữ nguyên là 'exam'. Do đó kết quả của print(s) là exam. Dòng 4 hiển thị trực tiếp giá trị trả về của s.replace('e', 'E') nên ra Exam, nhưng thao tác này cũng không làm thay đổi bản thân s. Khi muốn giữ lại kết quả, cần gán lại như s = s.upper(). Đây là điểm đối lập với các phương thức như sort() của list, vốn sắp xếp tại chỗ và trả về None.

Câu 25 | f-string

Kết quả hiển thị của chương trình sau, chỉ định định dạng để hiển thị, là gì?

n = 2.71828print(f'{n:.2f}')print(f'{"ok"!r}')
  1. Dòng 1 là 2.71, dòng 2 là 'ok'
  2. Dòng 1 là 2.72, dòng 2 là ok
  3. Dòng 1 là 2.718, dòng 2 là ok
  4. Dòng 1 là 2.72, dòng 2 là 'ok'
Đáp ánD. Dòng 1 là 2.72, dòng 2 là 'ok'

Chỉ định định dạng :.2f trong f-string làm tròn đến 2 chữ số thập phân để hiển thị, nên 2.71828 trở thành 2.72. Vì không phải là bỏ bớt (truncate) nên không ra 2.71, còn 2.718 là giá trị khi viết :.3f. Chỉ định !r ở dòng 2 là chỉ định chuyển đổi, hiển thị giá trị dưới dạng repr(). Khi dùng với chuỗi ký tự thì có thêm dấu nháy nên ra 'ok'; nếu không có !r thì chỉ hiển thị ok. Vì cú pháp định dạng dùng chung với str.format(), nên viết '{n:.2f}'.format(n=2.71828) cũng cho ra cùng kết quả 2.72.

Câu 26 | split

Kết quả hiển thị của chương trình sau, tách chuỗi ký tự, là gì?

print('a b  c'.split())print('a,b,c'.split(',', 1))
  1. ['a', 'b', 'c'] và ['a', 'b', 'c']
  2. ['a', 'b', '', 'c'] và ['a', 'b,c']
  3. ['a', 'b', 'c'] và ['a', 'b,c']
  4. ['a', 'b', '', 'c'] và ['a', 'b', 'c']
Đáp ánC. ['a', 'b', 'c'] và ['a', 'b,c']

Khi gọi split() không có đối số, các dấu cách liên tiếp được xem như một dấu phân cách duy nhất và không tạo ra phần tử rỗng. Vì vậy 'a b c'.split() cho ra danh sách 3 phần tử ['a', 'b', 'c']. Nếu chỉ định rõ ký tự phân cách như 'a b c'.split(' '), việc tách sẽ theo từng dấu cách đơn nên phần tử rỗng còn lại, cho ra danh sách 4 phần tử ['a', 'b', '', 'c']. Đối số thứ hai là maxsplit, biểu thị giới hạn số lần tách; nếu truyền 1 thì chỉ tách một lần đầu tiên, cho ra ['a', 'b,c'], phần còn lại được gộp thành một phần tử duy nhất không tách tiếp. Nếu bỏ qua đối số này thì tất cả đều được tách, cho ra ['a', 'b', 'c'].

Câu 27 | strip

Kết quả hiển thị của chương trình sau, cắt bỏ ký tự ở hai đầu, là gì?

print('xxabxx'.strip('x'))print('xxabxx'.lstrip('x'))
  1. Dòng 1 là abxx, dòng 2 là ab
  2. Dòng 1 là ab, dòng 2 là abxx
  3. Dòng 1 là xabx, dòng 2 là abxx
  4. Dòng 1 là ab, dòng 2 là xxab
Đáp ánB. Dòng 1 là ab, dòng 2 là abxx

Đối số của strip() là "tập hợp các ký tự cần cắt bỏ", và sẽ cắt liên tục từ hai đầu miễn là ký tự đó còn nằm trong tập hợp. Đây không phải là chỉ xóa một chuỗi con 'x' duy nhất, nên 'xxabxx'.strip('x') sẽ cắt bỏ toàn bộ x ở hai đầu, cho ra ab. Nếu nghĩ rằng chỉ xóa từng ký tự một thì sẽ chọn nhầm xabx. lstrip() chỉ tác động lên đầu trái nên cho ra abxx, còn rstrip() chỉ tác động lên đầu phải nên cho ra xxab. Nếu bỏ qua đối số thì mặc định cắt bỏ ký tự khoảng trắng. Vì được xử lý như một tập hợp, nên nếu viết 'xxabxx'.strip('xa') thì cả xx và a ở đầu trái cũng bị cắt, chỉ còn lại b.

Câu 28 | join và lặp lại

Kết quả hiển thị của chương trình sau, nối các chuỗi ký tự, là gì?

print('-'.join('abc'))print('ab' * 3)
  1. Dòng 1 là -a-b-c-, dòng 2 là ababab
  2. Dòng 1 là abc, dòng 2 là ababab
  3. Dòng 1 là a-b-c, dòng 2 là ab ab ab
  4. Dòng 1 là a-b-c, dòng 2 là ababab
Đáp ánD. Dòng 1 là a-b-c, dòng 2 là ababab

join() lấy chuỗi ký tự gọi nó làm dấu phân cách, nối các phần tử của đối tượng khả lặp (iterable) được truyền vào. Chuỗi ký tự cũng là một đối tượng khả lặp có thể lấy ra từng ký tự, nên '-'.join('abc') sẽ nối 'a', 'b', 'c' bằng dấu gạch ngang thành a-b-c. Vì dấu phân cách chỉ được chèn giữa các phần tử với nhau, nên không xuất hiện ở đầu hay cuối, do đó không thành -a-b-c-. Trường hợp không có dấu phân cách abc là khi viết ''.join('abc'). Toán tử * lặp lại chuỗi ký tự theo số lần chỉ định, nên 'ab' * 3 cho ra ababab, không có dấu cách xen giữa. Nếu muốn có ab ab ab thì cần viết ' '.join(['ab'] * 3).

Câu 29 | find và index

Kết quả hiển thị của chương trình sau, tìm chuỗi con, là gì?

print('banana'.find('z'))print('banana'.index('n'))
  1. Dòng 1 là -1, dòng 2 là 2
  2. Dòng 1 là -1, dòng 2 là 3
  3. Dòng 1 là -1, dòng 2 là 4
  4. Dòng 1 là 0, dòng 2 là 2
Đáp ánA. Dòng 1 là -1, dòng 2 là 2

find() trả về -1 khi không tìm thấy chuỗi con, và không gây ra ngoại lệ. Vì 'banana' không có 'z' nên kết quả là -1. 0 là giá trị khi tìm thấy ở đầu chuỗi, nên không được nhầm lẫn với trường hợp không tìm thấy. index() giống find() ở chỗ trả về vị trí tìm thấy; trong 'banana', ký tự 'n' xuất hiện lần đầu tiên ở chỉ số 2, nên kết quả là 2. Giá trị 3 xuất hiện khi đếm chỉ số từ 1 và hiểu là "ký tự thứ 3", còn 4 là vị trí xuất hiện cuối cùng, muốn có giá trị này thì dùng rfind() hoặc rindex(). Điểm khác nhau duy nhất giữa hai phương thức là khi không tìm thấy: index() sẽ phát sinh ValueError: substring not found và dừng xử lý.

Câu 30 | Chuỗi raw

Kết quả hiển thị của chương trình sau, đếm số ký tự, là gì?

print(len(r'a\nb'), len('a\nb'))
  1. 3 4
  2. 4 4
  3. 3 3
  4. 4 3
Đáp ánD. 4 3

Với chuỗi raw có thêm chữ r đằng trước, dấu gạch chéo ngược (backslash) không được hiểu là ký hiệu bắt đầu chuỗi thoát (escape). r'a\nb' gồm 4 ký tự a, backslash, n, b nên len là 4. Ngược lại, với chuỗi thông thường 'a\nb' thì \n được thay bằng 1 ký tự xuống dòng nên gồm 3 ký tự a, ký tự xuống dòng, b, nên len là 3. Nếu tính cả hai đều là chuỗi thoát sẽ ra 3 3, còn nếu tính cả hai đều là ký tự thô sẽ ra 4 4, nhưng điểm mấu chốt là cách xử lý thay đổi tùy theo có chữ r hay không. Dù có chữ r, kiểu vẫn là str thông thường, không trở thành một kiểu đặc biệt. Ngoài ra, len('\\') là 1, vì phải viết 2 dấu backslash liên tiếp để biểu diễn 1 ký tự backslash.

Luyện tập: giải các câu hỏi trên trang này

Đây là công cụ luyện tập với câu hỏi ngẫu nhiên (hoạt động khi JavaScript được bật). Bạn vẫn có thể đọc toàn bộ câu hỏi và phần giải thích ở trên.

* Lời giải là thông tin phục vụ việc học. Phạm vi ra đề và chế độ thi có thể thay đổi theo từng năm, vì vậy hãy luôn kiểm tra thông báo chính thức của đơn vị tổ chức thi.

Trang này là bản dịch từ nguyên bản tiếng Nhật. Nếu nội dung bản dịch và nguyên bản khác nhau, bản tiếng Nhật sẽ được ưu tiên. Xem nguyên bản tiếng Nhật