Saya hanya mencoba merampingkan salah satu kelas saya dan telah memperkenalkan beberapa fungsi dengan gaya yang sama dengan pola desain kelas terbang .
Namun, saya agak bingung mengapa __init__
selalu dipanggil setelah __new__
. Saya tidak mengharapkan ini. Adakah yang bisa memberi tahu saya mengapa ini terjadi dan bagaimana saya bisa mengimplementasikan fungsi ini? (Selain menempatkan implementasi ke dalam __new__
yang terasa cukup gila.)
Ini sebuah contoh:
class A(object):
_dict = dict()
def __new__(cls):
if 'key' in A._dict:
print "EXISTS"
return A._dict['key']
else:
print "NEW"
return super(A, cls).__new__(cls)
def __init__(self):
print "INIT"
A._dict['key'] = self
print ""
a1 = A()
a2 = A()
a3 = A()
Output:
NEW
INIT
EXISTS
INIT
EXISTS
INIT
Mengapa?
Jawaban:
Dari April 2008 posting: Kapan menggunakan
__new__
vs__init__
? di mail.python.org.Anda harus mempertimbangkan bahwa apa yang Anda coba lakukan biasanya dilakukan dengan Pabrik dan itulah cara terbaik untuk melakukannya. Menggunakan
__new__
bukanlah solusi bersih yang baik jadi harap pertimbangkan penggunaan pabrik. Di sini Anda memiliki contoh pabrik yang bagus .sumber
__new__
di dalam kelas Pabrik, yang telah menjadi sangat bersih, jadi terima kasih atas masukan Anda.__new__
harus dibatasi secara ketat untuk kasus-kasus yang disebutkan. Saya merasa sangat berguna untuk mengimplementasikan pabrik kelas generik yang dapat dikembangkan - lihat jawaban saya atas pertanyaan Penggunaan yang tidak benar__new__
untuk menghasilkan kelas dengan Python? untuk contoh melakukannya.__new__
adalah metode kelas statis, sedangkan__init__
metode contoh.__new__
harus membuat instance terlebih dahulu, jadi__init__
dapat menginisialisasi. Catatan yang__init__
diambilself
sebagai parameter. Sampai Anda membuat contoh, tidak adaself
.Sekarang, saya kumpulkan, bahwa Anda mencoba menerapkan pola singleton dengan Python. Ada beberapa cara untuk melakukan itu.
Juga, pada Python 2.6, Anda dapat menggunakan dekorator kelas .
sumber
MyClass
yang terikat ke fungsi yang mengembalikan instance dari kelas asli. Tetapi sekarang tidak ada cara untuk merujuk ke kelas asli sama sekali, danMyClass
menjadi fungsi istirahatisinstance
/issubclass
pemeriksaan, akses ke atribut / metode kelas secara langsung sebagaiMyClass.something
, penamaan kelas dalamsuper
panggilan, dll, dll. Apakah itu masalah atau tidak tergantung pada kelas tempat Anda menerapkannya (dan program lainnya).Dalam sebagian besar bahasa OO yang terkenal, ekspresi seperti
SomeClass(arg1, arg2)
akan mengalokasikan instance baru, menginisialisasi atribut instance, dan kemudian mengembalikannya.Dalam sebagian besar bahasa OO yang terkenal, bagian "inisialisasi atribut instance" dapat dikustomisasi untuk setiap kelas dengan mendefinisikan konstruktor , yang pada dasarnya hanya blok kode yang beroperasi pada instance baru (menggunakan argumen yang disediakan untuk ekspresi konstruktor ) untuk mengatur kondisi awal apa pun yang diinginkan. Dalam Python, ini sesuai dengan metode kelas
__init__
.Python
__new__
tidak lebih dan tidak kurang dari kustomisasi per kelas yang sama dari bagian "alokasikan contoh baru". Ini tentu saja memungkinkan Anda untuk melakukan hal-hal yang tidak biasa seperti mengembalikan contoh yang ada daripada mengalokasikan yang baru. Jadi dengan Python, kita seharusnya tidak benar-benar menganggap bagian ini melibatkan alokasi; semua yang kami butuhkan adalah yang__new__
datang dengan contoh yang cocok dari suatu tempat.Tapi itu masih hanya setengah dari pekerjaan, dan tidak ada cara bagi sistem Python untuk mengetahui bahwa kadang-kadang Anda ingin menjalankan setengah pekerjaan lainnya (
__init__
) sesudahnya dan kadang-kadang tidak. Jika Anda menginginkan perilaku itu, Anda harus mengatakannya secara eksplisit.Seringkali, Anda dapat melakukan refactor sehingga Anda hanya perlu
__new__
, atau Anda tidak perlu__new__
, atau sehingga__init__
berperilaku berbeda pada objek yang sudah diinisialisasi. Tetapi jika Anda benar-benar ingin, Python benar-benar memungkinkan Anda untuk mendefinisikan kembali "pekerjaan", jadi ituSomeClass(arg1, arg2)
tidak selalu memanggil__new__
diikuti__init__
. Untuk melakukan ini, Anda perlu membuat metaclass, dan menentukan__call__
metodenya.Metaclass hanyalah kelas dari suatu kelas. Dan metode kelas
__call__
mengontrol apa yang terjadi ketika Anda memanggil instance kelas. Jadi metaclass '__call__
metode kontrol apa yang terjadi ketika Anda menelepon kelas; yaitu memungkinkan Anda untuk mendefinisikan ulang mekanisme pembuatan instance dari awal hingga selesai . Ini adalah tingkat di mana Anda dapat paling elegan menerapkan proses pembuatan instance yang sepenuhnya tidak standar seperti pola tunggal. Bahkan, dengan kurang dari 10 baris kode Anda dapat menerapkanSingleton
metaclass yang kemudian bahkan tidak mengharuskan Anda untuk futz dengan__new__
sama sekali , dan dapat mengubah setiap kelas lain-normal menjadi tunggal dengan hanya menambahkan__metaclass__ = Singleton
!Namun ini mungkin sihir yang lebih dalam daripada yang sebenarnya diperlukan untuk situasi ini!
sumber
Mengutip dokumentasi :
sumber
If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked.
Itu poin penting. Jika Anda mengembalikan contoh yang berbeda, sumber asli__init__
tidak pernah dipanggil.__new__
diperiksa untuk kelasnya, metode akan melempar kesalahan daripada membiarkan cek gagal lulus diam-diam, karena saya tidak mengerti mengapa Anda ingin mengembalikan apa pun kecuali objek kelas .Saya menyadari bahwa pertanyaan ini cukup lama tetapi saya memiliki masalah serupa. Berikut ini melakukan apa yang saya inginkan:
Saya menggunakan halaman ini sebagai sumber daya http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html
sumber
Saya pikir jawaban sederhana untuk pertanyaan ini adalah bahwa, jika
__new__
mengembalikan nilai yang sama dengan kelas,__init__
fungsi dijalankan, jika tidak maka tidak akan. Dalam hal ini, kode Anda kembaliA._dict('key')
dengan kelas yang samacls
, sehingga__init__
akan dieksekusi.sumber
Ketika
__new__
mengembalikan instance dari kelas yang sama,__init__
dijalankan setelahnya pada objek yang dikembalikan. Yaitu Anda TIDAK bisa menggunakan__new__
untuk mencegah__init__
dijalankan. Bahkan jika Anda mengembalikan objek yang dibuat sebelumnya__new__
, itu akan menjadi dua kali lipat (tiga kali lipat, dll ...) diinisialisasi__init__
berulang kali.Berikut ini adalah pendekatan umum untuk pola Singleton yang memperluas jawaban vartec di atas dan memperbaikinya:
Kisah lengkap ada di sini .
Pendekatan lain, yang sebenarnya melibatkan
__new__
adalah dengan menggunakan metode class:Harap perhatikan, bahwa dengan pendekatan ini Anda perlu menghias SEMUA metode Anda
@classmethod
, karena Anda tidak akan pernah menggunakan contoh nyataMyClass
.sumber
Mengacu pada dokumen ini :
Mengenai apa yang ingin Anda capai, ada juga info dokumen yang sama tentang pola Singleton
Anda juga dapat menggunakan implementasi ini dari PEP 318, menggunakan dekorator
sumber
init
dari__new__
suara benar-benar aneh. Ini adalah untuk apa metaclasses.output:
NB Sebagai
M._dict
properti efek samping secara otomatis dapat diakses dariA
karenaA._dict
itu berhati-hatilah untuk tidak menimpanya secara kebetulan.sumber
__init__
metode yang menetapkancls._dict = {}
. Anda mungkin tidak ingin kamus dibagikan oleh semua kelas dari metatype ini (tetapi +1 untuk ide).__new__ harus mengembalikan instance kelas baru yang kosong. __init__ kemudian dipanggil untuk menginisialisasi instance itu. Anda tidak menelepon __init__ dalam "NEW" kasus __new__, jadi Anda dipanggil untuk itu. Kode yang memanggil
__new__
tidak melacak apakah __init__ telah dipanggil pada contoh tertentu atau tidak juga tidak seharusnya, karena Anda melakukan sesuatu yang sangat tidak biasa di sini.Anda bisa menambahkan atribut ke objek dalam fungsi __init__ untuk menunjukkan bahwa itu telah diinisialisasi. Periksa keberadaan atribut itu sebagai hal pertama dalam __init__ dan jangan melanjutkan lebih jauh jika sudah.
sumber
Pembaruan untuk jawaban @AntonyHatchkins, Anda mungkin ingin kamus contoh yang terpisah untuk setiap kelas metatype, yang berarti bahwa Anda harus memiliki
__init__
metode dalam metaclass untuk menginisialisasi objek kelas Anda dengan kamus itu alih-alih menjadikannya global di semua kelas.Saya telah pergi ke depan dan memperbarui kode asli dengan
__init__
metode dan mengubah sintaks ke notasi Python 3 (no-arg panggilan kesuper
dan metaclass di argumen kelas bukan sebagai atribut).Either way, poin penting di sini adalah bahwa inisialisasi kelas Anda (
__call__
metode) tidak akan mengeksekusi baik__new__
atau__init__
jika kunci ditemukan. Ini jauh lebih bersih daripada menggunakan__new__
, yang mengharuskan Anda untuk menandai objek jika Anda ingin melewati default__init__
langkah .sumber
Menggali lebih dalam ke dalamnya!
Jenis kelas generik dalam CPython adalah
type
dan kelas dasarnya adalahObject
(Kecuali jika Anda secara eksplisit mendefinisikan kelas basis lain seperti metaclass). Urutan panggilan tingkat rendah dapat ditemukan di sini . Metode pertama yang dipanggil adalahtype_call
yang kemudian memanggiltp_new
dan kemudiantp_init
.Bagian yang menarik di sini adalah yang
tp_new
akan memanggilObject
metode baru (kelas dasar)object_new
yang melakukan atp_alloc
(PyType_GenericAlloc
) yang mengalokasikan memori untuk objek :)Pada titik itu objek dibuat dalam memori dan kemudian
__init__
metode dipanggil. Jika__init__
tidak diimplementasikan di kelas Anda makaobject_init
dipanggil dan tidak melakukan apa-apa :)Kemudian
type_call
kembalikan objek yang terikat ke variabel Anda.sumber
Orang harus melihat
__init__
sebagai konstruktor sederhana dalam bahasa OO tradisional. Misalnya, jika Anda terbiasa dengan Java atau C ++, konstruktor meneruskan sebuah pointer ke instansnya sendiri secara implisit. Dalam kasus Jawa, itu adalahthis
variabel. Jika seseorang memeriksa kode byte yang dihasilkan untuk Java, orang akan melihat dua panggilan. Panggilan pertama adalah metode "baru", dan panggilan berikutnya adalah metode init (yang merupakan panggilan aktual ke konstruktor yang ditentukan pengguna). Proses dua langkah ini memungkinkan pembuatan instance aktual sebelum memanggil metode konstruktor dari kelas yang hanya metode lain dari instance itu.Sekarang, dalam kasus Python,
__new__
adalah fasilitas tambahan yang dapat diakses oleh pengguna. Java tidak menyediakan fleksibilitas itu, karena sifatnya yang diketik. Jika suatu bahasa menyediakan fasilitas itu, maka implementor__new__
dapat melakukan banyak hal dalam metode itu sebelum mengembalikan instance, termasuk membuat instance yang benar-benar baru dari objek yang tidak terkait dalam beberapa kasus. Dan, pendekatan ini juga bekerja dengan baik terutama untuk tipe yang tidak berubah dalam kasus Python.sumber
Saya pikir analogi C ++ akan berguna di sini:
__new__
cukup mengalokasikan memori untuk objek. Variabel instance dari suatu objek membutuhkan memori untuk menahannya, dan inilah yang__new__
akan dilakukan langkahnya .__init__
inisialisasi variabel internal objek ke nilai tertentu (bisa menjadi default).sumber
The
__init__
disebut setelah__new__
sehingga ketika Anda menimpa di subclass, kode menambahkan Anda akan tetap dipanggil.Jika Anda mencoba untuk subkelas kelas yang sudah memiliki
__new__
, seseorang yang tidak menyadari ini mungkin mulai dengan mengadaptasi__init__
dan meneruskan panggilan ke bawah ke subkelas__init__
. Konvensi panggilan__init__
setelah ini__new__
membantu pekerjaan itu seperti yang diharapkan.The
__init__
masih perlu untuk memungkinkan setiap parameter superclass__new__
diperlukan, tetapi gagal untuk melakukannya biasanya akan membuat kesalahan runtime yang jelas. Dan__new__
mungkin harus secara eksplisit mengizinkan*args
dan '** kw', untuk membuatnya jelas bahwa ekstensi itu OK.Biasanya bentuk yang buruk memiliki keduanya
__new__
dan__init__
di kelas yang sama pada tingkat warisan yang sama, karena perilaku yang digambarkan oleh poster aslinya.sumber
Tidak banyak alasan selain itu hanya dilakukan seperti itu.
__new__
tidak memiliki tanggung jawab menginisialisasi kelas, beberapa metode lain tidak (__call__
, mungkin - saya tidak tahu pasti).Anda bisa tidak
__init__
melakukan apa-apa jika sudah diinisialisasi, atau Anda bisa menulis metaclass baru dengan yang baru__call__
yang memanggil__init__
instance baru, dan sebaliknya hanya mengembalikan__new__(...)
.sumber
Alasan sederhananya adalah bahwa yang baru digunakan untuk membuat instance, sedangkan init digunakan untuk menginisialisasi instance. Sebelum menginisialisasi, instance harus dibuat terlebih dahulu. Itu sebabnya baru harus dipanggil sebelum init .
sumber
Sekarang saya punya masalah yang sama, dan untuk beberapa alasan saya memutuskan untuk menghindari dekorator, pabrik, dan kacamata. Saya melakukannya seperti ini:
File utama
Kelas contoh
Digunakan
Cobalah online!
sumber