Apa perbedaan antara kelas gaya lama dan gaya baru di Python? Kapan saya harus menggunakan yang satu atau yang lain?
Apa perbedaan antara kelas gaya lama dan gaya baru di Python? Kapan saya harus menggunakan yang satu atau yang lain?
Jawaban:
Dari kelas baru dan klasik :
Hingga Python 2.1, kelas gaya lama adalah satu-satunya rasa yang tersedia bagi pengguna.
Konsep kelas (gaya lama) tidak terkait dengan konsep tipe: jika
x
adalah turunan dari kelas gaya lama, maka kelas tersebut akanx.__class__
ditunjukx
, tetapitype(x)
selalu<type 'instance'>
.Ini mencerminkan fakta bahwa semua instance gaya lama, terlepas dari kelasnya, diimplementasikan dengan tipe built-in tunggal, yang disebut instance.
Kelas-kelas gaya baru diperkenalkan dalam Python 2.2 untuk menyatukan konsep kelas dan tipe . Kelas gaya baru hanyalah tipe yang ditentukan pengguna, tidak lebih, tidak kurang.
Jika x adalah turunan dari kelas gaya baru, maka
type(x)
biasanya sama denganx.__class__
(meskipun ini tidak dijamin - turunan kelas gaya baru diizinkan untuk mengesampingkan nilai yang dikembalikan untukx.__class__
).Motivasi utama untuk memperkenalkan kelas gaya baru adalah untuk menyediakan model objek terpadu dengan meta-model penuh .
Ini juga memiliki sejumlah manfaat langsung, seperti kemampuan untuk mensubklasifikasikan sebagian besar tipe bawaan, atau pengenalan "deskriptor", yang memungkinkan properti yang dihitung.
Untuk alasan kompatibilitas, kelas secara default masih bergaya lama .
Kelas gaya baru dibuat dengan menentukan kelas gaya baru lainnya (yaitu tipe) sebagai kelas induk, atau objek "tipe tingkat atas" jika tidak diperlukan induk lain.
Perilaku kelas gaya baru berbeda dari kelas gaya lama dalam sejumlah detail penting selain jenis apa yang dikembalikan.
Beberapa perubahan ini sangat mendasar bagi model objek baru, seperti cara metode khusus dipanggil. Lainnya adalah "perbaikan" yang tidak dapat diimplementasikan sebelumnya karena masalah kompatibilitas, seperti urutan resolusi metode dalam hal pewarisan berganda.
Python 3 hanya memiliki kelas gaya baru .
Tidak masalah apakah Anda subkelas dari
object
atau tidak, kelas adalah gaya baru di Python 3.
super()
tidak berfungsi pada kelas gaya lama. Belum lagi, seperti kata artikel itu, ada perbaikan mendasar, seperti MRO, dan metode khusus, yang lebih dari alasan yang baik untuk menggunakannya.
Dari segi deklarasi:
Kelas gaya baru mewarisi dari objek , atau dari kelas gaya baru lainnya.
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
Kelas gaya lama tidak.
class OldStyleClass():
pass
Python 3 Catatan:
Python 3 tidak mendukung kelas gaya lama, jadi bentuk apa pun yang disebutkan di atas menghasilkan kelas gaya baru.
object
.
class AnotherOldStyleClass: pass
class A: pass
dan class A(): pass
sangat setara. Yang pertama berarti "A tidak mewarisi kelas induk mana pun" dan yang kedua berarti "A warisan dari kelas induk tidak" . Itu sangat mirip dengan not is
danis not
Perubahan perilaku penting antara kelas gaya lama dan baru
Exception
(contoh di bawah)__slots__
ditambahkanItu disebutkan dalam jawaban lain, tetapi inilah contoh konkret dari perbedaan antara MRO klasik dan C3 MRO (digunakan dalam kelas gaya baru).
Pertanyaannya adalah urutan di mana atribut (yang mencakup metode dan variabel anggota) dicari dalam beberapa pewarisan.
Kelas klasik melakukan pencarian mendalam-pertama dari kiri ke kanan. Berhenti pada pertandingan pertama. Mereka tidak memiliki __mro__
atribut.
class C: i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 0
assert C21().i == 2
try:
C12.__mro__
except AttributeError:
pass
else:
assert False
Kelas-kelas gaya baru MRO lebih rumit untuk disintesis dalam satu kalimat bahasa Inggris. Dijelaskan secara rinci di sini . Salah satu propertinya adalah bahwa kelas dasar hanya dicari setelah semua kelas turunannya telah. Mereka memiliki __mro__
atribut yang menunjukkan urutan pencarian.
class C(object): i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 2
assert C21().i == 2
assert C12.__mro__ == (C12, C1, C2, C, object)
assert C21.__mro__ == (C21, C2, C1, C, object)
Exception
Sekitar Python 2.5 banyak kelas bisa dinaikkan, dan sekitar Python 2.6 ini dihapus. Pada Python 2.7.3:
# OK, old:
class Old: pass
try:
raise Old()
except Old:
pass
else:
assert False
# TypeError, new not derived from `Exception`.
class New(object): pass
try:
raise New()
except TypeError:
pass
else:
assert False
# OK, derived from `Exception`.
class New(Exception): pass
try:
raise New()
except New:
pass
else:
assert False
# `'str'` is a new style object, so you can't raise it:
try:
raise 'str'
except TypeError:
pass
else:
assert False
Kelas gaya lama masih sedikit lebih cepat untuk pencarian atribut. Ini biasanya tidak penting, tetapi mungkin berguna dalam kode Python 2.x yang peka terhadap kinerja:
Dalam [3]: kelas A: ...: def __init __ (mandiri): ...: self.a = 'hai disana' ...: Dalam [4]: kelas B (objek): ...: def __init __ (mandiri): ...: self.a = 'hai disana' ...: Dalam [6]: aobj = A () Dalam [7]: bobj = B () Dalam [8]:% timeit aobj.a 10000000 loop, terbaik 3: 78,7 ns per loop Dalam [10]:% timeit bobj.a 10000000 loop, terbaik 3: 86,9 ns per loop
%timeit aobj.a
10000000 loops, best of 3: 66.1 ns per loop
%timeit bobj.a
10000000 loops, best of 3: 53.9 ns per loop
Guido telah menulis The Inside Story di New-Style Classes , sebuah artikel yang sangat bagus tentang kelas gaya baru dan gaya lama dengan Python.
Python 3 hanya memiliki kelas gaya baru. Bahkan jika Anda menulis 'kelas gaya lama', itu secara implisit berasal dari object
.
Kelas-kelas gaya baru memiliki beberapa fitur lanjutan yang kurang di kelas-kelas gaya lama, seperti super
, mro C3 baru , beberapa metode magis, dll.
Inilah perbedaan yang sangat praktis, benar / salah. Satu-satunya perbedaan antara dua versi kode berikut adalah bahwa dalam versi kedua, Orang mewarisi dari objek . Selain itu, kedua versi itu identik, tetapi dengan hasil yang berbeda:
Kelas gaya lama
class Person():
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed1 is ahmed2
print ahmed1
print ahmed2
>>> False
<__main__.Person instance at 0xb74acf8c>
<__main__.Person instance at 0xb74ac6cc>
>>>
Kelas gaya baru
class Person(object):
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed2 is ahmed1
print ahmed1
print ahmed2
>>> True
<__main__.Person object at 0xb74ac66c>
<__main__.Person object at 0xb74ac66c>
>>>
_names_cache
adalah kamus yang menyimpan (menyimpan untuk pengambilan di masa depan) setiap nama yang Anda berikan Person.__new__
. Metode setdefault (didefinisikan dalam kamus apa pun) membutuhkan dua argumen: kunci dan nilai. Jika kunci ada dalam dikt, ia akan mengembalikan nilainya. Jika tidak ada dict, itu akan mengaturnya terlebih dahulu ke nilai yang diteruskan sebagai argumen kedua dan kemudian mengembalikannya.
__new__()
selalu dipanggil, dan selalu membangun objek baru, lalu melemparkannya. Dalam hal ini a if
lebih disukai daripada .setdefault()
.
__new__
sebenarnya bukan sesuatu untuk kelas gaya lama, itu tidak digunakan dalam konstruksi contoh (itu hanya nama acak yang terlihat istimewa, seperti mendefinisikan __spam__
). Jadi membangun kelas gaya lama hanya memanggil __init__
, sedangkan konstruksi gaya baru memanggil __new__
(menyatu dengan singleton misalnya dengan nama) untuk membangun, dan __init__
menginisialisasi.
Kelas-kelas gaya baru mewarisi dari object
dan harus ditulis seperti itu di Python 2.2 dan seterusnya (yaitu class Classname(object):
bukannya class Classname:
). Perubahan inti adalah menyatukan jenis dan kelas, dan efek samping yang bagus dari ini adalah memungkinkan Anda mewarisi dari tipe bawaan.
Baca descrintro untuk lebih jelasnya.
Kelas gaya baru dapat menggunakan di super(Foo, self)
mana Foo
kelas dan self
contohnya.
super(type[, object-or-type])
Mengembalikan objek proxy yang mendelegasikan panggilan metode ke kelas tipe induk atau saudara kandung. Ini berguna untuk mengakses metode yang diwariskan yang telah ditimpa dalam kelas. Urutan pencarian sama dengan yang digunakan oleh getattr () kecuali bahwa tipe itu sendiri dilewati.
Dan dengan Python 3.x Anda cukup menggunakan super()
di dalam kelas tanpa parameter apa pun.
type(x)
. Jika saya tidak mensubklasifikasikan tipe bawaan, maka sepertinya tidak ada keuntungan yang bisa saya lihat dari kelas-kelas gaya baru. Ada kelemahan, yaitu mengetik ekstra(object)
.