Cara menggunakan dataclass Python

Segala sesuatu di Python adalah sebuah objek, atau begitulah kata pepatah. Jika Anda ingin membuat objek kustom Anda sendiri, dengan properti dan metodenya sendiri, Anda menggunakan objek Python classuntuk mewujudkannya. Tetapi membuat kelas dengan Python terkadang berarti menulis banyak kode boilerplate yang berulang untuk menyiapkan instance kelas dari parameter yang diteruskan kepadanya atau untuk membuat fungsi umum seperti operator perbandingan.

Dataclasses, diperkenalkan di Python 3.7 (dan di-backport ke Python 3.6), menyediakan cara praktis untuk mengurangi verbose kelas. Banyak hal umum yang Anda lakukan di kelas, seperti membuat instance properti dari argumen yang diteruskan ke kelas, dapat direduksi menjadi beberapa instruksi dasar.

Contoh kelas data Python

Berikut adalah contoh sederhana dari kelas konvensional dengan Python:

buku kelas:

'' 'Objek untuk melacak buku fisik dalam koleksi.' ''

def __init __ (diri, nama: str, berat: float, shelf_id: int = 0):

self.name = nama

self.weight = weight # dalam gram, untuk menghitung pengiriman

self.shelf_id = shelf_id

def __repr __ (sendiri):

return (f "Book (name = {self.name! r},

weight = {self.weight! r}, shelf_id = {self.shelf_id! r}) ")

Sakit kepala terbesar di sini adalah cara setiap argumen yang diteruskan  __init__ harus disalin ke properti objek. Ini tidak begitu buruk jika Anda hanya berurusan dengan  Book, tetapi bagaimana jika Anda harus berurusan dengan  BookshelfLibraryWarehouse, dan sebagainya? Plus, semakin banyak kode yang harus Anda ketik dengan tangan, semakin besar kemungkinan Anda membuat kesalahan.

Berikut adalah kelas Python yang sama, diimplementasikan sebagai kelas data Python:

from dataclasses import dataclass @dataclass class Book: '' 'Object for tracking physical books in a collection.' '' name: str weight: float shelf_id: int = 0 

Saat Anda menentukan properti, yang disebut  bidang,  dalam kelas data,  @dataclass secara otomatis menghasilkan semua kode yang diperlukan untuk menginisialisasinya. Ini juga mempertahankan informasi tipe untuk setiap properti, jadi jika Anda menggunakan kode linter like  mypy, ini akan memastikan bahwa Anda memberikan jenis variabel yang tepat ke konstruktor kelas.

Hal lain yang  @dataclass dilakukan di balik layar adalah secara otomatis membuat kode untuk sejumlah metode dunder umum di kelas. Di kelas konvensional di atas, kami harus membuatnya sendiri  __repr__. Di dataclass, ini tidak perlu; @dataclass menghasilkan  __repr__ untuk Anda.

Setelah kelas data dibuat, secara fungsional identik dengan kelas biasa. Tidak ada penalti performa untuk menggunakan dataclass, kecuali overhead minimal dekorator saat mendeklarasikan definisi kelas.

Sesuaikan bidang kelas data Python dengan  field fungsinya

Cara kerja default kelas data seharusnya baik-baik saja untuk sebagian besar kasus penggunaan. Namun, terkadang, Anda perlu menyesuaikan bagaimana bidang di kelas data Anda diinisialisasi. Untuk melakukan ini, Anda dapat menggunakan  field fungsinya.

dari dataclasses import dataclass, field dari mengetik import List @dataclass class Book: '' 'Object for tracking physical books in a collection.' '' name: str condition: str = field (Compare = False) weight: float = field (default = 0.0, repr = False) shelf_id: int = 0 chapter: List [str] = field (default_factory = list) 

Saat Anda menyetel nilai default ke sebuah instance  field, itu mengubah cara bidang disiapkan bergantung pada parameter apa yang Anda berikan  field. Ini adalah opsi yang paling umum digunakan untuk field (ada yang lain):

  • default: Menyetel nilai default untuk bidang tersebut. Anda perlu menggunakan defaultjika Anda a) menggunakan  field untuk mengubah parameter lain untuk bidang tersebut, dan b) Anda ingin menetapkan nilai default pada bidang di atasnya. Dalam hal ini kita gunakan  default untuk set  weight ke  0.0.
  • default_factory: Memberikan nama fungsi, yang tidak menggunakan parameter, yang mengembalikan beberapa objek untuk dijadikan nilai default untuk bidang tersebut. Dalam hal ini, kami ingin  chapters menjadi daftar kosong.
  • repr: Secara default ( True), mengontrol jika bidang yang dimaksud muncul di kelas data yang dibuat secara otomatis  __repr__ . Dalam hal ini kami tidak ingin bobot buku ditampilkan di  __repr__, jadi kami menggunakan  repr=False untuk menghilangkannya.
  • compare: Secara default ( True), menyertakan bidang dalam metode perbandingan yang dibuat secara otomatis untuk kelas data. Di sini, kami tidak ingin  condition digunakan sebagai bagian dari perbandingan untuk dua buku, jadi kami menetapkan  compare=False.

