100 câu hỏi phỏng vấn thuật toán hàng đầu năm 2022

CÁC CÂU HỎI VỀ NGÔN NGỮ

1] List và tuple khác nhau thế nào?

ListTuple
List là các đối tượng có thể thay đổi được.
[Các đối tượng có thể thay đổi sau khi khởi tạo.]
Tuple bao gồm các đối tượng không thay đổi được.
[Các đối tượng không thể thay đổi được sau khi khởi tạo.]
List sử dụng bộ nhớ lớn. Tuple sử dụng bộ nhớ nhỏ.
List được lưu trong 2 khối bộ nhớ [một cái có kích thước cố định,
cái còn lại có kích thước biến đổi để lưu trữ dữ liệu.]
Tuple được lưu trong một khối nhớ duy nhất.
Tạo list chậm hơn vì cần truy cập 2 khối bộ nhớ. Tạo tuple nhanh hơn tạo list.
Có thể xoá hoặc thay thế các thành phần trong list. Không thể xoá hoặc thay thế các thành phần trong tuple.
Một list có dữ liệu lưu trong cặp ngoặc vuông []. Ví dụ: [1,2,3] Một tuple có dữ liệu lưu trong cặp ngoặc đơn []. Ví dụ: [1,2,3]

Khi nào nên sử dụng list, khi nào nên dùng tuple:

Nên dùng tuple để lưu trữ những thông tin bạn không muốn thay đổi trong quá trình thực thi, chẳng hạn như danh sách chọn lựa giới tính của một người, chúng ta có thể định nghĩa dưới dạng một tuple như sau: gender_chocies = [[1, 'Nam'], [2, 'Nữ'], [3, 'Không tiết lộ']]. Khi lưu vào database, chúng ta chỉ cần lưu các giá trị 1, 2, hoặc 3 để tiện xử lý. Khi hiện thị, chúng ta có thể dễ dàng hiện thị Nam thay cho giá trị 1, Nữ thay cho giá trị 2, và Không tiết lộ thay cho giá trị 3.

lists có thể thay đổi được sau khi khởi tạo, chúng ta nên dùng nó để chứa các thông tin cần thay đổi trong quá trình thực thi chương trình. Ví dụ: danh sách các mã chứng khoán đáp ứng một tiêu chí lọc. Chúng ta lưu trong list để có thể thay đổi thứ tự của các mã chứng khoán trong danh sách đó theo tên, hoặc theo giá.

2] Làm sao để chuyển một list thành một tuple?

  • Cách 1: chuyển toàn bộ list a thành một tuple: tuple[a]
  • Cách 2: chuyển một số thành phần của list a = [1, 2, 3, 4, 5] thành tuple b: b = [a[0], a[3], a[4]]

3] List và array khác nhau thế nào?

ListArray
Python list linh hoạt và có thể lưu nhiều kiểu dữ liệu khác nhau. Python array là một bọc mỏng quanh C array.
List là một phần trong cú pháp của Python, nên không cần khai báo trước. Array cần được import, hoặc khai báo từ các thư viện khác [như numpy]
List có thể thay đổi kích thước một cách nhanh chóng vì Python khởi tạo thêm một số thành phần khác trong list ngay khi khởi tạo. Array không thay đổi kích thước được. Thay vào đó, các giá trị của một array sẽ được sao chép qua một array khác có kích thước lớn hơn.
List có thể chứa các dữ liệu không đồng nhất về kiểu. Array chỉ có thể chứa các dữ liệu đồng nhất về kiểu.
Không thể áp dụng các hàm toán học trực tiếp lên list. Thay vào đó, chúng phải được áp dụng lần lượt trên từng thành phần của list. Array được tối ưu hoá cho các phép tính số học.
List tiêu thụ nhiều bộ nhớ hơn vì chúng phân bổ thêm bộ nhớ cho một số thành phần bổ sung để dễ dàng đưa thêm thành phần vào list. Vì array không thay đổi kích thước sau khi khởi tạo, chúng tiêu thụ ít bộ nhớ hơn.

4] Làm sao để chuyển một list thành một array?

Khi lập trình, sẽ có lúc chúng ta cần chuyển list thành array để thực hiện các phép toán trên đó một cách hiệu quả hơn. Để thực hiện việc này, chúng ta sử dụng hàm numpy.array[]. Hàm này nhận 1 list làm tham số và trả về một array chứa các thành phần của list. Ví dụ:

import numpy as np

a = [1, 2, 3]

a_array = np.array[a]

5] Python quản lý bộ nhớ thế nào?

  • Bộ nhớ Python được quản lý trong không gian nhớ riêng của Python. Tất cả các đối tượng cấu trúc dữ liệu được đặt trong một không gian nhớ riêng. Lập trình viên không truy cập vào vùng nhớ này được mà do Python tự quản lý.
  • Việc phân bổ vùng nhớ cho các đối tượng do trình quản lý bộ nhớ của Python đảm nhiệm. API lõi cho các lập trình viên truy cập qua một số công cụ để lập trình.
  • Python cũng có trình gom rác tích hợp sẵn để tái chế các vùng nhớ không còn sử dụng đến và giải pháp chúng tạo thêm chỗ cho không gian nhớ.

