Python 3.8 keluar malam ini dan mengetik anotasi mendapat fitur baru:
- Protokol
- Kamus yang Diketik
- Specifier akhir
- Pencocokan nilai tetap
Jika Anda belum terbiasa dengan anotasi jenis, saya sarankan memperhatikan artikel saya sebelumnya ( awal , lanjutan )
Dan sementara semua orang khawatir tentang walrus, saya ingin berbicara singkat tentang yang terbaru dalam modul pengetikan
Protokol
Python menggunakan pengetikan bebek dan kelas tidak diwajibkan untuk mewarisi dari antarmuka tertentu, seperti dalam beberapa bahasa lainnya.
Sayangnya, sebelum versi 3.8, kami tidak dapat mengungkapkan persyaratan yang diperlukan untuk objek menggunakan anotasi jenis.
PEP 544 dirancang untuk mengatasi masalah ini.
Istilah seperti "protokol iterator" atau "protokol deskriptor" sudah akrab dan telah digunakan untuk waktu yang lama.
Sekarang Anda dapat menggambarkan protokol dalam bentuk kode dan memeriksa kepatuhannya pada tahap analisis statis.
Perlu dicatat bahwa, dimulai dengan Python 3.6, modul pengetikan sudah mencakup beberapa protokol standar.
Misalnya, SupportsInt
(yang membutuhkan metode __int__
), SupportsBytes
(membutuhkan __bytes__
), dan beberapa lainnya.
Deskripsi Protokol
Protokol dijelaskan sebagai kelas reguler yang diturunkan dari Protokol. Itu dapat memiliki metode (termasuk yang dengan implementasi) dan bidang.
Kelas nyata yang mengimplementasikan protokol dapat mewarisi darinya, tetapi ini tidak perlu.
from abc import abstractmethod from typing import Protocol, Iterable class SupportsRoar(Protocol): @abstractmethod def roar(self) -> None: raise NotImplementedError class Lion(SupportsRoar): def roar(self) -> None: print("roar") class Tiger: def roar(self) -> None: print("roar") class Cat: def meow(self) -> None: print("meow") def roar_all(bigcats: Iterable[SupportsRoar]) -> None: for t in bigcats: t.roar() roar_all([Lion(), Tiger()])
Kita bisa menggabungkan protokol menggunakan warisan, membuat yang baru.
Namun, dalam hal ini, Anda juga harus secara eksplisit menentukan Protokol sebagai kelas induk.
class BigCatProtocol(SupportsRoar, Protocol): def purr(self) -> None: print("purr")
Generik, diketik sendiri, bisa dipanggil
Protokol, seperti kelas reguler, bisa menjadi generik. Alih-alih menetapkan Protocol
dan Generic[T, S,...]
sebagai orang tua Generic[T, S,...]
Anda cukup menentukan Protocol[T, S,...]
Jenis protokol penting lainnya adalah mengetik sendiri (lihat PEP 484 ). Sebagai contoh
C = TypeVar('C', bound='Copyable') class Copyable(Protocol): def copy(self: C) -> C: class One: def copy(self) -> 'One': ...
Selain itu, protokol dapat digunakan ketika sintaks anotasi Callable
tidak cukup.
Cukup jelaskan protokol dengan metode __call__
tanda tangan yang diinginkan
Pemeriksaan runtime
Meskipun protokol terutama dirancang untuk digunakan oleh analisis statis, kadang-kadang perlu untuk memeriksa apakah kelas milik protokol yang diinginkan.
Untuk memungkinkan ini, terapkan dekorator @runtime_checkable
ke protokol dan pemeriksaan isinstance
/ issubclass
akan mulai memeriksa kepatuhan terhadap protokol
Namun, fitur ini memiliki sejumlah batasan penggunaan. Secara khusus, obat generik tidak didukung
Kamus yang Diketik
Kelas (khususnya, kelas data ) atau tupel bernama biasanya digunakan untuk mewakili data terstruktur.
tetapi kadang-kadang, misalnya, dalam kasus deskripsi struktur json, dapat bermanfaat untuk memiliki kamus dengan kunci tertentu.
PEP 589 memperkenalkan konsep TypedDict
, yang sebelumnya tersedia dalam ekstensi dari mypy
Seperti dataclasses atau tuple yang diketik, ada dua cara untuk mendeklarasikan kamus yang diketik. Dengan warisan atau menggunakan pabrik:
class Book(TypedDict): title: str author: str AlsoBook = TypedDict("AlsoBook", {"title": str, "author": str})
Kamus yang diketik mendukung warisan:
class BookWithDesc(Book): desc: str
Secara default, semua kunci kamus diperlukan, tetapi Anda dapat menonaktifkan ini dengan melewatkan total=False
saat membuat kelas.
Ini hanya berlaku untuk kunci yang dijelaskan dalam box office saat ini dan tidak mempengaruhi warisan
class SimpleBook(TypedDict, total=False): title: str author: str simple_book: SimpleBook = {"title": "Fareneheit 481"}
Menggunakan TypedDict
memiliki sejumlah keterbatasan. Khususnya:
- pemeriksaan runtime melalui isinstance tidak didukung
- kunci harus berupa nilai literal atau final
Selain itu, operasi yang tidak aman seperti .clear
atau del
dilarang dengan kamus semacam itu.
Bekerja pada kunci yang bukan literal juga dapat dilarang, karena dalam hal ini tidak mungkin untuk menentukan jenis nilai yang diharapkan
Pengubah akhir
PEP 591 memperkenalkan pengubah akhir (sebagai dekorator dan anotasi) untuk beberapa tujuan
- Penunjukan kelas dari mana tidak mungkin untuk diwarisi:
from typing import final @final class Childfree: ... class Baby(Childfree):
- Penunjukan metode yang dilarang menimpa:
from typing import final class Base: @final def foo(self) -> None: ... class Derived(Base): def foo(self) -> None:
- Penunjukan variabel (parameter fungsi. Bidang kelas), yang dilarang untuk ditugaskan kembali.
ID: Final[float] = 1 ID = 2
Dalam hal ini, kode bentuk self.id: Final = 123
, tetapi hanya dalam metode __init__
Secara harfiah
Literal
tipe yang didefinisikan dalam PEP 586 digunakan ketika Anda perlu memeriksa secara harfiah pada nilai-nilai spesifik
Misalnya, Literal[42]
berarti bahwa hanya 42 yang diharapkan sebagai nilai.
Penting bahwa tidak hanya kesetaraan nilai diperiksa, tetapi juga jenisnya (misalnya, tidak mungkin untuk menggunakan False jika 0 diharapkan).
def give_me_five(x: Literal[5]) -> None: pass give_me_five(5)
Dalam hal ini, beberapa nilai dapat ditransmisikan dalam tanda kurung, yang setara dengan menggunakan Union (jenis nilai mungkin tidak bersamaan).
Ekspresi (misalnya, Literal[1+2]
) atau nilai dari tipe yang tidak bisa diubah tidak dapat digunakan sebagai nilai.
Sebagai salah satu contoh yang bermanfaat, menggunakan Literal
adalah fungsi open()
, yang mengharapkan nilai mode
tertentu.
Ketik penanganan dalam runtime
Jika Anda ingin memproses berbagai jenis informasi (seperti saya ) saat program sedang berjalan,
fungsi get_origin dan get_args sekarang tersedia.
Jadi, untuk tipe formulir X[Y, Z,...]
tipe X
akan dikembalikan sebagai asal, dan (Y, Z, ...)
sebagai argumen
Perlu dicatat bahwa jika X adalah alias untuk tipe bawaan atau jenis dari modul collections
, maka akan diganti oleh yang asli.
assert get_origin(Dict[str, int]) is dict assert get_args(Dict[int, str]) == (int, str) assert get_origin(Union[int, str]) is Union assert get_args(Union[int, str]) == (int, str)
Sayangnya, fungsi untuk __parameters__
tidak
Referensi