Pertanyaan yang akan saya tanyakan tampaknya merupakan duplikat dari penggunaan Python untuk __new__ dan __init__? , tetapi terlepas dari itu, masih belum jelas bagi saya apa perbedaan praktis antara __new__
dan __init__
.
Sebelum Anda buru-buru memberi tahu saya bahwa __new__
ini untuk membuat objek dan __init__
untuk menginisialisasi objek, izinkan saya menjelaskan: Saya mengerti. Pada kenyataannya, perbedaan itu cukup alami bagi saya, karena saya memiliki pengalaman dalam C ++ di mana kami memiliki penempatan baru , yang juga memisahkan alokasi objek dari inisialisasi.
The Python C API tutorial menjelaskan seperti ini:
Anggota baru bertanggung jawab untuk membuat (bukan menginisialisasi) objek jenis. Itu diekspos dalam Python sebagai
__new__()
metode. ... Salah satu alasan untuk menerapkan metode baru adalah untuk memastikan nilai awal variabel instan .
Jadi, ya - saya mendapatkan apa yang __new__
dilakukan, tetapi meskipun demikian, saya masih tidak mengerti mengapa ini berguna dalam Python. Contoh yang diberikan mengatakan bahwa __new__
mungkin berguna jika Anda ingin "memastikan nilai awal variabel instan". Nah, bukankah itu tepatnya yang __init__
akan dilakukan?
Dalam tutorial C API, contoh ditampilkan di mana Tipe baru (disebut "Noddy") dibuat, dan fungsi Type __new__
didefinisikan. Tipe Noddy berisi anggota string yang disebut first
, dan anggota string ini diinisialisasi ke string kosong seperti:
static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
.....
self->first = PyString_FromString("");
if (self->first == NULL)
{
Py_DECREF(self);
return NULL;
}
.....
}
Perhatikan bahwa tanpa __new__
metode yang didefinisikan di sini, kita harus menggunakan PyType_GenericNew
, yang hanya menginisialisasi semua anggota variabel instan ke NULL. Jadi satu-satunya manfaat dari __new__
metode ini adalah bahwa variabel instance akan mulai sebagai string kosong, sebagai lawan dari NULL. Tetapi mengapa ini pernah berguna, karena jika kita peduli untuk memastikan variabel instan kita diinisialisasi ke beberapa nilai default, kita bisa saja melakukannya dalam __init__
metode ini?
sumber
__new__
bukan boilerplate, karena boilerplate tidak pernah berubah. Terkadang Anda perlu mengganti kode tertentu dengan sesuatu yang berbeda.super
panggilan) dan mengembalikan instance adalah bagian penting dari setiap__new__
implementasi, dan "boilerplate" yang saya maksud. Sebaliknya,pass
adalah implementasi yang valid untuk__init__
- tidak ada perilaku yang diperlukan sama sekali.Mungkin ada kegunaan lain untuk
__new__
tetapi ada satu yang benar-benar jelas: Anda tidak dapat mensubklasifikasikan tipe abadi tanpa menggunakan__new__
. Jadi misalnya, katakan Anda ingin membuat subkelas tupel yang hanya dapat berisi nilai integral antara 0 dansize
.Anda tidak dapat melakukan ini dengan
__init__
- jika Anda mencoba untuk memodifikasiself
dalam__init__
, penafsir akan mengeluh bahwa Anda sedang berusaha untuk memodifikasi objek abadi.sumber
__new__
. Kami memberikancls
secara eksplisit kepada__new__
karena, seperti yang Anda baca di sini__new__
selalu memerlukan jenis sebagai argumen pertama. Kemudian mengembalikan sebuah instance dari tipe itu. Jadi kami tidak mengembalikan instance dari superclass - kami mengembalikan instancecls
. Dalam hal ini, itu sama seperti jika kita katakantuple.__new__(ModularTuple, tup)
.__new__()
dapat mengembalikan objek tipe selain dari kelas yang terikat padanya.__init__()
hanya menginisialisasi instance kelas yang ada.sumber
__init__
metode yang berisi kode yang miripself.__class__ = type(...)
. Itu menyebabkan objek menjadi kelas yang berbeda dari yang Anda pikir Anda buat. Saya sebenarnya tidak bisa mengubahnya menjadiint
seperti yang Anda lakukan ... Saya mendapatkan kesalahan tentang tipe tumpukan atau sesuatu ... tapi contoh saya menugaskannya ke karya kelas yang dibuat secara dinamis.__init__()
dipanggil. Misalnya, dalam jawaban lonetwin , baikTriangle.__init__()
atauSquare.__init__()
secara otomatis dipanggil tergantung pada jenis yang__new__()
dikembalikan. Dari apa yang Anda katakan dalam jawaban Anda (dan saya sudah membaca ini di tempat lain), sepertinya tidak satu pun dari keduanya karenaShape.__new__()
tidak mengembalikan instance daricls
(atau salah satu dari subkelasnya).__init__()
Metode dalam jawaban lonetwin dipanggil saat masing-masing objek dipakai (yaitu ketika metode mereka__new__()
kembali), bukan ketikaShape.__new__()
kembali.Shape.__init__()
(jika ada) tidak akan dipanggil. Sekarang semuanya lebih masuk akal ...:¬)
Bukan jawaban yang lengkap tapi mungkin sesuatu yang menggambarkan perbedaannya.
__new__
akan selalu dipanggil ketika suatu objek harus dibuat. Ada beberapa situasi di mana__init__
tidak akan dipanggil. Salah satu contoh adalah ketika Anda menghapus objek dari file acar, mereka akan dialokasikan (__new__
) tetapi tidak diinisialisasi (__init__
).sumber
__new__
metode ini adalah membuat (ini menyiratkan alokasi memori) sebuah instance dari kelas dan mengembalikannya. Inisialisasi adalah langkah terpisah dan itulah yang biasanya terlihat oleh pengguna. Silakan ajukan pertanyaan terpisah jika ada masalah khusus yang Anda hadapi.Hanya ingin menambahkan kata tentang maksud (sebagai lawan dari perilaku) mendefinisikan
__new__
versus__init__
.Saya menemukan pertanyaan ini (antara lain) ketika saya mencoba memahami cara terbaik untuk mendefinisikan pabrik kelas. Saya menyadari bahwa salah satu cara yang
__new__
secara konseptual berbeda dari__init__
adalah kenyataan bahwa manfaat__new__
persis seperti yang dinyatakan dalam pertanyaan:Mempertimbangkan skenario yang dinyatakan, kami peduli tentang nilai-nilai awal dari variabel instan ketika instance sebenarnya adalah kelas itu sendiri. Jadi, jika kita secara dinamis membuat objek kelas saat runtime dan kita perlu mendefinisikan / mengendalikan sesuatu yang istimewa tentang instance kelas ini yang sedang dibuat, kita akan mendefinisikan kondisi / properti ini dalam
__new__
metode metaclass.Saya bingung tentang hal ini sampai saya benar-benar memikirkan tentang penerapan konsep dan bukan hanya maknanya. Berikut ini contoh yang diharapkan akan membuat perbedaan menjadi jelas:
Perhatikan ini hanyalah contoh demonstratif. Ada beberapa cara untuk mendapatkan solusi tanpa menggunakan pendekatan pabrik kelas seperti di atas dan bahkan jika kita memang memilih untuk mengimplementasikan solusi dengan cara ini, ada sedikit peringatan yang ditinggalkan demi singkatnya (misalnya, mendeklarasikan metaclass secara eksplisit )
Jika Anda membuat kelas reguler (alias non-metaclass), maka
__new__
tidak terlalu masuk akal kecuali itu adalah kasus khusus seperti skenario yang bisa berubah versus yang tidak dapat diubah dalam jawaban jawaban ncoghlan (yang pada dasarnya adalah contoh yang lebih spesifik dari konsep mendefinisikan nilai / properti awal dari kelas / jenis yang dibuat via__new__
untuk kemudian diinisialisasi melalui__init__
).sumber