6] Bạn thực hiện đa luồng [multithreading] trong Python thế nào?

  1. Python có một thư viện đa luồng nhưng nếu bạn muốn sử dụng đa luồng để tăng tốc chương trình, sử dụng thư viện này không phải là một ý kiến hay.
  2. Python có một cấu trúc gọi là Khoá Phiên Dịch Toàn Cục [Global Interpreter Lock - GIL]. GIL đảm bảo rằng tại một thời điểm chỉ có một luồng được thực thi. Một luồng lấy GIL, làm một số công việc, rồi GIL cho luồng kế tiếp.
  3. Quá trình này xảy ra rất nhanh, nên dưới ánh mắt con người, nó có vẻ đang thực hiện song song, nhưng thực ra chúng chỉ luân phiên sử dụng cùng một lõi CPU.
  4. Tất cả quá trình luân chuyển GIL này làm tăng thêm tải cho quá trình thực thi. Điều này nghĩa là nếu bạn muốn tăng tốc cho chương trình của mình, sử dụng thư viện đa luồng không phải là ý tưởng tốt.

7] Monkey patching là gì?

Trong Python, thuật ngữ monkey patch chỉ việc thay đổi một lớp [class] hay một module trong quá trình thực thi [run-time]

8] Hàm lambda là gì? Cho 1 ví dụ để minh hoạ khi nào nên dùng, khi nào không nên dùng.

Hàm lambda là một hàm nhỏ không có tên và trả về một đối tượng.

Đối tượng do hàm lambda trả về thường được gán cho 1 biến hoặc được sử dụng như một thành phần của một hàm lớn hơn.

Thay vì dùng từ khoá def để định nghĩa hàm như thông thường, hàm lambda được định nghĩa bằng cách dùng từ khoá lambda. Ví dụ, ta có thể tạo hàm lambda để cộng thêm 10 vào biến a như sau:

x = lambda a : a + 10
print[x[5]]

Kết quả trả về của x[5] sẽ là 15

Mục tiêu của hàm lambda

Hàm lambda dễ đọc hơn nhiều so voi một hàm thông thường vì nó có thể được viết trên 1 dòng lệnh. Do đó, dùng hàm lambda khi biểu thức hàm nhỏ là một cách thực hành tốt.

Cái đẹp của hàm lambda là nó trả về một đối tượng hàm. Đặc tính này làm cho hàm lambda trở nên hữu dụng khi sử dụng với các hàm map hoặc filter vốn đòi hỏi các đối tượng hàm làm tham số.

Hàm lambda không hữu dụng khi biểu thức hàm vượt quá 1 dòng.

9] Pickling và unpickling là gì?

Module Pickle nhận một đối tượng Python, chuyển nó thành chuỗi đại diện, và lưu nó ra tập tin bằng hàm dump. Quá trình này gọi là pickling. Quá trình ngược lại, nghĩa là chuyển một chuỗi đại diện thành đối tượng Python được gọi là unpickling.

 10] Numpy array có lợi thế gì so với list [hoặc list lồng trong list - nested list]?

  1. Python list là những biến tập hợp đa dụng và hiệu quả. Chúng hỗ trợ khá tốt các thao tác chèn [insert,] xoá [deletion,] thêm vào cuối [appending,] và nối list [concatenate.] Cấu trúc duyệt danh sách [list comprehension] giúp cho list dễ xây dựng và thao tác.
  2. Tuy nhiên, chúng có một số hạn chế sau: list không hỗ trợ các phép tính vector ['vectorized' operations] như cộng hay nhân theo từng phần tử [elementwise addition and multiplication,] và việc list chứa các phần tử thuộc nhiều kiểu dữ liệu khác nhau cho thấy Python phải lưu kiểu dữ liệu của mỗi phần tử, và phải thực thi lệnh theo kiểu dữ liệu khi thao tác trên mỗi phần tử.
  3. NumPy không chỉ hiệu quả hơn; nó cũng tiện hơn. Bạn có thể thực hiện nhiều phép tính theo kiểu vector và ma trận miễn phí, giúp tiết kiệm thời gian làm các việc không cần thiết. Các phép tính này được thực hiện một cách hiệu quả.
  4. NumPy array nhanh hơn và bạn có thể sử dụng rất nhiều hàm dựng sẵn [built-in] với Numpy, FFTs, tích chập [convolutions,] tìm nhanh, thống kê cơ bản, đại số tuyến tính, biểu đồ tần suất [histogram] v.v...

11] Giải thích cơ chế kế thừa trong Python và đưa ra một ví dụ minh hoạ:

Kế thừa cho phép một Lớp [Class] thừa hưởng tất cả các thành phần [thuộc tính và hàm] của một Lớp khác. Kế thừa giúp tăng khả năng tái sử dụng mã nguồn, giúp dễ tạo và bảo trì các ứng dụng. Lớp cho kế thừa được gọi là Lớp cha [super class] và lớp kế thừa từ lớp khác được gọi là Lớp con [child class.]

Python hỗ trợ 4 kiểu kế thừa sau:

  1. Kế thừa đơn - một lớp kế thừa từ một lớp cha duy nhất.
  2. Kế thừa đa lớp - lớp con d1 kế thừa từ lớp Cha 1, và d2 kế thừa từ lớp Cha 2.
  3. Kế thừa theo thứ bậc - một lớp cha có thể cho cho nhiều lớp con kế thừa.
  4. Đa kế thừa - một lớp con kế thừa từ nhiều hơn một lớp cha.

12] Tính đa hình [Polymorphism] trong Python là gì?

Tính đa hình chỉ khả năng một đối tượng có thể có nhiều hình thức khác nhau. Ví dụ, nếu Lớp cha có một hàm tên ABC, Lớp con có thể có một hàm cùng tên nhưng với các tham số và biến khác nhau. Python hỗ trợ tính đa hình.

