TYPE_CHECKING của Python

Xin chào, hãy cho phép người tiêm thực hiện một dự án và thấy một chút vấn đề khi cố gắng tiêm các loại được định nghĩa là tham chiếu chuyển tiếp bằng cách sử dụng khối if TYPE_CHECKING, được MyPy hỗ trợ giúp ngăn chặn các vấn đề phụ thuộc vòng tròn dựa trên nhu cầu nhập nội dung

Tôi nghi ngờ điều đó có thể không thực hiện được và trình tiêm sẽ yêu cầu nhập các mô-đun đó khi chạy, nhưng việc sử dụng chức năng này sẽ cực kỳ tiện dụng nếu có thể vì nếu không, tôi thấy rằng MyPy không sử dụng nhập TYPE_CHECKING có xu hướng tạo mã OO phức tạp hơn

Tôi đã tự hỏi liệu một cách tiếp cận nào đó của Injector nhập tất cả các mô-đun được xác định trong bất kỳ liên kết nào trong khối TYPE_CHECKING để nó có tham chiếu đến chúng mà sau này có thể sử dụng khi thử đánh giá tham chiếu chuyển tiếp không?

Nghi ngờ rằng điều này có thể gây ra một số vấn đề với các tham chiếu vòng tròn trong chính bộ tiêm, vì vậy có lẽ điều đó là không thể, hoặc có thể thực hiện một phương pháp khác?

def add_context[self, ctx. IRTLSContext] -> Không. nếu TYPE_CHECKING. # Điều này là cần thiết vì nếu không thì tự. __setitem__ nhầm lẫn mọi thứ. xử lý. Có thể gọi [[str, str], Không] nếu ctx. is_fallback. bản thân. is_fallback = True cho secretinfokey, handler, hkey in [ [ 'cert_chain_file', self. update_cert_zero, 'certificate_chain' ], [ 'private_key_file', self. update_cert_zero, 'private_key' ], [ 'cacert_chain_file', self. update_validation, 'trusted_ca' ], ]. nếu secretinfokey trong ctx['secret_info']. trình xử lý[hkey, ctx['secret_info'][secretinfokey]] cho ctxkey, handler, hkey trong [ [ 'alpn_protocols', self. update_alpn, 'alpn_protocols' ], ​​[ 'cert_required', self. __setitem__, 'require_client_certificate' ], [ 'min_tls_version', self. update_tls_version, 'tls_minimum_protocol_version' ], [ 'max_tls_version', tự. update_tls_version, 'tls_maximum_protocol_version' ], [ 'sni', tự. __setitem__, 'sni' ], ]. giá trị = ctx. get[ctxkey, Không có] nếu giá trị không phải là Không có. handler[hkey, value] # Đây là một vòng lặp riêng vì self. update_tls_cipher không cùng loại # với các trình cập nhật khác. nó lấy một danh sách các chuỗi cho giá trị, không phải một chuỗi # đơn lẻ. Bắt mypy hài lòng với điều đó là _annoying_. cho ctxkey, list_handler, hkey trong [ [ 'cipher_suites', self. update_tls_cipher, 'cipher_suites' ], ​​[ 'ecdh_curves', self. update_tls_cipher, 'ecdh_curves' ], ​​]. giá trị = ctx. get[ctxkey, Không có] nếu giá trị không phải là Không có. list_handler[hkey, value]

Việc sử dụng thành ngữ các chú thích kiểu đôi khi có thể chạy ngược lại với những gì một phiên bản Python nhất định coi là mã hợp pháp. Phần này mô tả các tình huống này và giải thích cách để mã của bạn chạy lại. Nói chung, chúng ta có ba công cụ tùy ý sử dụng

  • Sử dụng

    def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
    def g[n: 'int'] -> None: ...      # Also OK, though not useful
    
    class A: pass
    
    6 [PEP 563] [hành vi này cuối cùng có thể được đặt làm mặc định trong phiên bản Python trong tương lai]

  • Sử dụng các loại chuỗi ký tự hoặc loại nhận xét

  • Sử dụng

    def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
    def g[n: 'int'] -> None: ...      # Also OK, though not useful
    
    class A: pass
    
    7

