Thứ tư, 19/08/2020 | 00:00 GMT+7

Cách sử dụng module bộ sưu tập trong Python 3

Python 3 có một số cấu trúc dữ liệu được tích hợp sẵn, bao gồm bộ dữ liệu, từ điển và danh sách. Cấu trúc dữ liệu cung cấp cho ta cách tổ chức và lưu trữ dữ liệu. Mô-đun collections giúp ta điền và thao tác cấu trúc dữ liệu một cách hiệu quả.

Trong hướng dẫn này, ta sẽ đi qua ba lớp trong mô-đun collections để giúp bạn làm việc với bộ dữ liệu, từ điển và danh sách. Ta sẽ sử dụng namedtuples để tạo ra bộ dữ liệu với các lĩnh vực được đặt tên, defaultdict đến thông tin group chính xác trong từ điển, và deque để thêm hiệu quả các yếu tố để hai bên của một danh sách giống như đối tượng.

Đối với hướng dẫn này, ta sẽ làm việc chủ yếu với một kho cá mà ta cần sửa đổi khi cá được thêm vào hoặc xóa khỏi một bể cá hư cấu.

Yêu cầu

Để tận dụng tối đa hướng dẫn này, bạn nên làm quen với các kiểu dữ liệu tuple, từ điển và danh sách, cả cú pháp của chúng và cách truy xuất dữ liệu từ chúng. Bạn có thể xem lại các hướng dẫn này để biết thông tin cơ bản cần thiết:

Thêm các trường được đặt tên vào Tuples

Bộ giá trị Python là một chuỗi các phần tử có thứ tự bất biến hoặc không thể thay đổi. Tuples thường được sử dụng để đại diện cho dữ liệu dạng cột; ví dụ: các dòng từ file CSV hoặc các hàng từ database SQL. Một bể cá có thể theo dõi số lượng cá tồn kho của nó dưới dạng một loạt các bộ giá trị.

Một cá thể tuple:

("Sammy", "shark", "tank-a") 

Tuple này bao gồm ba phần tử chuỗi.

Mặc dù hữu ích theo một số cách, nhưng tuple này không chỉ ra rõ ràng những gì mỗi trường của nó đại diện. Trên thực tế, phần tử 0 là tên, phần tử 1 là loài và phần tử 2 là bể chứa.

Giải thích về cánh đồng cá

Tên loài xe tăng
Sammy cá mập tank-a

Bảng này làm rõ rằng mỗi yếu tố trong số ba yếu tố của tuple đều có ý nghĩa rõ ràng.

namedtuple từ module collections cho phép bạn thêm tên rõ ràng vào từng phần tử của một bộ tuple để làm rõ những ý nghĩa này trong chương trình Python của bạn.

Hãy sử dụng namedtuple để tạo một lớp đặt tên rõ ràng cho từng phần tử của bộ cá:

from collections import namedtuple  Fish = namedtuple("Fish", ["name", "species", "tank"]) 

from collections import namedtuple cấp cho chương trình Python của bạn quyền truy cập vào chức năng nhà máy có namedtuple . Lệnh gọi namedtuple() trả về một lớp được liên kết với tên Fish . Hàm namedtuple() có hai đối số: tên mong muốn của lớp mới là "Fish" và danh sách các phần tử được đặt tên ["name", "species", "tank"] .

Ta có thể sử dụng lớp Fish để đại diện cho bộ cá từ trước đó:

sammy = Fish("Sammy", "shark", "tank-a")  print(sammy) 

Nếu ta chạy mã này, ta sẽ thấy kết quả sau:

Output
Fish(name='Sammy', species='shark', tank='tank-a')

sammy được khởi tạo bằng cách sử dụng lớp Fish . sammy là một bộ ba với ba nguyên tố được đặt tên rõ ràng.

sammy có thể được truy cập bằng tên của chúng hoặc bằng index tuple truyền thống:

print(sammy.species) print(sammy[1]) 