13] Hãy giải thích sự khác nhau giữa hàm range[] và hàm xrange[]:

Trong phần lớn các trường hợp range[] và xrange[] chỉ cùng một tính năng. Chúng đều cho chúng ta tạo ra một Danh sách [List] các số nguyên để sử dụng. Sự khác biệt duy nhất là hàm range trả về một đối tượng Danh sách của Python, còn hàm xrange trả về một đối tượng xrange.

14] Hãy giải thích sự khác nhau giữa Flask và Diango

Django là một khung ứng dụng web [Web framework] mã nguồn mở, trình độ cao giúp "khuyến khích việc phát triển nhanh, thiết kế thực dụng, tinh tế và sạch sẽ." Django chạy nhanh, an toàn, và có khả thể mở rộng [scalable] nhanh. Django có hỗ trợ cộng đồng mạnh mẽ và tài liệu chi tiết.

Django là một gói tổng thể, trong đó bạn sẽ có Bảng điều khiển quản trị [Admin panel,] giao diện cơ sở dữ liệu [CSDL,] và cấu trúc thư mục ngay khi tạo ứng dụng. Ngoài ra, nó còn nhiều tính năng, nên bạn không cần thêm các thư viện riêng và các thư viện phụ thuộc. Một vài tính năng sẵn có là xác thực người dùng, động cơ mẫu [template engine,] định tuyến [routing,] quản lý thay đổi và dịch chuyển thiết kế CSDL [database schema migration,] và rất nhiều tính năng khác.

Django rất linh động. Bạn có thể dùng Django để làm các ứng dụng tối thiểu [MVP - Minimum Viable Product] cho đến các ứng dụng cho các công ty lớn. Để tiện hình dung, một số công ty lớn sử dụng Django là Instagram, Dropbox, Pinterest, và Spotify.

Flask là một khung ứng dụng web vi mô [microframework.] Nó không bao gồm sẵn các thư viện cần thiết cho việc phát triển web toàn tập như Django. Flask tiếp cận theo hướng tối giản, nghĩa là bạn sẽ thêm các thư viện trong quá trình lập trình thay vì đóng gói chúng sẵn trong khung ứng dụng. Triết lý của Flask là Flask chỉ cung cấp những thành phần bạn cần để xây dựng một ứng dụng để bạn có sự linh hoạt và kiểm soát. Nói cách khác, Flask không giả định bạn sẽ cần thư viên này hay thư viên khác và đóng gói chúng sẵn. Thay vào đó, Flask để bạn chọn lựa và thêm các thư viện khi mình cần. Một số tính năng Flask cung cấp là Máy chủ phục vụ web [dành cho quá trình phát triển,] xử lý yêu cầu RESTFUL, http, và rất nhiều tính năng khác.

15] PYTHONPATH là gì?

Đó là một biến môi trường được sử dụng khi một đơn nguyên ứng dụng [module] được nhập [import] vào chương trình. Khi nhập một module, PYTHONPATH sẽ được tìm kiếm để kiểm tra sự có mặt của module được yêu cầu nhập trong các thư mục khác. Trình phiên dịch dùng biến PYTHONPATH để xác định module nào sẽ được tải vào bộ nhớ.

Nguồn: lược dịch từ //dev.to/educative/50-python-interview-questions-and-answers-nh2#q12

16] PEP 8 là gì?

PEP là chữ viết tắt của Python Enhancement Proposal [Đề xuất tăng cường Python.] Đó là một bộ các quy ước về cách viết, định dạng mã nguồn, một bộ các đề xuất về cách viết mã nguồn Python để dễ đọc hơn.

17] Vật trang trí [decorator] Python là gì?

Vật trang trí là một mẫu thiết kế [design pattern] trong Python cho phép người dùng thêm tính năng cho một đối tượng sẵn có mà không cần sửa đổi cấu trúc của nó. Vật trang trí thường được gọi trước định nghĩa hàm chúng ta muốn trang trí.

18] Init là gì?

__init__ là một hàm tạo dựng [constructor] trong Python. Hàm này tự động được gọi để phân chia bộ nhớ khi một đối tượng hay một thực thể [instance] của một Lớp được khởi tạo. Tất cả các Lớp đều có hàm __init__

19] Toán tử bộ ba là gì?

Toán tử bộ ba là một cách viết câu lệnh điều kiện trong Python. Như từ 'bộ ba' trong tên gợi ý, toán tử này bao gồm 3 thành phần:

  • Giá trị điều kiện đúng: Giá trị sẽ được gán cho biến nếu biểu thức điều kiện trả về giá trị True
  • Điều kiện: một biểu thức trả về kết quả True hoặc False
  • Giá trị điều kiện sai: Giá trị sẽ được gán cho biến nếu biểu thức điều kiện trả về giá trị Sai

Toán tử bộ ba có thể được xem như một phiên bản đơn giản hoá, 1 dòng của câu lệnh if-else với một điều kiện.

Cú pháp

var = true_val if condition else false_val

Biến var bên tay trái của toán tử gán sẽ có giá trị: 

  • true_val nếu điều kiện là True
  • false_val nếu điều kiện là False

20] Biến toàn cục [global] và biến địa phương [local] trong Python là gì?

Biến toàn cục là các biến được khai báo bên ngoài phạm vi một hàm hoặc được khai báo trong không gian toàn cục. Các biến này có thể được truy xuất bởi bất kỳ hàm nào trong chương trình.

Biến địa phương là các biến được khai báo trong phạm vi của một hàm. Các biến địa phương tồn tại trong không gian địa phương, và không tồn tại trong không gian toàn cục. Biến địa phương chỉ có thể được truy xuất trong phạm vi hàm mà nó được khai báo.