Chúng tôi cung cấp mô tả về những vấn đề này trước khi chuyển sang thảo luận về các vấn đề cụ thể mà bạn có thể gặp phải

Chuỗi các loại chữ và loại bình luận #

Mypy cho phép bạn thêm chú thích loại bằng cách sử dụng chú thích loại

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
8. Ví dụ

a = 1  # type: int

def f[x]:  # type: [int] -> int
    return x + 1

# Alternative type comment syntax for functions with many arguments
def send_email[
     address,     # type: Union[str, List[str]]
     sender,      # type: str
     cc,          # type: Optional[List[str]]
     subject='',
     body=None    # type: List[str]
]:
    # type: [...] -> bool

Nhập nhận xét không thể gây ra lỗi thời gian chạy vì nhận xét không được Python đánh giá

Theo cách tương tự, việc sử dụng các kiểu ký tự chuỗi sẽ tránh được vấn đề về chú thích có thể gây ra lỗi thời gian chạy

Bất kỳ loại nào cũng có thể được nhập dưới dạng chuỗi ký tự và bạn có thể tự do kết hợp các loại ký tự chuỗi với các loại ký tự không phải chuỗi

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass

Các loại chuỗi ký tự không bao giờ cần thiết trong

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
8 nhận xét và tệp sơ khai .

Các kiểu chuỗi ký tự phải được xác định [hoặc nhập] sau trong cùng một mô-đun. Chúng không thể được sử dụng để bỏ qua các tham chiếu mô-đun chéo chưa được giải quyết. [Để xử lý các chu kỳ nhập, hãy xem Chu kỳ nhập . ]

Nhập chú thích trong tương lai [PEP 563]#

Nhiều vấn đề được mô tả ở đây là do Python cố gắng đánh giá các chú thích. Các phiên bản Python trong tương lai [có thể là Python 3. 12] theo mặc định sẽ không còn cố gắng đánh giá các chú thích biến và hàm. Hành vi này được cung cấp trong Python 3. 7 trở lên thông qua việc sử dụng

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
6

Điều này có thể được coi là tự động hóa chuỗi ký tự của tất cả các chú thích chức năng và biến. Lưu ý rằng các chú thích hàm và biến vẫn phải là cú pháp Python hợp lệ. Để biết thêm chi tiết, xem PEP 563

Ghi chú

Ngay cả khi nhập

# base class example
from __future__ import annotations
class A[tuple['B', 'C']]: ... # String literal types needed here
class B: ...
class C: ...
1, có một số tình huống vẫn có thể yêu cầu chuỗi ký tự hoặc dẫn đến lỗi, thường liên quan đến việc sử dụng tham chiếu chuyển tiếp hoặc khái quát trong

  • nhập bí danh ;

  • loại thu hẹp ;

  • định nghĩa loại [xem ________ 72, ________ 73, ________ 74];

  • các lớp cơ sở

# base class example
from __future__ import annotations
class A[tuple['B', 'C']]: ... # String literal types needed here
class B: ...
class C: ...

Cảnh báo

Một số thư viện có thể có các trường hợp sử dụng để đánh giá động các chú thích, chẳng hạn như thông qua việc sử dụng

# base class example
from __future__ import annotations
class A[tuple['B', 'C']]: ... # String literal types needed here
class B: ...
class C: ...
5 hoặc
# base class example
from __future__ import annotations
class A[tuple['B', 'C']]: ... # String literal types needed here
class B: ...
class C: ...
6. Nếu chú thích của bạn phát sinh lỗi khi được đánh giá [giả sử bằng cách sử dụng cú pháp PEP 604 với Python 3. 9], bạn có thể cần phải cẩn thận khi sử dụng các thư viện như vậy

đánh máy. TYPE_CHECKING#

Mô-đun

# base class example
from __future__ import annotations
class A[tuple['B', 'C']]: ... # String literal types needed here
class B: ...
class C: ...
7 định nghĩa hằng số
# base class example
from __future__ import annotations
class A[tuple['B', 'C']]: ... # String literal types needed here
class B: ...
class C: ...
8 là
# base class example
from __future__ import annotations
class A[tuple['B', 'C']]: ... # String literal types needed here
class B: ...
class C: ...
9 khi chạy nhưng được coi là
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
70 khi kiểm tra kiểu

