Gợi ý loại trong Python 3.5 là gì?


250

Một trong những tính năng được nói nhiều nhất trong Python 3.5 là gợi ý loại .

Một ví dụ về gợi ý loại được đề cập trong bài viết nàybài này trong khi cũng đề cập đến việc sử dụng gợi ý loại có trách nhiệm. Ai đó có thể giải thích thêm về chúng và khi nào chúng nên được sử dụng và khi nào không?


4
Bạn nên xem PEP 484 được liên kết từ thay đổi chính thức .
Stefan

1
@AvinashRaj: Một cuộc thảo luận tốt về các bản phát hành đang diễn ra ở đây
Vaulstein

1
Thật đáng tiếc khi trường hợp sử dụng C-API hoàn toàn bị bỏ qua bởi PEP 484 này, đặc biệt là gợi ý loại cho Cython và Numba.
denfromufa

Câu trả lời:


343

Tôi sẽ đề nghị đọc PEP 483PEP 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 ý?

  1. 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.
  2. 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.
  3. 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: typeNhậ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: typenhậ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 typingmô-đ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 ABCstrong collections.abcđược bao gồm nhưng trong một Generichì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 documentationnó đượ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: typeNhậ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 Containersvà 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ừ typingmô-đ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 # typebì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 mypymặ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 avà chỉ bổ sung strcá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 : typesau 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 -> typeký 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ừ typingmô-đ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.7khá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 typingmô-đun, PEP hoặc mypytà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 intcá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 combinechứ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à mypybạ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 typesheddự á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 Counterlớp trong .pyitệ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 Counterlớ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 Mappingtừ bất kỳ loại nào sang một int hoặc lấy Iterablebất kỳ loại nào.


Lưu ý : Một điều tôi quên đề cập là typingmô-đ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 # typenhậ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: typecâ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.


3
"Do tính chất rất năng động của Python, việc suy luận hoặc kiểm tra loại đối tượng đang sử dụng là đặc biệt khó khăn." Bạn đang đề cập đến kiểm tra tĩnh, phải không?
bsam

53

Thêm vào câu trả lời công phu của Jim:

Kiểm tra typingmô-đun - mô-đun này hỗ trợ gợi ý loại như được chỉ định bởi PEP 484 .

Ví dụ, hàm bên dưới nhận và trả về các giá trị loại strvà được chú thích như sau:

def greeting(name: str) -> str:
    return 'Hello ' + name

Các typingmô-đun cũng hỗ trợ:

  1. Gõ răng cưa .
  2. Nhập gợi ý cho các chức năng gọi lại .
  3. Generics - Các lớp cơ sở trừu tượng đã được mở rộng để hỗ trợ đăng ký để biểu thị các loại dự kiến ​​cho các thành phần container.
  4. Các loại chung do người dùng định nghĩa - Một lớp do người dùng định nghĩa có thể được định nghĩa là một lớp chung.
  5. Bất kỳ loại nào - Mỗi loại là một kiểu con của Bất kỳ.

26

PyCharm 5 mới được phát hành hỗ trợ gợi ý loại. Trong bài đăng trên blog của họ về nó (xem gợi ý loại Python 3.5 trong PyCharm 5 ), họ đưa ra một lời giải thích tuyệt vời về gợi ý loại nào và không cùng với một số ví dụ và minh họa về cách sử dụng chúng trong mã của bạn.

Ngoài ra, nó được hỗ trợ trong Python 2.7, như được giải thích trong bình luận này :

PyCharm hỗ trợ mô-đun gõ từ PyPI cho Python 2.7, Python 3.2-3.4. Đối với 2.7, bạn phải đặt gợi ý loại trong các tệp * .pyi vì các chú thích chức năng đã được thêm vào Python 3.0 .


0

Gợi ý loại là một bổ sung gần đây cho một ngôn ngữ động, trong nhiều thập kỷ, mọi người đã thề rằng các quy ước đặt tên đơn giản như tiếng Hungary (nhãn đối tượng với chữ cái đầu tiên b = boolian, c = character, d = dictionary, i = số nguyên, l = list, n = số , s = string, t = tuple) không cần thiết, quá cồng kềnh, nhưng giờ đã quyết định rằng, ồ chờ đã ... thật quá rắc rối khi sử dụng ngôn ngữ (type ()) để nhận ra các đối tượng và IDE ưa thích của chúng tôi cần trợ giúp để làm bất cứ điều gì phức tạp và các giá trị đối tượng được gán động khiến chúng hoàn toàn vô dụng, trong khi một quy ước đặt tên đơn giản có thể giải quyết tất cả, cho bất kỳ nhà phát triển nào, chỉ trong nháy mắt.


Thẳng thắn mà nói, điều này nghe giống như một câu nói hay hơn là một câu trả lời.
Dimitris Fasarakis Hilliard

-1

Gợi ý kiểu là để duy trì và không được Python giải thích. Trong mã bên dưới, dòng def add(self, ic:int)không gây ra lỗi cho đến return...dòng tiếp theo :

class C1:
    def __init__(self):
        self.idn = 1
    def add(self, ic: int):
        return self.idn + ic
    
c1 = C1()
c1.add(2)

c1.add(c1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in add
TypeError: unsupported operand type(s) for +: 'int' and 'C1'
 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.