21] @property trong Python là gì?

@property là một vật trang trí trong Python. @property trước định nghĩa một hàm cho phép truy xuất hàm đó như cách truy xuất một thuộc tính [không cần cặp ngoặc đơn sau tên hàm.]

22] try / except được sử dụng thế nào trong Python?

Một "ngoại lệ" [exception] là một lỗi xảy ra trong quá trình thực thi một chương trình. Khi lỗi này phát sinh, chương trình sẽ dừng lại và tạo ra một "ngoại lệ" và chuyển những "ngoại lệ" này qua bộ xử lý riêng để ngăn chương trình bị dừng thực thi do lỗi [crash.]

Những ngoại lệ được tạo ra bởi một chương trình có thể được bắt trong khối lệnh try và xử lý trong khối lệnh except.

  • Try: hãy thử thực thi khối lệnh sau đây để xem có bị lỗi không
  • Except: nếu lỗi xảy ra ở đoạn lệnh trên, hãy xử lý nó bằng khối lệnh dưới đây

23] Hãy giải thích sự khác biệt giữa Python 2 và Python 3

Python 2Python 3
Mã hoá chuỗi
Python 2 mã hoá chuỗi theo bộ mã ASCII. Unicode là bộ mã cha [super set] của ASCII và do đó, có thể mã hoá nhiều ký tự hơn, bao gồm cả các ký tự nước ngoài.
Mã hoá chuỗi 
Python 3 lưu  chuỗi ở định dạng Unicode theo mặc định.
Phép chia 
Python 2 áp dụng hàm floor vào kết quả của phép chia và chỉ trả về phần nguyên của thương số. Do đó, 5 / 2 sẽ trả về kết quả 2 [thay vì 2.5]
Phép chia
Phép chia trong Python 3 trả về kết quả thập phân như thông thường. 5 / 2 = 2.5
Lệnh in [print]
Python 2 không yêu cầu cặp ngoặc đơn []. Ví dụ: print 'print me!'
Lệnh in 
Python 3 yêu cầu đặt nội dung in giữa cặp ngoặc đơn []. Ví dụ: print['print me!']
Các thư viện 
Nhiều thư viện được xây dựng cho Python 2 và không tương thích với Python 3
Các thư viện 
Một số thư viện mới hơn được xây dựng cho Python 3, và không tương thích với Python 2

24] Hàm join trong Python là gì?

Hàm join trong Python nhận một biến có cấu trúc khả lặp [iterable] và nối chúng lại với nhau sử dụng một giá trị chuỗi.

Cách join làm việc?

Ví dụ: Nối các thành phần bằng chuỗi rỗng 

array = ['H','E','L','L','O']
connector = ""
joined_string = connector.join[array]
print[joined_string]

Output:
HELLO

25] Toán tử tạo từ điển từ biến khả lặp [dictionary comprehension] là gì?

Đây là một cách tạo 1 từ điển trong Python. Nó tạo ra một từ điển bằng cách sáp nhập 2 bộ dữ liệu ở dạng danh sách [list] hoặc mảng [array.]

Dữ liệu của một trong hai list / array sẽ trở thành 'từ khoá' của từ điển, cái còn lại là giá trị. Mỗi 'từ khoá' sẽ là khoá nhận dạng duy nhất cho 1 giá trị, và do đó, kích thước của 2 list/array nên bằng nhau.

Cú pháp chung:

tu_dien_moi = {key:value for [key, value] in bien_kha_lap}

Ví dụ:

Ví dụ dưới đây áp dụng cho CSDL của trường đại học và sử dụng phương pháp sáp nhập đơn giản [không có điều kiện.] Giả sử CSDL chưa nhiều dữ liệu như địa chỉ, điểm, học phí, mã số SV, v.v... Bây giờ chúng ta cần xác định mỗi SV duy nhất và tạo ra một từ điển chỉ chứa thông tin các sinh viên. Quyết định của chúng ta đơn giản dựa vào 2 câu hỏi:

Từ khoá [key] nên là thông tin nào?

Giá trị [value] nên là thông tin nào?

Ở đây chúng ta chọn Mã số SV làm từ khoá và tên sinh viên là giá trị, vì mã số SV là duy nhất, còn tên có thể bị trùng.

rollNumbers =[122,233,353,456]
names = ['alex','bob','can', 'don'] 
NewDictionary={ i:j for [i,j] in zip [rollNumbers,names]}
print[NewDictionary]

Output:
{456: 'don', 233: 'bob', 122: 'alex', 353: 'can'}

26] Làm sao để tạo ra bản sao sâu [deep copy] trong Python?

Tạo bản sao sâu chỉ việc sao chép một đối tượng. Khi dùng toán tử gán [=,] chúng ta không tạo ra bản sao của một đối tượng, mà thay vào đó, chúng ra chỉ trỏ biến của mình vào cùng một đối tượng [tạo bản sao nông.] Điều này nghĩa là thay đổi giá trị của một biến cũng sẽ ảnh hưởng đến giá trị của biến kia, vì nó cùng trỏ về một đối tượng. Sự khác biệt giữa bản sao sâu và bản sao nông chỉ áp dụng với các đối tượng chứa các đối tượng khác như danh sách [list] và các biến thực thể của Lớp.

Phương pháp

Để tạo bản sao sâu của một đối tượng, chúng ta nhập vào chương trình module copy trong Python. Module này có chứa hàm deepcopy[] giúp đơn giản hoá công việc của chúng ta.