Vì mã bên trong

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
71 không được thực thi trong thời gian chạy, nên nó cung cấp một cách thuận tiện để nói với mypy điều gì đó mà không đánh giá mã trong thời gian chạy. Điều này hữu ích nhất để giải quyết chu kỳ nhập .

Tên lớp chuyển tiếp tài liệu tham khảo #

Python không cho phép tham chiếu đến một đối tượng lớp trước khi lớp được xác định [còn gọi là tham chiếu chuyển tiếp]. Do đó, mã này không hoạt động như mong đợi

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
7

Bắt đầu từ Python 3. 7, bạn có thể thêm

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
6 để giải quyết vấn đề này, như đã thảo luận trước đó

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
1

Đối với Trăn 3. 6 trở xuống, bạn có thể nhập loại dưới dạng chuỗi ký tự hoặc loại nhận xét

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
2

Tất nhiên, thay vì sử dụng các kiểu nhập chú thích hoặc chuỗi ký tự trong tương lai, bạn có thể di chuyển định nghĩa hàm sau định nghĩa lớp. Điều này không phải lúc nào cũng mong muốn hoặc thậm chí có thể xảy ra, mặc dù

Chu kỳ nhập khẩu #

Một chu kỳ nhập xảy ra khi mô-đun A nhập mô-đun B và mô-đun B nhập mô-đun A [có lẽ là gián tiếp, e. g.

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
73]. Đôi khi để thêm chú thích loại, bạn phải thêm các lần nhập bổ sung vào một mô-đun và những lần nhập đó gây ra các chu kỳ không tồn tại trước đó. Điều này có thể dẫn đến lỗi trong thời gian chạy như

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
4

Nếu các chu kỳ đó trở thành vấn đề khi chạy chương trình của bạn, thì có một mẹo. nếu việc nhập chỉ cần thiết cho chú thích loại và bạn đang sử dụng a] nhập chú thích trong tương lai hoặc b] chuỗi ký tự hoặc loại nhận xét cho các chú thích có liên quan . Thí dụ.

Tệp

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
75

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
7

Tệp

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
76

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
9

Sử dụng các lớp chung trong sơ khai nhưng không phải trong thời gian chạy#

Một số lớp được khai báo là chung trong sơ khai, nhưng không phải trong thời gian chạy.

Trong Trăn 3. 8 trở về trước, có một số ví dụ trong thư viện tiêu chuẩn, ví dụ,

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
77 và
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
78. Đăng ký một lớp như vậy sẽ dẫn đến lỗi thời gian chạy

a = 1  # type: int

def f[x]:  # type: [int] -> int
    return x + 1

# Alternative type comment syntax for functions with many arguments
def send_email[
     address,     # type: Union[str, List[str]]
     sender,      # type: str
     cc,          # type: Optional[List[str]]
     subject='',
     body=None    # type: List[str]
]:
    # type: [...] -> bool
2

Để tránh lỗi khi sử dụng các từ chung này trong chú thích, chỉ cần sử dụng nhập chú thích trong tương lai [hoặc ký tự chuỗi ký tự hoặc nhập nhận xét cho Python 3. 6 trở xuống].

Để tránh lỗi khi kế thừa từ các lớp này, mọi thứ phức tạp hơn một chút và bạn cần sử dụng typeping. TYPE_CHECKING .

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
0

Nếu lớp con của bạn cũng chung chung, bạn có thể sử dụng như sau

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
1

Trong Trăn 3. 9, chúng ta chỉ có thể kế thừa trực tiếp từ

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
79 hoặc
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
10 vì
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
78 của nó triển khai
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
12, vì vậy đối tượng lớp có thể được đăng ký trong thời gian chạy mà không gặp sự cố

Sử dụng các loại được xác định trong sơ khai nhưng không phải trong thời gian chạy#

Đôi khi sơ khai bạn đang sử dụng có thể xác định loại bạn muốn sử dụng lại không tồn tại trong thời gian chạy. Nhập các loại này một cách ngây thơ sẽ khiến mã của bạn bị lỗi khi chạy với

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
13 hoặc
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
14. Tương tự như các phần trước, bạn có thể xử lý những vấn đề này bằng cách sử dụng nhập. TYPE_CHECKING .

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
2

Sử dụng nội trang chung #