Nếu ta chạy hai print , ta sẽ thấy kết quả sau:

Output
shark shark

Truy cập .species trả về giá trị giống như truy cập phần tử thứ hai của sammy bằng cách sử dụng [1] .

Sử dụng namedtuple từ module collections làm cho chương trình của bạn dễ đọc hơn trong khi vẫn duy trì các thuộc tính quan trọng của một bộ tuple (chúng không thể thay đổi và có thứ tự).

Ngoài ra, namedtuple nhà máy được đặt tên là bổ sung một số phương thức bổ sung cho các version của Fish .

Sử dụng ._asdict() để chuyển đổi một version thành từ điển:

print(sammy._asdict()) 

Nếu ta chạy print , bạn sẽ thấy kết quả như sau:

Output
{'name': 'Sammy', 'species': 'shark', 'tank': 'tank-a'}

Gọi .asdict() trên sammy trả về một từ điển ánh xạ từng tên trường trong số ba tên trường với các giá trị tương ứng của chúng.

Các version Python cũ hơn 3.8 có thể xuất dòng này hơi khác một chút. Ví dụ: bạn có thể xem một OrderedDict thay vì từ điển đơn giản được hiển thị ở đây.

Lưu ý: Trong Python, các phương thức có dấu gạch dưới ở đầu thường được coi là "riêng tư". Phương pháp bổ sung được cung cấp bởi namedtuple (như _asdict() , ._make() ,. _replace() , vv), tuy nhiên, là công khai .

Thu thập dữ liệu trong từ điển

Việc thu thập dữ liệu trong từ điển Python thường rất hữu ích. defaultdict từ module collections có thể giúp ta thu thập thông tin trong từ điển một cách nhanh chóng và ngắn gọn.

defaultdict không bao giờ làm tăng KeyError . Nếu một quan trọng là không có mặt, defaultdict chỉ chèn và trả về một giá trị giữ chỗ thay vì:

from collections import defaultdict  my_defaultdict = defaultdict(list)  print(my_defaultdict["missing"]) 

Nếu ta chạy mã này, ta sẽ thấy kết quả như sau:

Output
[]

defaultdict chèn và trả về giá trị giữ chỗ thay vì ném KeyError . Trong trường hợp này, ta đã chỉ định giá trị trình giữ chỗ dưới dạng danh sách.

Ngược lại, các từ điển thông thường sẽ tạo ra KeyError khi thiếu các khóa:

my_regular_dict = {}  my_regular_dict["missing"] 

Nếu ta chạy mã này, ta sẽ thấy kết quả như sau:

Output
Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'missing'

Từ điển thông thường my_regular_dict gây ra KeyError khi ta cố gắng truy cập vào một khóa không có.

defaultdict hoạt động khác với một từ điển thông thường. Thay vì tăng KeyError trên một khóa bị thiếu, defaultdict gọi giá trị giữ chỗ không có đối số để tạo một đối tượng mới. Trong trường hợp này, list() để tạo một danh sách trống.

Tiếp tục với ví dụ về bể cá giả tưởng của ta , giả sử ta có một danh sách các bộ cá đại diện cho hàng tồn kho của bể cá:

fish_inventory = [     ("Sammy", "shark", "tank-a"),     ("Jamie", "cuttlefish", "tank-b"),     ("Mary", "squid", "tank-a"), ] 

Ba con cá tồn tại trong bể - tên, loài và bể chứa của chúng được ghi chú trong ba bộ này.

Mục tiêu của ta là sắp xếp hàng tồn kho của ta theo bể — ta muốn biết danh sách cá có trong mỗi bể. Nói cách khác, ta muốn có một từ điển ánh xạ "tank-a" thành ["Sammy", "Mary"]"tank-b" thành ["Jamie"] .

Ta có thể sử dụng defaultdict để group cá theo bể:

from collections import defaultdict  fish_inventory = [     ("Sammy", "shark", "tank-a"),     ("Jamie", "cuttlefish", "tank-b"),     ("Mary", "squid", "tank-a"), ] fish_names_by_tank = defaultdict(list) for name, species, tank in fish_inventory:     fish_names_by_tank[tank].append(name)  print(fish_names_by_tank) 

Chạy mã này, ta sẽ thấy kết quả sau:

Output
defaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})

fish_names_by_tank được khai báo là một defaultdict rằng giá trị mặc định để chèn list() thay vì ném một KeyError . Vì điều này đảm bảo mọi khóa trong fish_names_by_tank sẽ trỏ đến một list , ta có thể tự do gọi .append() để thêm tên vào danh sách của mỗi bể.

defaultdict giúp bạn ở đây vì nó làm giảm nguy cơ xảy ra các KeyErrors không mong muốn. Giảm các KeyErrors không mong muốn nghĩa là chương trình của bạn có thể được viết rõ ràng hơn và ít dòng hơn. Cụ thể hơn, thành ngữ defaultdict cho phép bạn tránh tạo danh sách trống cho mọi bể theo cách thủ công.

Nếu không có defaultdict thì for cơ thể loop có thể trông như thế này:

Thêm ví dụ chi tiết mà không có phán đoán mặc định
...  fish_names_by_tank = {} for name, species, tank in fish_inventory:     if tank not in fish_names_by_tank:       fish_names_by_tank[tank] = []     fish_names_by_tank[tank].append(name) 

Sử dụng chỉ một từ điển thông thường (thay vì một defaultdict ) nghĩa là for thân vòng lặp luôn luôn phải kiểm tra sự tồn tại của cho tank trong fish_names_by_tank . Chỉ sau khi ta xác minh tank đã có trong fish_names_by_tank hoặc vừa được khởi tạo bằng [] , ta mới có thể thêm tên cá vào.

defaultdict có thể giúp cắt giảm mã soạn sẵn khi điền từ điển vì nó không bao giờ gây ra KeyError .

Sử dụng deque để thêm hiệu quả các phần tử vào mỗi bên của bộ sưu tập

Danh sách Python là một chuỗi các phần tử có thứ tự có thể thay đổi hoặc có thể thay đổi. Python có thể thêm vào danh sách trong thời gian không đổi (độ dài của danh sách không ảnh hưởng đến thời gian cần thêm), nhưng việc chèn vào đầu danh sách có thể chậm hơn — thời gian cần tăng khi danh sách lớn hơn.

Theo ký hiệu Big O , việc thêm vào một danh sách là một phép toán O(1) thời gian không đổi. Ngược lại, việc chèn vào đầu danh sách sẽ chậm hơn với hiệu suất O(n) .

Lưu ý: Các kỹ sư phần mềm thường đo lường hiệu suất của các thủ tục bằng cách sử dụng ký hiệu “Big O”. Khi kích thước của một đầu vào không ảnh hưởng đến thời gian thực hiện một thủ tục, nó được cho là chạy trong thời gian không đổi hoặc O(1) (“O lớn của 1”). Như bạn đã học ở trên, Python có thể thêm vào danh sách với hiệu suất thời gian không đổi, còn gọi là O(1) .

Đôi khi, kích thước của đầu vào ảnh hưởng trực tiếp đến lượng thời gian cần để chạy một thủ tục. Ví dụ: việc chèn vào đầu danh sách Python chạy càng chậm khi càng có nhiều phần tử trong danh sách. Ký hiệu Big O sử dụng chữ cái n để biểu thị kích thước của đầu vào. Điều này nghĩa là việc thêm các mục vào đầu danh sách Python chạy trong “thời gian tuyến tính” hoặc O(n) (“O lớn của n”).

Nói chung, thủ tục O(1) nhanh hơn thủ tục O(n) .

Ta có thể chèn vào đầu danh sách Python:

favorite_fish_list = ["Sammy", "Jamie", "Mary"]  # O(n) performance favorite_fish_list.insert(0, "Alice")  print(favorite_fish_list) 