Cú pháp

Hàm này nhận đối tượng chúng ta muốn tạo bản sao làm tham số, và trả về bản sao của đối tượng đó.

import copy

# Using '=' operator
x = [1, 2, 3]
y = x
x[0] = 5    # value of 'y' also changes as it is the SAME object
x[1] = 15
print "Shallow copy: ", y

# Using copy.deepcopy[]
a = [10, 20, 30]
b = copy.deepcopy[a]
a[1] = 70   # value of 'b' does NOT change because it is ANOTHER object
print "Deep copy: ", b

Output:
Shallow copy: [5, 15, 3]
Deep copy: [10, 20, 30]

27] Làm sao để kiểm tra xem một từ điển có chứa một từ khoá nào đó?

Kiểm tra xem một từ khoá có trong từ điển hay không trước khi truy xuất giá trị của từ khoá đó trong từ điển là một cách thực hành an toàn. Để làm việc này, Python cung cấp 2 hàm sẵn có [built-in]:

  • Hàm has_key[]:
  • Câu lệnh if-in

Hàm has_key[] trả về kết quả True nếu từ khoá có trong từ điển, và False trong trường hợp ngược lại.

Fruits = {'a': "Apple", 'b':"Banana", 'c':"Carrot"}
key_to_lookup = 'a'
if Fruits.has_key[key_to_lookup]:
  print "Key exists"
else:
  print "Key does not exist"

Output: Key exists

Câu lệnh if-in kiểm tra xem một từ khoá có trong từ điển hay không

Fruits = {'a': "Apple", 'b':"Banana", 'c':"Carrot"}
key_to_lookup = 'a'
if key_to_lookup in Fruits:
  print "Key exists"
else:
  print "Key does not exist"

Output: Key exists

28] Bạn thực hiện kỹ thuật nhớ kết quả tạm [memoization] trong Python thế nào?

Memoization là một kỹ thuật cải thiện tốc độ tính toán của các chương trình sử dụng thuật toán đệ quy [recursive.] Kỹ thuật Memoization lưu các kết quả tính toán của các bước trung gian trong một mảng, và truy xuất chúng trong các bước đệ quy sau, thay vì phải tính lại kết quả của các bước trước đó.

Để minh hoạ, hãy xem đoạn mã rất tốn kém về mặt tính toán sau:

# Fibonacci Numbers
def fib[num]:
    if num == 0:
        return 0
    elif num == 1:
        return 1
    return fib[num - 1] + fib[n - 2]

Ứng dụng kỹ thuật nhớ tạm [memoization] có thể được thực hiện qua việc dùng vật trang trí Python như sau:

import timeit

def memoize_fib[func]:
    cache = {}
    def inner[arg]:
        if arg not in cache:            
            cache[arg] = func[arg]
        return cache[arg]
    return inner


def fib[num]:
    if num == 0:
        return 0
    elif num == 1:
        return 1
    else:
        return fib[num-1] + fib[num-2]

fib = memoize_fib[fib]


print[timeit.timeit['fib[30]', globals=globals[], number=1]]
print[timeit.timeit['fib[30]', globals=globals[], number=1]]
print[timeit.timeit['fib[30]', globals=globals[], number=1]]

Output:

4.9035000301955733e-05
1.374000021314714e-06
1.2790005712304264e-06

29] Bạn xếp thứ tự từ điển trong Python thế nào?

Chúng ta có thể xếp thứ tự từ điển theo 'từ khoá' hoặc theo 'giá trị' dùng hàm sorted[]. Trước hết, chúng ta cần biết cách lấy dữ liệu từ từ điển để truyền vào hàm sorted. Có 3 cách lấy dữ liệu từ từ điển:

  • tu_dien.keys[]: trả về các từ khoá theo thứ tự bất định.
  • tu_dien.values[]: trả về một danh sách các giá trị.
  • tu_dien.items[]: trả về dữ liệu dưới dạng một danh sách các cặp từ khoá - giá trị.

Cú pháp:

Sorted[data[bắt buộc], key[tuỳ chọn], reverse[tuỳ chọn]]

Hàm sorted nhận một tham số bắt buộc và 2 tham số tuỳ chọn.

  • Data [mandatory]: Dữ liệu cần xếp thứ tự. Dữ liệu truyền vào sẽ được lấy từ 1 trong 3 cách ở trên.
  • Key [optional]: Một hàm [hay tiêu chuẩn] chúng ta muốn xếp thứ tự theo. Chẳng hạn, sắp xếp thứ tự các chuỗi theo độ dài của chúng, hoặc theo một hàm tuỳ ý. Hàm này sẽ được áp dụng với từng thành phần trong danh sách và kết quả của chúng sẽ được sử dụng để xếp thứ tự. Nếu tham số này trống, hàm sorted sẽ xếp thứ tự theo giá trị ban đầu của dữ liệu.
  • Reverse [optional]: Tham số thứ ba là True sẽ trả về kết quả theo thứ tự giảm dần [descending.] Để trống sẽ trả về kết quả theo thứ tự tăng dần [ascending]

keys[]

dict = {}
dict['1'] = 'apple'
dict['3'] = 'orange'
dict['2'] = 'pango'

lst = dict.keys[]

# Sorted by key
print["Sorted by key: ", sorted[lst]]

values[]

dict = {}
dict['1'] = 'apple'
dict['3'] = 'orange'
dict['2'] = 'pango'

lst = dict.values[]

#Sorted by value
print["Sorted by value: ", sorted[lst]]