Bắt đầu với Python 3. 9 [PEP 585], đối tượng kiểu của nhiều bộ sưu tập trong thư viện chuẩn hỗ trợ đăng ký khi chạy. Điều này có nghĩa là bạn không còn phải nhập các giá trị tương đương từ

# base class example
from __future__ import annotations
class A[tuple['B', 'C']]: ... # String literal types needed here
class B: ...
class C: ...
7;

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
3

Có hỗ trợ hạn chế cho việc sử dụng cú pháp này trong Python 3. 7 trở lên cũng vậy. nếu bạn sử dụng

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
6, mypy sẽ hiểu cú pháp này trong chú thích. Tuy nhiên, vì điều này sẽ không được trình thông dịch Python hỗ trợ trong thời gian chạy, hãy đảm bảo rằng bạn biết những lưu ý được đề cập trong ghi chú tại nhập chú thích trong tương lai .

Sử dụng X. Cú pháp Y cho Unions#

Bắt đầu với Python 3. 10 [PEP 604], bạn có thể đánh vần các loại liên kết là

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
18, thay vì
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
19

Có hỗ trợ hạn chế cho việc sử dụng cú pháp này trong Python 3. 7 trở lên cũng vậy. nếu bạn sử dụng

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
6, mypy sẽ hiểu cú pháp này trong chú thích, kiểu ký tự chuỗi, nhận xét kiểu và tệp sơ khai. Tuy nhiên, vì điều này sẽ không được trình thông dịch Python hỗ trợ trong thời gian chạy [nếu được đánh giá,
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
21 sẽ tăng
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
22], hãy đảm bảo rằng bạn biết về các cảnh báo được đề cập trong ghi chú tại nhập chú thích trong tương lai . .

Sử dụng các bổ sung mới cho mô-đun gõ #

Bạn có thể thấy mình muốn sử dụng các tính năng được thêm vào mô-đun

# base class example
from __future__ import annotations
class A[tuple['B', 'C']]: ... # String literal types needed here
class B: ...
class C: ...
7 trong các phiên bản Python cũ hơn so với tính năng bổ sung, ví dụ: sử dụng bất kỳ tính năng nào trong số
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
24,
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
25,
def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
26 với Python 3. 6

Cách dễ nhất để thực hiện việc này là cài đặt và sử dụng gói

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
27 từ PyPI cho các mục nhập có liên quan, chẳng hạn

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
4

Nếu bạn không muốn dựa vào việc cài đặt

def f[a: list['A']] -> None: ...  # OK, prevents NameError since A is defined later
def g[n: 'int'] -> None: ...      # Also OK, though not useful

class A: pass
27 trên Pythons mới hơn, bạn có thể sử dụng cách khác

Type_checking Python là gì?

Python vừa là ngôn ngữ lập trình được gõ mạnh vừa là ngôn ngữ động. Điều này có nghĩa là bất kỳ biến nào cũng có thể nhận bất kỳ loại dữ liệu nào vào bất kỳ lúc nào [đây là kiểu được nhập động], nhưng một khi biến đã được gán với một loại, nó không thể thay đổi theo cách không mong muốn.

Bạn có thể chỉ định các loại trong Python không?

Python là ngôn ngữ được nhập động, có nghĩa là chúng ta không phải chỉ định loại dữ liệu khi tạo biến và hàm . Mặc dù điều này làm giảm số lượng mã chúng tôi cần viết, nhưng khối lượng công việc mà chúng tôi tiết kiệm được lại được thêm vào cho nhà phát triển tiếp theo cần hiểu và gỡ lỗi chức năng hiện có.

Chú thích kiểu trong Python là gì?

Chú thích loại là gì. ? . used to indicate the data types of variables and inputs/outputs of functions and methods.

Làm thế nào để Python suy luận các loại?

Suy luận kiểu đề cập đến việc tự động phát hiện kiểu dữ liệu của một biểu thức trong ngôn ngữ lập trình . Có nghĩa là loại a là Int, phát hiện tự động này được gọi là suy luận loại. Python, Swift và Kotlin có kiểu suy luận. Python là ngôn ngữ kiểu động trong khi Swift và Kotlin là ngôn ngữ kiểu nghiêm ngặt.

Chủ Đề