Đây là phần thứ ba của loạt bài viết về hệ thống chú thích kiểu trong Python, viết tắt là type hints. Phần thứ hai thảo luận về một tập hợp các ví dụ dành cho người mới bắt đầu và nêu bật những lợi ích của việc sử dụng gợi ý loại
Loạt bài viết này nhằm vào những người mới nhập gợi ý và muốn giúp bạn bắt đầu
Trong phần thứ ba này, tôi sẽ tiếp tục thảo luận về các ví dụ nâng cao hơn một chút để hiểu sâu hơn về kiến thức của bạn về gợi ý loại. Mỗi ví dụ sẽ đề cập đến một chủ đề nhất định và xem xét các gợi ý về loại từ một góc độ hơi khác. Hầu hết các ví dụ sẽ kết thúc bằng mẹo hoặc phương pháp hay nhất để giúp bạn cải thiện kỹ năng gợi ý loại của mình
Biến rỗng có ý nghĩa
Một biến có thể được khởi tạo dưới dạng một vùng chứa trống hoặc
countries: dict[str, tuple[str, int]] = {}
0 vì nội dung thực tế của nó được chỉ định sau trong mã. Ví dụ, đây có thể là trường hợp của biến giữ giá trị trả về của hàm. Thông thường, biến này được khởi tạo ngay ở phần đầu của thân hàm và giá trị của nó được gán nhiều lần trong toàn bộ mã. Khi giá trị trả về của hàm được chú thích kiểu chính xác, điều này sẽ không có vấn đề gì vì bạn đã biết kiểu của giá trị trả về nhờ gợi ý kiểu trong tiêu đề hàm [bạn có nhớ chú thích countries: dict[str, tuple[str, int]] = {}
1 không?]. Tuy nhiên, nếu kiểu trả về không được chú thích chính xác, bạn sẽ không biết chắc. Tương tự, nếu biến được đề cập không phải là giá trị trả về mà là một biến cục bộ nào đó không được ghi lại trong chuỗi tài liệu của hàm cũng như thông qua nhận xét nội tuyến, thì bạn khó có thể biết nó sẽ chứa gì. Giả sử bạn bắt gặp phép gán biến sau đây ở đâu đó trong đoạn mãcountries = {}
Tất cả những gì bạn biết về biến
countries: dict[str, tuple[str, int]] = {}
2 là nó là một từ điển vì nó được khởi tạo như vậy. Nhưng bạn không biết cấu trúc của từ điển này trông như thế nào. Mục đích dự định của biến này là gì? . Tuy nhiên, câu hỏi thú vị là mối quan hệ này trông như thế nào. Các khóa là gì và các giá trị là gì, từ điển này phức tạp đến mức nào? Bây giờ, hãy để chúng tôi giúp người đọc bằng cách chú thích biến này
countries: dict[str, tuple[str, int]] = {}
Điều này có làm thay đổi hiểu biết của bạn về biến không? . Với gợi ý loại thích hợp, bạn biết chính xác những gì mong đợi từ từ điển này. Rằng các khóa của nó sẽ là các chuỗi [có thể là tên của các quốc gia] và các giá trị của nó sẽ là các bộ hai phần tử, phần tử đầu tiên là một chuỗi [có thể là thủ đô của quốc gia] và phần tử thứ hai là một số nguyên [có thể là dân số của quốc gia]. Tất nhiên, bạn sẽ không biết các kiểu đại diện cho điều gì khi chỉ đọc các gợi ý về kiểu, nhưng bạn sẽ hiểu rõ hơn về cách từ điển này được cấu trúc và cách nó sẽ được sử dụng sau này trong mã. Và thực tế là các giá trị của nó là các bộ cho bạn biết điều gì đó về các giá trị. chúng được mong đợi là một cặp hai yếu tố cố định [bất biến], do đó, đó phải là dữ liệu hợp lệ cho tất cả các quốc gia [như thủ đô và dân số]. Trong ví dụ này, gợi ý loại giúp giảm sự mơ hồ và không chắc chắn về cấu trúc và cách sử dụng biến
Đây là ví dụ đầu tiên mà chú thích kiểu được thực hiện cho một biến nằm ngoài định nghĩa hàm. Trên thực tế, bạn có thể gõ chú thích mọi thứ bằng Python, không chỉ các tham số chức năng. Tuy nhiên, khi bạn tương tác với các hàm hoặc phương thức thường xuyên hơn so với cách triển khai thực tế của chúng, gợi ý loại có giá trị nhất ở những nơi thường được truy cập
Tuy nhiên, hãy để tôi cung cấp cho bạn một số ví dụ khác về các chú thích loại biến bên ngoài các hàm có thể hữu ích
class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
Ở đây, gợi ý loại rất hữu ích vì giá trị thực tế cho
countries: dict[str, tuple[str, int]] = {}
3 có thể được đặt muộn hơn nhiều, trong phương thức countries: dict[str, tuple[str, int]] = {}
4 hoặc thậm chí muộn hơn. Trước gợi ý loại, chúng tôi thường đặt gợi ý loại trong nhận xét, nhưng vì các nhận xét bị bộ kiểm tra loại tĩnh bỏ qua và không được chuẩn hóa, tốt hơn hết là sử dụng gợi ý loại vì đó là mục đích của nóGợi ý loại thậm chí còn hữu ích hơn khi kết hợp với lớp dữ liệu
________số 8
Mặc dù Python không ngăn cản bạn khởi tạo một phiên bản
countries: dict[str, tuple[str, int]] = {}
5 với các loại sai, do đó, countries: dict[str, tuple[str, int]] = {}
6 sẽ không gây ra lỗi, trình trang trí countries: dict[str, tuple[str, int]] = {}
7 sẽ sử dụng thông tin gợi ý loại cho một số phép thuật bên trong như tự động tạo phương thức countries: dict[str, tuple[str, int]] = {}
8 với chú thích loại phù hợp cho . Bạn thậm chí có thể sử dụng countries: dict[str, tuple[str, int]] = {}
9 để diễn đạt rằng một biến là biến lớp thay vì biến thể hiện. Trong trường hợp này, một lần nữa, Python không ngăn bạn truy cập biến này thông qua một thể hiện [class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
0 thay vì class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
1] nhưng nó sẽ ngăn không cho biến lớp được thêm vào danh sách tham số của phương thức class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
2countries: dict[str, tuple[str, int]] = {}
7Bí danh loại có ý nghĩa
Đến bây giờ, bạn đã thấy khá nhiều gợi ý về loại và tôi phải thừa nhận rằng khả năng đọc không phải lúc nào cũng được cải thiện khi các loại trở nên phức tạp hơn và do đó, các gợi ý về loại dài hơn là cần thiết, nhất là với các loại lồng nhau và nhiều loại. Vì vậy, thay vì chấp nhận gợi ý loại dài hơn bao giờ hết, bạn có thể cải thiện cả khả năng đọc và ý nghĩa rất nhiều bằng cách giới thiệu tên loại của riêng bạn. Điều này là có thể bởi vì các chú thích kiểu chỉ là các đối tượng Python và có thể được gán cho các biến
Nhìn vào ví dụ sau
countries: dict[str, tuple[str, int]] = {}
8Bằng cách giới thiệu các tên nói
class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
3, class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
4 và class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
5 như các loại mới, gợi ý loại không chỉ cung cấp thông tin về các tham số của chúng mà còn thông báo về miền và xây dựng một ngôn ngữ miền nhất quán. Bây giờ bạn có thể nghĩ về hàm class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
6 không còn là một hàm lấy hai danh sách số float và tạo ra một số duy nhất mà là một phép toán giữa hai vectơ tạo ra một số vô hướng. Và class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
7 của một vectơ lại là một đại lượng vô hướng, biểu thị độ dài của nó. Và bởi vì class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
8 của ma trận có hai phần tử nên ma trận phải là một đối tượng nào đó có hai chiềuBằng cách gán các tên mới và có ý nghĩa cho các định nghĩa kiểu hiện có, bạn có thể tăng thêm khả năng đọc và hiểu mã của mình và truyền đạt ý định của bạn tới người đọc tốt hơn
Cập nhật cho Python 3. 10. Kể từ Python 3. 10 [và nhờ có PEP 613], có thể sử dụng
class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
9 mới từ mô-đun from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
0 để khai báo rõ ràng một bí danh loại. Điều này giải quyết một số vấn đề với cú pháp cũ, nói đúng ra, là một biểu thức toàn cục và chỉ được hiểu là bí danh kiểu nếu được sử dụng đúng cách. Với class MyClass:
# You can optionally declare instance variables in the class body
attr: int
...
9 , bạn có thể viết lại ví dụ thànhcountries = {}
8Điều này yêu cầu nhập bổ sung, điều này có thể giống như một gánh nặng, nhưng nó làm tăng khả năng đọc, nêu rõ ý định của bạn và cải thiện khả năng của trình kiểm tra kiểu tĩnh
Tránh nhầm lẫn về nhiều loại
Nếu bạn cần cho phép nhiều loại, bạn có thể sử dụng
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
2, e. g. from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
3 cho người đọc biết rằng một biến có thể là một chuỗi hoặc một số nguyên. Điều này có thể hữu ích bất cứ khi nào không thể yêu cầu một loại hoặc bạn muốn cung cấp một số tiện lợi cho người dùng chức năng hoặc lớp của bạn. Tuy nhiên, tốt nhất là tránh nhiều loại để rõ ràng và sử dụng cơ chế khác như công văn đơn lẻ hoặc loại biến tổng quát hơn như từ điểnNhìn vào ví dụ sau
countries: dict[str, tuple[str, int]] = {}
1Có vẻ như các tác giả của hàm
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
4 muốn ít nghiêm ngặt hơn về loại tham số from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
5. Thay vì luôn yêu cầu nó phải là một danh sách, họ cũng thấy trước khả năng hoặc sự cần thiết phải cho phép một chuỗi đơn. Ta có thể hiểu ngay kiểu hint này như sau. Khi chỉ có một địa chỉ duy nhất bạn muốn gửi email đến, bạn có thể chuyển địa chỉ này cho chức năng, nhưng nếu bạn có nhiều địa chỉ, bạn cũng có thể chuyển toàn bộ danh sách và chức năng sẽ xử lý nó. Thật tuyệt khi thông tin này được mã hóa trực tiếp trong một gợi ý loại, phải không?Nếu bạn thắc mắc trình soạn thảo yêu thích của mình xử lý trường hợp có nhiều loại như thế nào, thì ít nhất là đối với VSC, tôi có thể nói với bạn rằng nó rất thông minh và gợi ý tất cả các phương thức cho cả chuỗi và danh sách. Không phải những gì tôi đã mong đợi nhưng chắc chắn đánh giá cao
Tránh nhầm lẫn về Không
countries: dict[str, tuple[str, int]] = {}
0 là một trong những người bạn tốt nhất của chúng tôi và đồng thời có thể thực sự khó chịu. Cách tốt nhất là khởi tạo các tham số tùy chọn với giá trị mặc định là countries: dict[str, tuple[str, int]] = {}
0 để bạn có thể đối chiếu với countries: dict[str, tuple[str, int]] = {}
0 trước khi sử dụng thuộc tính và tránh sự cố về một số lỗi khó chịu, e. g. khi sử dụng danh sách trống làm mặc định [xem [7]]. Tuy nhiên, điều này không ngăn người dùng chức năng của bạn chuyển countries: dict[str, tuple[str, int]] = {}
0 tới bất kỳ tham số nào, ngay cả những tham số bắt buộc. Điều này xảy ra thường xuyên, chủ yếu là khi xâu chuỗi các hàm và chuyển qua các giá trị trả về. Vậy bạn có thể làm gì để nhận ra điều này? Hãy trở lại với ví dụ về việc gửi email, lần này một chút chi tiết
countries: dict[str, tuple[str, int]] = {}
0Có một số tham số có gợi ý loại
countries: dict[str, tuple[str, int]] = {}
72, cụ thể là countries: dict[str, tuple[str, int]] = {}
73, countries: dict[str, tuple[str, int]] = {}
74 và countries: dict[str, tuple[str, int]] = {}
75. Hoàn toàn không có gợi ý loại, kiểm tra loại tĩnh sẽ không gây ra bất kỳ lỗi nào cho bạn khi chuyển countries: dict[str, tuple[str, int]] = {}
0 cho bất kỳ tham số nào, vì theo mặc định, tham số không có gợi ý loại có loại countries: dict[str, tuple[str, int]] = {}
77, cũng được đáp ứng bởi countries: dict[str, tuple[str, int]] = {}
0. Khi thêm một gợi ý loại mà không có countries: dict[str, tuple[str, int]] = {}
72, thì countries: dict[str, tuple[str, int]] = {}
0 không còn là một tùy chọn hợp lệ và trình kiểm tra loại của bạn sẽ thông báo cho bạn về loại không tương thích. Vì vậy, ví dụ khi chuyển countries: dict[str, tuple[str, int]] = {}
0 làm giá trị cho countries: dict[str, tuple[str, int]] = {}
82, bạn nhận được. countries: dict[str, tuple[str, int]] = {}
83Để cho phép rõ ràng đối với
countries: dict[str, tuple[str, int]] = {}
0, bạn có thể thêm countries: dict[str, tuple[str, int]] = {}
72 vào gợi ý loại, cho phép cả loại thực tế và giá trị đặc biệt countries: dict[str, tuple[str, int]] = {}
0. Bây giờ, việc gọi hàm với countries: dict[str, tuple[str, int]] = {}
0 cho tham số countries: dict[str, tuple[str, int]] = {}
75 không còn là vấn đề nữa vì countries: dict[str, tuple[str, int]] = {}
89 là lối tắt của countries = {}
80 và do đó cho phép giá trị countries: dict[str, tuple[str, int]] = {}
0Một ví dụ hơi khác là như sau. Hãy tưởng tượng bạn muốn gọi hàm
countries = {}
82 với một số danh sách số. Tuy nhiên, một số số thực sự có thể là giá trị countries: dict[str, tuple[str, int]] = {}
0 do một số lỗi trong quá trình ghi [e. g. giá trị cảm biến]. Đây là một vấn đề vì hàm countries = {}
82 không xử lý tốt các giá trị countries: dict[str, tuple[str, int]] = {}
0 và thay vào đó ném ra một giá trị countries = {}
86countries: dict[str, tuple[str, int]] = {}
6Điều này khó thấy nếu không có trình kiểm tra kiểu tĩnh. Tuy nhiên, nếu có một biến
countries = {}
87 được chú thích chính xác bằng countries = {}
88, cho biết rằng một số số có thể bị thiếu, Pylance sẽ thông báo cho bạn về sự cố khi chuyển biến này cho hàm countries = {}
82. “Không có tình trạng quá tải nào đối với countries = {}
82 khớp với các đối số được cung cấp Các loại đối số. countries: dict[str, tuple[str, int]] = {}
11Vì vậy, nhập gợi ý đã ngăn bạn thành công khỏi lỗi thời gian chạy do giá trị
countries: dict[str, tuple[str, int]] = {}
0 ẩn trong một số thuộc tínhĐiều này kết thúc phần thứ ba của loạt bài này. Bạn nên tìm hiểu về cách chú thích các biến chưa được khởi tạo đầy đủ, các biến thể hiện và lớp và cách xử lý nhiều loại cùng một lúc cũng như giá trị đặc biệt
countries: dict[str, tuple[str, int]] = {}
0. Tôi nghĩ đây là đủ ví dụ, vì vậy phần tiếp theo của loạt bài này sẽ giải quyết cách thực sự kiểm tra gợi ý loạiTôi hy vọng bạn đã thích đọc bài viết. Nếu bạn có bất kỳ câu hỏi, nhận xét hoặc đề xuất nào, vui lòng liên hệ với tôi qua PyBites Slack, LinkedIn hoặc GitHub. Bạn cũng có thể để lại bình luận của mình ngay trên trang này
Hãy cho tôi biết nếu bạn muốn đọc thêm về chủ đề này hoặc bất kỳ chủ đề nào khác, về vấn đề đó
Tài nguyên
Tài nguyên bao gồm toàn bộ loạt bài viết về gợi ý loại. Không phải tất cả các tài nguyên đều được tham chiếu trong phần này