items[]

dict = {}
dict['1'] = 'apple'
dict['3'] = 'orange'
dict['2'] = 'strawberry'

lst = dict.items[]

# Sorted by key
print["Sorted by key: ", sorted[lst, key = lambda x : x[0]]]

#Sorted by value
print["Sorted by value: ", sorted[lst, key = lambda x : x[1]]]

30] Bạn dùng hàm any[] và all[] thế nào và khi nào?

Hàm any[] là gì?

any[] là một hàm nhận một biến khả lặp [iterable] như 1 danh sách [list], 1 danh sách cố định [tuple,] 1 bộ [set,] làm tham số và trả về kết quả True nếu bất kỳ thành phần nào trong biến khả lặp đó có giá trị True, nhưng trả về giá trị False nếu tất cả các thành phần có giá trị False.

Truyền biến khả lặp cho hàm any[] để kiểm tra xem có thành phần nào có giá trị True hay không có thể được thực hiện như sau:

one_truth = [True, False, False]
three_lies = [0, '', None]

print[any[one_truth]]

print[any[three_lies]]

Output:
True
False

Lệnh in đầu tiên trả về kết quả True vì một trong những thành phần của biến one_truth có giá trị True.

Trong khi đó, lệnh in thứ hai trả về kết quả False vì không một thành phần nào trong biến three_lies có giá trị True.

Dùng hàm any[] để kiểm tra một chuỗi dài các điều kiện or

Hàm all[] là gì?

all[] là một hàm Python khác nhận một biến khả lặp và trả về kết quả True nếu tất cả các thành phần trong biến có giá trị True. Nếu bất kỳ thành phần nào trong biến khả lặp có giá trị False, hàm all[] sẽ trả về kết quả False.

all_true = [True, 1, 'a', object[]]
one_true = [True, False, False, 0]
all_false = [None, '', False, 0]

print[all[all_true]]
print[all[one_true]]
print[all[all_false]]

Output:
True
False
False

Câu lệnh đầu tiên trả về kết quả True vì all_true bao gồm tất cả các thành phần có giá trị True.

Truyền biến one_true cho hàm all[] trả về kết quả False vì tham số có 1 chứa 1 hoặc nhiều hơn một giá trị False.

Và sau cùng, truyền biến all_false cho hàm all[], chúng ta nhận được kết quả False vì tham số bao gồm một hoặc nhiều hơn giá trị False.

Dùng hàm all[] khi cần kiểm tra một chuỗi dài các điều kiện and

31] Python Docstring là gì?

Python docstrings là một cách thích hợp để gắn tài liệu thuyết minh mã lập trình với:

  • Đơn nguyên [modules] Python
  • Hàm Python
  • Lớp [Class] Python

Nó là đoạn văn bản nhất định cho đoạn mã gắn liền với nó. Không như những lời bình lập trình [code comments,] docstring mô tả việc mà một hàm thực hiện, không phải cách nó thực hiện.

docstring có thể được truy xuất bằng cách gọi hàm:

  • __doc__ của đối tượng
  • help

32] Missing question?

To be filled out later

33] Hãy giải thích sự khác nhau giữa Bộ tạo [generator] và Bộ lặp [iterator] trong Python.

Bộ lặp [iterator] trong Python đóng vai trò như một kẻ giữ chỗ cho các đối tượng để nó có thể duyệt qua; Bộ tạo [generator] tạo điều kiện dễ dàng cho việc tạo một bộ lặp tuỳ biến. Như vậy, một bộ tạo luôn luôn là một bộ lặp, nhưng không phải lúc nào một bộ lặp cũng là một bộ tạo.

Ngoài những khác biệt về cú pháp, còn có các khác biệt đáng kể sau:

Bộ tạo - GeneratorBộ lặp - Iterator
Được thực thi thông qua 1 hàm. Được thực thi thông qua 1 lớp [class.]
Sử dụng từ khoá yield. Không dùng từ khoá yield.
Việc sử dụng tạo ra mã cô đọng. Việc sử dụng tạo ra mã nguồn kém cô đọng hơn [so với hàm tạo.]
Tất cả các biến địa phương trước câu lệnh yield đều được lưu trữ. Không có biến địa phương nào được sử dụng.

Ví dụ về một Bộ lặp

# Hàm tạo tất cả các số không âm
# nhỏ hơn hoặc bằng một số không âm cho trước.
class UpTo:
    # gán cho tham số giá trị mặc định 0 
    def __init__[self, max = 0]:
        self.max = max
    def __iter__[self]:
        self.n = 0
        return self
    def __next__[self]:
        # Hàm next sẽ tạo một ngoại lệ 
        # khi gặp phải điều kiện kết thúc.
        if self.n > self.max:
            raise StopIteration
        else:
            result = self.n
            self.n += 1
            return result
for number in UpTo[5]:
    print[number]

Output:
0
1
2
3
4
5

Ví dụ về Bộ tạo

# Hàm tạo tất cả các số nguyên không âm 
# nhỏ hơn hoặc bằng một số nguyên không âm cho trước
def upto[n]:
  for i in range[n+1]:
    # Câu lệnh yield là điều làm cho đoạn mã này trở thành 1 hàm
    # một bộ tạo 
    yield i
for number in upto[5]:
  print[number]

Output:
0
1
2
3
4
5

34] defaultdict trong Python là gì?

Từ điển Python, dict, chứa các từ và nghĩa như các cặp từ khoá - giá trị của bất kỳ kiểu dữ liệu gì. defaultdict là một loại dữ liệu con của lớp dict dựng sẵn trong Python.