Nếu ta chạy như sau, ta sẽ thấy kết quả như sau:

Output
['Alice', 'Sammy', 'Jamie', 'Mary']

Phương thức .insert(index, object) trong danh sách cho phép ta chèn "Alice" vào đầu favorite_fish_list . Tuy nhiên, đáng chú ý, việc chèn vào đầu danh sách có hiệu suất O(n) . Khi độ dài của favorite_fish_list càng tăng, thời gian để chèn một con cá vào đầu danh sách sẽ tăng theo tỷ lệ thuận và ngày càng lâu hơn.

deque (phát âm là “boong”) từ module collections là một đối tượng giống như danh sách cho phép ta chèn các mục vào đầu hoặc cuối của một chuỗi với hiệu suất thời gian không đổi ( O(1) ).

Chèn một mục vào đầu deque :

from collections import deque  favorite_fish_deque = deque(["Sammy", "Jamie", "Mary"])  # O(1) performance favorite_fish_deque.appendleft("Alice")  print(favorite_fish_deque) 

Chạy mã này, ta sẽ thấy kết quả sau:

Output
deque(['Alice', 'Sammy', 'Jamie', 'Mary'])

Ta có thể khởi tạo deque bằng cách sử dụng một tập hợp các phần tử có sẵn, trong trường hợp này là danh sách ba tên cá yêu thích. Gọi favorite_fish_deque 's appendleft phương pháp cho phép ta chèn một mục vào đầu bộ sưu tập của ta với O(1) hiệu suất. O(1) hiệu suất nghĩa là thời gian cần thêm một mục vào đầu favorite_fish_deque sẽ không tăng lên ngay cả khi favorite_fish_deque có hàng nghìn hoặc hàng triệu phần tử.

Lưu ý: Mặc dù deque thêm các mục vào đầu một chuỗi hiệu quả hơn một danh sách, nhưng deque không thực hiện tất cả các hoạt động của nó hiệu quả hơn một danh sách. Ví dụ: truy cập một mục ngẫu nhiên trong deque có hiệu suất O(n) , nhưng truy cập một mục ngẫu nhiên trong danh sách có hiệu suất O(1) . Sử dụng deque khi cần nhanh chóng chèn hoặc xóa các phần tử từ một trong hai bên của bộ sưu tập. So sánh đầy đủ về hiệu suất thời gian có sẵn trên wiki của Python .

Kết luận

Mô-đun collections là một phần mạnh mẽ của thư viện chuẩn Python cho phép bạn làm việc với dữ liệu một cách ngắn gọn và hiệu quả. Hướng dẫn này bao gồm ba trong số các lớp được cung cấp bởi module collections bao gồm namedtuple , defaultdictdeque .

Từ đây, bạn có thể sử dụng tài liệu của module collection để tìm hiểu thêm về các lớp và tiện ích có sẵn khác. Để tìm hiểu thêm về Python nói chung, bạn có thể đọc loạt bài hướng dẫn Cách viết mã trong Python 3 của ta .


Tags:

Các tin liên quan

Cách chuyển đổi kiểu dữ liệu trong Python 3
2020-08-07
Cách sử dụng hàm bản đồ Python
2020-08-03
Cách sử dụng quy trình con để chạy các chương trình bên ngoài trong Python 3
2020-07-30
Làm thế nào để đánh lừa một mạng neural trong Python 3
2020-07-30
Cách sử dụng hàm bộ lọc Python
2020-07-24
Cách sử dụng module pathlib để thao tác đường dẫn hệ thống tệp trong Python 3
2020-07-15
Cách tạo Slackbot bằng Python trên Ubuntu 20.04
2020-06-30
Cách sử dụng ThreadPoolExecutor trong Python 3
2020-06-23
Cách sử dụng module sqlite3 trong Python 3
2020-06-02
Cách thiết lập notebook Jupyter với Python 3 trên Ubuntu 20.04 và Kết nối qua Đường hầm SSH
2020-05-19