Perhatikan bahwa kami harus menyesuaikan urutan bidang sehingga bidang non-default muncul lebih dulu.

Gunakan  __post_init__ untuk mengontrol inisialisasi kelas data Python

Pada titik ini Anda mungkin bertanya-tanya: Jika  __init__ metode kelas data dibuat secara otomatis, bagaimana cara saya mengontrol proses init untuk membuat perubahan yang lebih detail?

Masukkan  __post_init__ metodenya. Jika Anda menyertakan  __post_init__metode dalam definisi kelas data, Anda dapat memberikan instruksi untuk mengubah bidang atau data contoh lainnya.

dari dataclasses import dataclass, field dari mengetik import List @dataclass class Book: '' 'Object for tracking physical books in a collection.' '' name: str weight: float = field (default = 0.0, repr = False) shelf_id: int = field (init = False) chapter: List [str] = field (default_factory = list) kondisi: str = field (default = "Good", bandingkan = False) def __post_init __ (self): if self.condition == "Dibuang ": self.shelf_id = Tidak ada yang lain: self.shelf_id = 0 

Dalam contoh ini, kami telah membuat  __post_init__ metode untuk mengatur shelf_id untuk  None jika kondisi buku diinisialisasi sebagai  "Discarded". Perhatikan bagaimana kita menggunakan  field untuk menginisialisasi  shelf_id, dan lulus  init sebagai  False untuk  field. Ini berarti  shelf_id tidak akan diinisialisasi  __init__.

Gunakan  InitVar untuk mengontrol inisialisasi kelas data Python

Cara lain untuk menyesuaikan pengaturan kelas data Python adalah dengan menggunakan  InitVar tipe. Ini memungkinkan Anda menentukan bidang yang akan diteruskan ke  __init__ dan kemudian ke  __post_init__, tetapi tidak akan disimpan dalam instance kelas.

Dengan menggunakan InitVar, Anda dapat mengambil parameter saat menyiapkan kelas data yang hanya digunakan selama inisialisasi. Sebuah contoh:

dari dataclasses import dataclass, field, InitVar dari mengetik import List @dataclass class Book: '' 'Object for tracking physical books in a collection.' '' name: str condition: InitVar [str] = None weight: float = field (default = 0.0, repr = False) shelf_id: int = field (init = False) chapter: List [str] = field (default_factory = list) def __post_init __ (self, condition): if condition == "Discarded": self.shelf_id = Tidak ada yang lain: self.shelf_id = 0 

Menyetel jenis bidang ke  InitVar (dengan subjenisnya menjadi jenis bidang sebenarnya) memberi sinyal  @dataclass untuk tidak menjadikan bidang itu menjadi bidang kelas data, tetapi meneruskan data  __post_init__ sebagai argumen.

Dalam versi Book kelas kami ini  , kami tidak menyimpan  condition sebagai bidang dalam instance kelas. Kami hanya menggunakan conditionselama fase inisialisasi. Jika kami menemukan bahwa  condition itu disetel ke  "Discarded", kami disetel  shelf_id ke  None - tetapi kami tidak menyimpannya  condition di instance kelas.

Kapan menggunakan dataclasses Python - dan kapan tidak menggunakannya

Satu skenario umum untuk menggunakan dataclasses adalah sebagai pengganti nametuple. Dataclass menawarkan perilaku yang sama dan banyak lagi, dan mereka bisa dibuat tetap (seperti yang dinamai tuple) hanya dengan menggunakan  @dataclass(frozen=True) sebagai dekorator.

Another possible use case is replacing nested dictionaries, which can be clumsy to work with, with nested instances of dataclasses. If you have a dataclass Library, with a list property shelves, you could use a dataclass ReadingRoom to populate that list, and then add methods to make it easy to access nested items (e.g., a book on a shelf in a particular room).

But not every Python class needs to be a dataclass. If you’re creating a class mainly as a way to group together a bunch of static methods, rather than as a container for data, you don’t need to make it a dataclass. For instance, a common pattern with parsers is to have a class that takes in an abstract syntax tree, walks the tree, and dispatches calls to different methods in the class based on the node type. Because the parser class has very little data of its own, a dataclass isn’t useful here.

How to do more with Python

  • Get started with async in Python
  • How to use asyncio in Python
  • How to use PyInstaller to create Python executables
  • Cython tutorial: How to speed up Python
  • How to install Python the smart way
  • How to manage Python projects with Poetry
  • How to manage Python projects with Pipenv
  • Virtualenv dan venv: Penjelasan lingkungan virtual Python
  • Python virtualenv dan venv lakukan dan tidak boleh dilakukan
  • Penjelasan threading dan subproses Python
  • Cara menggunakan debugger Python
  • Cara menggunakan timeit untuk membuat profil kode Python
  • Cara menggunakan cProfile untuk membuat profil kode Python
  • Cara mengonversi Python ke JavaScript (dan kembali lagi)