defaultdict khác chỗ nào?

defaultdict là kiểu dữ liệu con của lớp dict. Sự quan trọng của nó nằm ở chỗ nó cho phép mỗi từ khoá mới có một giá trị mặc định dựa vào kiểu từ điển được tạo.

Một defaultdict có thể được tạo bằng cách khai báo, một tham số có có thể có 3 giá trị; list, set, hoặc int. Tuỳ theo loại dữ liệu khai báo, từ điển sẽ được tạo và khi bất kỳ từ khoá nào chưa có trong default dict sẽ được thêm vào hoặc truy xuất. Nó sẽ được gán một giá trị mặc định thay vì báo lỗi KeyError.

Ví dụ

Đoạn mã nhỏ dưới đây minh hoạ cách khởi tạo một từ điển đơn giản và khi truy xuất từ điển với một từ khoá không tồn tại, nó báo lỗi thế nào.

dict_demo = dict[]
print[dict_demo[3]]

Đoạn mã nhỏ dưới đây minh hoạ một biến defaultdict và hãy xem chuyện gì xảy ra.

from collections import defaultdict

defaultdict_demo = defaultdict[int]
print[defaultdict_demo[3]]

Output: 0

Trong trường hợp này, chúng ta đã truyền int làm tham số quy định kiểu dữ liệu cho defaultdict. Do đó, khi truy xuất một từ khoá chưa tồn tại, từ khoá đó sẽ được thêm vào và được gán giá trị 0.

Ghi chú: Bạn cũng có thể truyền set hoặc list làm tham số cho defaultdict.

35] Đơn nguyên [modules] Python là gì?

Một đơn nguyên Python là tập tin Python bao gồm một bộ các hàm và biến để sử dụng trong một ứng dụng. Các biến có thể thuộc bất kỳ kiểu dữ liệu nào. Đơn nguyên có thể thuộc 1 trong 2 dạng:

  1. Dựng sẵn [Built in]

  2. Do người dùng định nghĩa [User-defined]

Lợi ích của đơn nguyên trong Python

Có một số lợi ích chủ chốt trong việc tạo và sử dụng đơn nguyên trong Python:

  • Mã nguồn có cấu trúc

    • Mã nguồn được tổ chức mạch lạc bằng cách nhóm chúng vào một tập tin Python. Việc này giúp cho quá trình phát triển trở nên dễ hơn, và ít bị lỗi hơn.
    • Dễ hiểu và sử dụng mã nguồn hơn.
  • Tái sử dụng

    • Các tính năng định nghĩa trong một đơn nguyên có thể dễ dàng được sử dụng lại ở những phần khác của ứng dụng. Qua đó, chúng ta không phải viết lại các đoạn mã nguồn đã viết ở chỗ khác.

CÁC CÂU HỎI VỀ KỸ THUẬT LẬP TRÌNH

Trong phần này chúng ta sẽ điểm qua các câu hỏi phỏng vấn về lập trình thông dụng liên quan đến danh sách [list], danh sách liên kết [linked list], biểu đồ [graph,] cây [trees,] đa luồng, thực thi đồng thời và hơn thế nữa. Hãy cũng bắt đầu khám phá nào.

36] Hãy đảo ngược một chuỗi

Hãy đảo ngược chuỗi "Python" dùng phương pháp cắt lát [slicing.] Để đảo ngược một chuỗi, chúng ta tạo ra một lát cắt bắt đầu với độ dài chuỗi, và kết thúc tại ví trí index 0.

Để đảo ngược một chuỗi bằng phương pháp cắt lát, chúng ta có thể dùng một trong các cách sau:To reverse a string using slicing, write:


stringname
[stringlength::-1] # method 1

Hoặc viết mà bỏ trống tham số độ dài chuỗi như sau:

stringname[::-1] # method2

Câu lệnh cắt lát có nghĩa bắt đầu tại vị trí độ dài chuỗi, và kết thúc tại vị trí index 0, mỗi bước dịch chuyển một bước có giá trị -1 [một bước lùi về phía sau.]

str="Python" # initial string
stringlength=len[str] # calculate length of the list
slicedString=str[stringlength::-1] # slicing 
print [slicedString] # print the reversed string

Output: nohtyP

Đây là một cách để đảo ngược chuỗi. Các cách khác bao gồm phương pháp lặp và dùng lệnh join để nối chuỗi.

37] Kiểm tra xem một chuỗi có chứa một chuỗi con khác hay không

Có vài cách để thực hiện việc này. Trong phần này, chúng ta dùng hàm find. 

Hàm find kiểm tra xem chuỗi có chứa một chuỗi con hay không. Nếu có, nó trả về vị trí index đầu tiên của chuỗi con trong chuỗi cha. Nếu không, nó trả về -1.

Cú pháp chung là:

string.find[substring]

Ví dụ: 

a_string="Python Programming" 
substring1="Programming" 
substring2="Language" 
print["Check if "+a_string+" contains "+substring1+":"]
print[a_string.find[substring1]] 
print["Check if "+a_string+" contains "+substring2+":"]
print[a_string.find[substring2]]

Output:
Check if Python Programming contains Programming: 7
Check if Python Programming contains Language: -1

Các phương pháp khác là dùng toán tử in hoặc hàm count.

38] Hãy thực hiện thuật toán Tìm kiếm Độ Rộng Trước Hết [Breadth First Search - BFS] bằng Python

Hãy xem xét sơ đồ dưới đây qua đoạn mã bên dưới:

graph = {
  'A' : ['B','C'],
  'B' : ['D', 'E'],
  'C' : ['F'],
  'D' : [],
  'E' : ['F'],
  'F' : []
}

visited = [] # List to keep track of visited nodes.
queue = []     #Initialize a queue

def bfs[visited, graph, node]:
  visited.append[node]
  queue.append[node]

  while queue:
    s = queue.pop[0] 
    print [s, end = " "] 

    for neighbour in graph[s]:
      if neighbour not in visited:
        visited.append[neighbour]
        queue.append[neighbour]

# Driver Code
bfs[visited, graph, 'A']

Output: A B C D E F

Giải thích

Dòng 3-10: Sơ đồ trên được đại diện bằng một danh sách các điểm liền kề. Một cách dễ dàng để thực hiện việc này là dùng cấu trúc dữ liệu kiểu từ điển, trong đó mỗi đỉnh chứa một danh sách các điểm liền kề.

Dòng 12: visited là một danh sách để lưu trữ các nốt đã đi qua.

Dòng 13: queue là một danh sách để lưu trữ các nốt hiện đang trong hàng đợi.

Dòng 29: Tham số của hàm bfs là danh sách visited, sơ đồ dưới dạng 1 từ điển, và nốt xuất phát A.

Dòng 15-26: bfs theo thuật toán mô tả ở trên:

  1. Nó kiểm tra và thêm nốt xuất phát vào danh sách visited và danh sách queue.
  2. Kế đến, trong khi biến queue còn chứa các phần tử, nó tiếp tục lấy các phần tử đó ra khỏi hàng đợi, thêm nốt lân cận của nốt đó vào hàng đợi nếu nó chưa được đi qua, trong danh sách unvisited, và đánh dấu chúng là đã đi qua.
  3. Việc này tiếp diễn cho đến khi hàng đợi trống rỗng.

Độ phức tạp về thời gian [Time Complexity]

Vì tất cả các nốt và đỉnh đều được đi qua, độ phức tạp về thời gian cho thuật toán BFS trên 1 sơ đồ là O[V + E] trong đó V là số đỉnh, và E là số cạnh.

Question 39: Hãy thực hiện thuật toán Độ Sâu Trước Hết [Depth First Search - DBS] bằng Python

Hãy xem sơ đồ này, và thực thi đoạn mã nguồn dưới đây:

# Using a Python dictionary to act as an adjacency list
graph = {
    'A' : ['B','C'],
    'B' : ['D', 'E'],
    'C' : ['F'],
    'D' : [],
    'E' : ['F'],
    'F' : []
}

visited = set[] # Set to keep track of visited nodes.

def dfs[visited, graph, node]:
    if node not in visited:
        print [node]
        visited.add[node]
        for neighbour in graph[node]:
            dfs[visited, graph, neighbour]

# Driver Code
dfs[visited, graph, 'A']

Output: A B D E F C

Explanation

Dòng 2-9: Sơ đồ trên được đại diện bằng một danh sách các điểm liền kề. Một cách dễ dàng để thực hiện việc này là dùng cấu trúc dữ liệu kiểu từ điển, trong đó mỗi đỉnh chứa một danh sách các điểm liền kề.

Dòng 11: visited là một bộ [set] được dùng để lưu các nốt đã đi qua.

Dòng 21: Hàm dfs được gọi và truyền tham số là bộ chứa các nốt đã đi qua, sơ đồ ở dạng từ điển, và nốt xuất phát A.

Dòng 13-18: dfs theo thuật toán được mô tả dưới đây:

  1. Trước hết, thuật toán kiểm tra xem nốt hiện hành đã được đi qua hay chưa. Nếu có, nó sẽ được thêm vào bộ visited.
  2. Kế đến, cho mỗi nốt lân cận của nốt hiện hành, hàm dfs lại được gọi.
  3. Tình huống cơ sở được kích hoạt khi tất cả các nốt đã được đi qua, sau đó hàm trả về kết quả cuối cùng.

Độ phức tạp về thời gian

Vì tất cả các nốt và các cạnh đều được viếng thăm, độ phức tạp về thời gian bình quân để thực thi thuật toán DFS trên 1 sơ đồ là O[V + E], trong đó V là số đỉnh và E là số cạnh. Trong trường hợp DFS trên 1 cây, độ phức tạp về thời gian là O[V], trong đó V là số nốt.

40] Hãy thực thi ký tự đại diện [wildcards] bằng Python

Trong Python, bạn có thể thực thi ký tự đại diện bằng thư viện regex [regular expressions - biểu thức thông thường.]

Ký tự chấm . được dùng thay cho dấu chấm hỏi ?. Do đó, để tìm tất cả các từ phù hợp với mẫu màu, mã có thể trông như sau:

# Regular expression library
import re

# Add or remove the words in this list to vary the results
wordlist = ["color", "colour", "work", "working",
            "fox", "worker", "working"]

for word in wordlist:
        # The . symbol is used in place of ? symbol
        if re.search['col.r', word] : 
                print [word]

Output: color

 41] Hãy thực thi thuật toán sắp xếp sáp nhập [merge sort] bằng Python

Đây là mã chương trình cho thuật toán sắp xếp sáp nhập:

def mergeSort[myList]:
    if len[myList] > 1:
        mid = len[myList] // 2
        left = myList[:mid]
        right = myList[mid:]

        # Recursive call on each half
        mergeSort[left]
        mergeSort[right]

        # Two iterators for traversing the two halves
        i = 0
        j = 0

        # Iterator for the main list
        k = 0

        while i 

Chủ Đề