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: pass8. 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: pass8 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: pass6
Đ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: pass70 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: pass71 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: pass7
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: pass6 để 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: pass1
Đố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: pass2
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: pass73]. Đô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: pass4
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: pass75
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: pass7
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: pass76
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: pass9
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: pass77 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: pass78. Đă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: [...] -> bool2
Để 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: pass0
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: pass1
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: pass79 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: pass10 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: pass78 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: pass12, 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: pass13 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: pass14. 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: pass2
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: pass3
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: pass6, 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: pass18, 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: pass19
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: pass6, 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: pass21 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: pass22], 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: pass24,
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: pass25,
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: pass26 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: pass27 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: pass4
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: pass27 trên Pythons mới hơn, bạn có thể sử dụng cách khác