Katakanlah Anda memiliki kelas Foo :
class Foo(object): def __init__(self, x, y=0): self.x = x self.y = y
Apa yang terjadi ketika Anda membuat objeknya?
f = Foo(1, y=2)
Metode mana yang dipanggil pertama kali dengan panggilan ini ke Foo ? Kebanyakan pendatang baru, dan mungkin beberapa pythonis berpengalaman, akan segera menjawab: "metode __init__ ." Tetapi jika Anda perhatikan dengan saksama cuplikan di atas, akan segera menjadi jelas bahwa jawaban seperti itu salah.
__init__ tidak mengembalikan hasil apa pun, dan Foo (1, y = 2) , sebaliknya, mengembalikan turunan kelas . Selain itu, __init__ menganggap diri sebagai parameter pertama, yang tidak terjadi ketika memanggil Foo (1, y = 2) . Membuat instance sedikit lebih rumit, yang akan kita bicarakan di artikel ini.
Prosedur Pembuatan Objek
Instansiasi python terdiri dari beberapa tahap. Memahami setiap langkah membuat kita sedikit lebih dekat untuk memahami bahasa secara keseluruhan. Foo adalah kelas, tetapi dalam Python, kelas juga objek! Kelas, fungsi, metode dan instance adalah semua objek, dan setiap kali Anda menempatkan tanda kurung di belakang namanya, Anda memanggil metode __call__ mereka. Jadi Foo (1, y = 2) adalah setara dengan Foo. Panggilan __ (1, y = 2) . Selain itu, metode __call__ dideklarasikan di kelas objek Foo . Apa kelas objek Foo ?
>>> Foo.__class__ <class 'type'>
Jadi kelas Foo adalah turunan dari kelas tipe dan memanggil metode __call__ yang terakhir mengembalikan kelas Foo . Sekarang mari kita lihat apa metode __call__ dari kelas tipe . Di bawah ini adalah implementasinya dalam C di CPython dan di PyPy. Jika Anda bosan menontonnya, gulir sedikit lebih jauh untuk menemukan versi yang disederhanakan:
Cpython
Tautan ke sumber .
static PyObject * type_call(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *obj; if (type->tp_new == NULL) { PyErr_Format(PyExc_TypeError, "cannot create '%.100s' instances", type->tp_name); return NULL; } obj = type->tp_new(type, args, kwds); obj = _Py_CheckFunctionResult((PyObject*)type, obj, NULL); if (obj == NULL) return NULL; if (type == &PyType_Type && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 && (kwds == NULL || (PyDict_Check(kwds) && PyDict_Size(kwds) == 0))) return obj; if (!PyType_IsSubtype(Py_TYPE(obj), type)) return obj; type = Py_TYPE(obj); if (type->tp_init != NULL) { int res = type->tp_init(obj, args, kwds); if (res < 0) { assert(PyErr_Occurred()); Py_DECREF(obj); obj = NULL; } else { assert(!PyErr_Occurred()); } } return obj; }
Pypy
Tautan ke sumber .
def descr_call(self, space, __args__): promote(self)
Jika Anda lupa tentang semua jenis pemeriksaan kesalahan, kode di atas kira-kira setara dengan ini:
def __call__(obj_type, *args, **kwargs): obj = obj_type.__new__(*args, **kwargs) if obj is not None and issubclass(obj, obj_type): obj.__init__(*args, **kwargs) return obj
__new__ mengalokasikan memori untuk objek "kosong" dan memanggil __init__ untuk menginisialisasi.
Untuk meringkas:
- Foo (* args, ** kwargs) setara dengan Foo .__ panggil __ (* args, ** kwargs) .
- Karena objek Foo adalah turunan dari kelas tipe , memanggil Foo .__ panggilan __ (* args, ** kwargs) setara dengan mengetikkan .__ panggilan __ (Foo, * args, ** kwargs) .
- ketik .__ panggilan __ (Foo, * args, ** kwargs) memanggil metode .__ baru __ (Foo, * args, ** kwargs) , yang mengembalikan obj .
- obj diinisialisasi dengan memanggil obj .__ init __ (* args, ** kwargs) .
- Hasil dari seluruh proses adalah objek yang diinisialisasi.
Kustomisasi
Sekarang mari kita mengalihkan perhatian kita ke __new__ . Metode ini mengalokasikan memori untuk objek dan mengembalikannya. Anda bebas untuk menyesuaikan proses ini dengan berbagai cara. Perlu dicatat bahwa meskipun __new__ adalah metode statis, Anda tidak perlu mendeklarasikannya menggunakan @staticmethod : penerjemah memperlakukan __new__ sebagai kasus khusus.
Contoh umum dari penggantian __new__ baru adalah menciptakan Singleton:
class Singleton(object): _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls, *args, **kwargs) return cls._instance
Lalu:
>>> s1 = Singleton() ... s2 = Singleton() ... s1 is s2 True
Perhatikan bahwa __init__ akan dipanggil setiap kali Singleton () dipanggil, jadi harus berhati-hati.
Contoh lain dari penggantian __new__ baru adalah penerapan pola Borg (βBorgβ) :
class Borg(object): _dict = None def __new__(cls, *args, **kwargs): obj = super().__new__(cls, *args, **kwargs) if cls._dict is None: cls._dict = obj.__dict__ else: obj.__dict__ = cls._dict return obj
Lalu:
>>> b1 = Borg() ... b2 = Borg() ... b1 is b2 False >>> b1.x = 8 ... b2.x 8
Ingatlah bahwa meskipun contoh di atas menunjukkan kemungkinan mengganti __new__ baru , ini tidak berarti bahwa itu harus digunakan:
__new__ adalah salah satu korban pelecehan yang paling sering. Apa yang dapat dilakukan dengan mengganti metode ini adalah yang paling sering dicapai dengan cara lain. Namun, ketika itu benar-benar diperlukan, __baru__ adalah alat yang sangat berguna dan kuat.
- Arion Sprag, Tua yang Terlupakan dengan Python
Satu jarang menemukan masalah dalam Python, di mana solusi terbaik adalah menggunakan __new__ . Tetapi ketika Anda memiliki palu, setiap masalah mulai terlihat seperti paku, jadi selalu lebih suka menggunakan alat yang paling kuat untuk menggunakan yang paling cocok .