Tôi sẽ đề nghị đọc PEP 483 và PEP 484 và xem bản trình bày này của Guido trên Type Hinting.
Tóm lại : Gợi ý gõ theo nghĩa đen của từ này, bạn gợi ý loại đối tượng bạn đang sử dụng .
Do tính chất động của Python, việc suy luận hoặc kiểm tra loại đối tượng đang được sử dụng là đặc biệt khó khăn. Thực tế này khiến các nhà phát triển khó hiểu chính xác những gì đang diễn ra trong mã mà họ chưa viết và quan trọng nhất là đối với các công cụ kiểm tra loại được tìm thấy trong nhiều IDE [PyCharm, PyDev đến với tâm trí] bị hạn chế do thực tế là họ không có bất kỳ chỉ số nào về các loại đối tượng. Kết quả là họ dùng đến việc cố gắng suy ra loại với (như đã đề cập trong phần trình bày) khoảng 50% tỷ lệ thành công.
Để có hai trang trình bày quan trọng từ bản trình bày Gợi ý Loại:
Tại sao Gợi ý?
- Giúp trình kiểm tra loại : Bằng cách gợi ý loại bạn muốn đối tượng là trình kiểm tra loại có thể dễ dàng phát hiện nếu, chẳng hạn, bạn đang truyền một đối tượng có loại không mong đợi.
- Giúp với tài liệu: Một người thứ ba xem mã của bạn sẽ biết những gì được mong đợi ở đâu, ergo, làm thế nào để sử dụng nó mà không nhận được chúng
TypeErrors
.
- Giúp IDE phát triển các công cụ chính xác và mạnh mẽ hơn: Môi trường phát triển sẽ phù hợp hơn trong việc đề xuất các phương pháp phù hợp khi biết loại đối tượng của bạn là gì. Bạn có thể đã trải nghiệm điều này với một số IDE tại một số điểm, nhấn
.
và có các phương thức / thuộc tính bật lên mà không được xác định cho một đối tượng.
Tại sao nên sử dụng Bộ kiểm tra loại tĩnh?
- Tìm lỗi sớm hơn : Điều này là hiển nhiên, tôi tin.
- Dự án của bạn càng lớn bạn càng cần nó : Một lần nữa, có ý nghĩa. Ngôn ngữ tĩnh cung cấp một sự mạnh mẽ và kiểm soát mà ngôn ngữ động thiếu. Ứng dụng của bạn càng lớn và càng phức tạp thì càng có nhiều khả năng kiểm soát và dự đoán (từ khía cạnh hành vi) mà bạn yêu cầu.
- Các nhóm lớn đã chạy phân tích tĩnh : Tôi đoán điều này sẽ xác minh hai điểm đầu tiên.
Như một lưu ý cuối cùng cho phần giới thiệu nhỏ này : Đây là một tính năng tùy chọn và, theo những gì tôi hiểu, nó đã được giới thiệu để gặt hái một số lợi ích của việc gõ tĩnh.
Nói chung, bạn không cần phải lo lắng về điều đó và chắc chắn không cần sử dụng nó (đặc biệt là trong trường hợp bạn sử dụng Python làm ngôn ngữ kịch bản phụ trợ). Nó sẽ hữu ích khi phát triển các dự án lớn vì nó cung cấp khả năng mạnh mẽ, kiểm soát và gỡ lỗi bổ sung rất cần thiết .
Gõ Gợi ý với mypy :
Để làm cho câu trả lời này đầy đủ hơn, tôi nghĩ một chút trình diễn sẽ phù hợp. Tôi sẽ sử dụng mypy
, thư viện lấy cảm hứng từ Loại gợi ý khi chúng được trình bày trong PEP. Điều này chủ yếu được viết cho bất cứ ai va vào câu hỏi này và tự hỏi bắt đầu từ đâu.
Trước khi tôi làm điều đó, hãy để tôi nhắc lại những điều sau: PEP 484 không thi hành bất cứ điều gì; nó chỉ đơn giản là thiết lập một hướng cho các chú thích chức năng và đề xuất các hướng dẫn về cách kiểm tra kiểu có thể / nên được thực hiện. Bạn có thể chú thích các chức năng của bạn và gợi ý nhiều thứ bạn muốn; tập lệnh của bạn vẫn sẽ chạy bất kể sự hiện diện của chú thích vì bản thân Python không sử dụng chúng.
Dù sao, như đã lưu ý trong PEP, các loại gợi ý thường có ba dạng:
- Chú thích chức năng. ( PEP 3107 )
- Các tệp sơ khai cho các mô-đun tích hợp / người dùng.
# type: type
Nhận xét đặc biệt bổ sung cho hai hình thức đầu tiên. (Xem: Chú thích biến trong Python 3.6 là gì? Để cập nhật Python 3.6 cho # type: type
nhận xét)
Ngoài ra, bạn sẽ muốn sử dụng các gợi ý loại kết hợp với typing
mô-đun mới được giới thiệu trong Py3.5
. Trong đó, nhiều ABC (bổ sung) (Các lớp cơ sở trừu tượng) được định nghĩa cùng với các hàm trợ giúp và trang trí để sử dụng trong kiểm tra tĩnh. Hầu hết ABCs
trong collections.abc
được bao gồm nhưng trong một Generic
hình thức để cho phép đăng ký (bằng cách xác định một __getitem__()
phương thức).
Đối với bất kỳ ai quan tâm đến một lời giải thích sâu hơn về những điều này, thì mypy documentation
nó được viết rất độc đáo và có rất nhiều mẫu mã trình bày / mô tả chức năng của trình kiểm tra của họ; Nó chắc chắn là giá trị đọc.
Chú thích chức năng và nhận xét đặc biệt:
Đầu tiên, thật thú vị khi quan sát một số hành vi chúng ta có thể nhận được khi sử dụng các bình luận đặc biệt. # type: type
Nhận xét đặc biệt có thể được thêm vào trong các bài tập biến để chỉ ra loại đối tượng nếu không thể suy ra trực tiếp. Bài tập đơn giản thường dễ dàng được suy luận nhưng những bài khác, như danh sách (liên quan đến nội dung của chúng), không thể.
Lưu ý: Nếu chúng tôi muốn sử dụng bất kỳ dẫn xuất nào Containers
và cần chỉ định nội dung cho vùng chứa đó, chúng tôi phải sử dụng các loại chung từ typing
mô-đun. Những chỉ số hỗ trợ.
# generic List, supports indexing.
from typing import List
# In this case, the type is easily inferred as type: int.
i = 0
# Even though the type can be inferred as of type list
# there is no way to know the contents of this list.
# By using type: List[str] we indicate we want to use a list of strings.
a = [] # type: List[str]
# Appending an int to our list
# is statically not correct.
a.append(i)
# Appending a string is fine.
a.append("i")
print(a) # [0, 'i']
Nếu chúng ta thêm các lệnh này vào một tệp và thực thi chúng bằng trình thông dịch của mình, mọi thứ sẽ hoạt động tốt và print(a)
chỉ in nội dung của danh sách a
. Các # type
bình luận đã bị loại bỏ, được coi là các bình luận đơn giản không có ý nghĩa ngữ nghĩa bổ sung .
Bằng cách chạy cái này với mypy
mặt khác, chúng ta có được phản hồi sau:
(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
Chỉ ra rằng một danh sách các str
đối tượng không thể chứa một int
, trong đó, nói một cách tĩnh, là âm thanh. Điều này có thể được khắc phục bằng cách tuân theo loại a
và chỉ bổ sung str
các đối tượng hoặc bằng cách thay đổi loại nội dung a
để chỉ ra rằng mọi giá trị đều được chấp nhận (Được thực hiện trực quan List[Any]
sau khi Any
đã được nhập từ typing
).
Chú thích chức năng được thêm vào dưới dạng param_name : type
sau mỗi tham số trong chữ ký hàm của bạn và loại trả về được chỉ định bằng cách sử dụng -> type
ký hiệu trước dấu hai chấm của hàm kết thúc; tất cả các chú thích được lưu trữ trong __annotations__
thuộc tính cho hàm đó dưới dạng từ điển tiện dụng. Sử dụng một ví dụ tầm thường (không yêu cầu thêm loại từ typing
mô-đun):
def annotated(x: int, y: str) -> bool:
return x < y
Các annotated.__annotations__
thuộc tính hiện nay có các giá trị sau:
{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
Nếu chúng tôi là một người mới hoàn toàn, hoặc chúng tôi quen thuộc với các Py2.7
khái niệm và do đó không biết về sự TypeError
ẩn giấu trong so sánh annotated
, chúng tôi có thể thực hiện một kiểm tra tĩnh khác, bắt lỗi và cứu chúng tôi một số rắc rối:
(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
Trong số những thứ khác, việc gọi hàm với các đối số không hợp lệ cũng sẽ bị bắt:
annotated(20, 20)
# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
Chúng có thể được mở rộng về cơ bản cho bất kỳ trường hợp sử dụng nào và các lỗi bắt gặp còn mở rộng hơn cả các cuộc gọi và thao tác cơ bản. Các loại bạn có thể kiểm tra thực sự linh hoạt và tôi chỉ đơn thuần đưa ra một đỉnh lẻn nhỏ về tiềm năng của nó. Một cái nhìn trong typing
mô-đun, PEP hoặc mypy
tài liệu sẽ cho bạn ý tưởng toàn diện hơn về các khả năng được cung cấp.
Các tệp còn sơ khai:
Các tệp sơ khai có thể được sử dụng trong hai trường hợp không loại trừ lẫn nhau:
- Bạn cần gõ kiểm tra một mô-đun mà bạn không muốn thay đổi trực tiếp chữ ký hàm
- Bạn muốn viết các mô-đun và kiểm tra kiểu nhưng cũng muốn tách các chú thích khỏi nội dung.
Những tệp sơ khai nào (có phần mở rộng là .pyi
) là một giao diện chú thích của mô-đun bạn đang thực hiện / muốn sử dụng. Chúng chứa chữ ký của các chức năng bạn muốn kiểm tra loại với phần thân của các chức năng bị loại bỏ. Để cảm nhận điều này, đã đưa ra một bộ ba hàm ngẫu nhiên trong một mô-đun có tên randfunc.py
:
def message(s):
print(s)
def alterContents(myIterable):
return [i for i in myIterable if i % 2 == 0]
def combine(messageFunc, itFunc):
messageFunc("Printing the Iterable")
a = alterContents(range(1, 20))
return set(a)
Chúng tôi có thể tạo một tệp sơ khai randfunc.pyi
, trong đó chúng tôi có thể đặt một số hạn chế nếu chúng tôi muốn làm như vậy. Nhược điểm là ai đó xem nguồn mà không có sơ khai sẽ không thực sự nhận được sự trợ giúp chú thích đó khi cố gắng hiểu những gì được cho là sẽ được thông qua ở đâu.
Dù sao, cấu trúc của một tệp sơ khai khá đơn giản: Thêm tất cả các định nghĩa hàm với các phần trống ( pass
điền) và cung cấp các chú thích dựa trên yêu cầu của bạn. Ở đây, giả sử chúng ta chỉ muốn làm việc với int
các loại cho Container của chúng ta.
# Stub for randfucn.py
from typing import Iterable, List, Set, Callable
def message(s: str) -> None: pass
def alterContents(myIterable: Iterable[int])-> List[int]: pass
def combine(
messageFunc: Callable[[str], Any],
itFunc: Callable[[Iterable[int]], List[int]]
)-> Set[int]: pass
Các combine
chức năng đưa ra một dấu hiệu cho thấy lý do tại sao bạn có thể muốn sử dụng các chú thích trong một tập tin khác nhau, họ một số lần lộn xộn lên mã và giảm khả năng đọc (không lớn không có cho Python). Tất nhiên bạn có thể sử dụng các bí danh loại nhưng đôi khi điều đó gây nhầm lẫn nhiều hơn là có ích (vì vậy hãy sử dụng chúng một cách khôn ngoan).
Điều này sẽ giúp bạn làm quen với các khái niệm cơ bản về Loại gợi ý trong Python. Mặc dù trình kiểm tra loại được sử dụng là
mypy
bạn nên dần dần bắt đầu thấy nhiều hơn trong số chúng bật lên, một số nội bộ trong IDE ( PyCharm ,) và các loại khác như các mô-đun python tiêu chuẩn. Tôi sẽ thử và thêm các trình kiểm tra / gói liên quan bổ sung trong danh sách sau đây khi và nếu tôi tìm thấy chúng (hoặc nếu được đề xuất).
Cờ đam tôi biết :
- Mypy : như được mô tả ở đây.
- PyType : Bởi Google, sử dụng các ký hiệu khác với những gì tôi thu thập được, có lẽ đáng xem.
Gói / Dự án liên quan :
- typeshed: Repo Python chính thức chứa một tập tin sơ khai cho thư viện chuẩn.
Các typeshed
dự án thực sự là một trong những nơi tốt nhất mà bạn có thể tìm kiếm để xem cách gõ gián tiếp có thể được sử dụng trong một dự án của riêng bạn. Hãy lấy một ví dụ về các bộ __init__
đệm của Counter
lớp trong .pyi
tệp tương ứng :
class Counter(Dict[_T, int], Generic[_T]):
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
@overload
def __init__(self, iterable: Iterable[_T]) -> None: ...
Trường hợp _T = TypeVar('_T')
được sử dụng để định nghĩa các lớp chung . Đối với Counter
lớp chúng ta có thể thấy rằng nó có thể không có đối số trong trình khởi tạo của nó, hãy lấy một Mapping
từ bất kỳ loại nào sang một int
hoặc lấy Iterable
bất kỳ loại nào.
Lưu ý : Một điều tôi quên đề cập là typing
mô-đun đã được giới thiệu trên cơ sở tạm thời . Từ PEP 411 :
Gói tạm thời có thể được sửa đổi API trước khi "tốt nghiệp" thành trạng thái "ổn định". Một mặt, trạng thái này cung cấp gói với các lợi ích chính thức là một phần của phân phối Python. Mặt khác, nhóm phát triển cốt lõi tuyên bố rõ ràng rằng không có lời hứa nào được thực hiện liên quan đến tính ổn định của API của gói, có thể thay đổi cho lần phát hành tiếp theo. Mặc dù được coi là một kết quả không thể xảy ra, các gói như vậy thậm chí có thể bị xóa khỏi thư viện tiêu chuẩn mà không có thời gian khấu hao nếu những lo ngại về API hoặc bảo trì của chúng chứng minh có căn cứ.
Vì vậy, lấy những thứ ở đây với một nhúm muối; Tôi nghi ngờ rằng nó sẽ bị xóa hoặc thay đổi theo những cách quan trọng nhưng người ta không bao giờ có thể biết.
** Một chủ đề khác hoàn toàn nhưng hợp lệ trong phạm vi gợi ý loại PEP 526
:: Cú pháp cho chú thích biến là một nỗ lực để thay thế các # type
nhận xét bằng cách đưa ra cú pháp mới cho phép người dùng chú thích loại biến trong các varname: type
câu lệnh đơn giản .
Xem chú thích biến trong Python 3.6 là gì? , như đã đề cập trước đây, cho một phần giới thiệu nhỏ về những điều này.