Apa perbedaan antara kelas gaya lama dan gaya baru di Python?

994

Apa perbedaan antara kelas gaya lama dan gaya baru di Python? Kapan saya harus menggunakan yang satu atau yang lain?

Hanya baca
sumber

Jawaban:

560

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 xadalah turunan dari kelas gaya lama, maka kelas tersebut akan x.__class__ ditunjuk x, tetapi type(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 dengan x.__class__(meskipun ini tidak dijamin - turunan kelas gaya baru diizinkan untuk mengesampingkan nilai yang dikembalikan untuk x.__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 objectatau tidak, kelas adalah gaya baru di Python 3.

Projesh Bhoumik
sumber
41
Tak satu pun dari perbedaan ini terdengar seperti alasan kuat untuk menggunakan kelas gaya baru, namun semua orang mengatakan Anda harus selalu menggunakan gaya baru. Jika saya menggunakan bebek mengetik seperti yang seharusnya, saya tidak perlu menggunakannya 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).
Rekursif
78
Fitur tertentu seperti 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.
John Doe
21
@ Pengguna: Kelas-kelas lama berperilaku sama di 2,7 seperti yang mereka lakukan di 2.1-dan, karena beberapa orang bahkan mengingat quirks, dan dokumentasi tidak lagi membahas sebagian besar dari mereka, mereka bahkan lebih buruk. Kutipan dokumentasi di atas secara langsung mengatakan ini: ada "perbaikan" yang tidak dapat diimplementasikan pada kelas gaya lama. Kecuali Anda ingin mengalami quirks yang tidak pernah ditangani oleh orang lain sejak Python 2.1, dan bahwa dokumentasi tidak lagi menjelaskan, jangan gunakan kelas gaya lama.
abarnert
10
Berikut adalah contoh dari kekhasan yang mungkin Anda temui
KT.
5
Bagi siapa pun yang bertanya-tanya, alasan yang baik untuk secara eksplisit mewarisi dari objek di Python 3 adalah membuat dukungan beberapa versi Python lebih mudah.
jpmc26
308

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.

Mark Harrison
sumber
24
jika kelas gaya baru mewarisi dari kelas gaya baru lain, maka dengan ekstensi, itu mewarisi dari object.
aaronasterling
2
Apakah ini contoh yang salah dari kelas python gaya lama? class AnotherOldStyleClass: pass
Ankur Agarwal
11
@ abc Saya percaya itu class A: passdan class A(): passsangat 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 isdanis not
eyquem
5
Sama seperti catatan tambahan, untuk 3.X, warisan "objek" secara otomatis diasumsikan (artinya kita tidak punya cara untuk tidak mewarisi "objek" dalam 3.X). Untuk alasan kompatibilitas ke belakang, tidak buruk untuk menyimpan "(objek)" di sana.
Yo Hsiao
1
Jika kita akan mendapatkan teknis tentang kelas bawaan, jawaban ini harus mencatat bahwa Anda dapat membuat kelas gaya lama lain dengan mewarisi dari kelas gaya lama. (Seperti yang tertulis, jawaban ini membuat pengguna mempertanyakan apakah Anda dapat mewarisi dari kelas gaya lama. Anda dapat.)
jpmc26
224

Perubahan perilaku penting antara kelas gaya lama dan baru

  • super ditambahkan
  • MRO berubah (dijelaskan di bawah)
  • deskriptor ditambahkan
  • objek kelas gaya baru tidak dapat dinaikkan kecuali berasal dari Exception(contoh di bawah)
  • __slots__ ditambahkan

MRO (Urutan Resolusi Metode) berubah

Itu 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)

Objek kelas gaya baru tidak dapat dinaikkan kecuali berasal dari 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
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
8
Ringkasan yang jelas dan bagus, terima kasih. Ketika Anda mengatakan "sulit untuk dijelaskan dalam bahasa Inggris" Saya pikir Anda menggambarkan pencarian kedalaman-pertama postorder yang bertentangan dengan kelas gaya lama yang menggunakan pencarian kedalaman-pertama preorder. (preorder berarti kita mencari diri kita sendiri sebelum anak pertama kita dan postorder berarti kita mencari diri kita sendiri setelah anak terakhir kita).
Steve Carter
40

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
xioxox
sumber
5
Menarik bahwa Anda perhatikan dalam praktek, saya baru saja membaca bahwa ini adalah karena kelas gaya baru, setelah mereka menemukan atribut dalam contoh dict, harus melakukan pencarian tambahan untuk mengetahui apakah itu adalah deskripsi, yaitu, ia memiliki dapatkan metode yang perlu dipanggil untuk mendapatkan nilai yang akan dikembalikan. Kelas gaya lama mengembalikan objek yang ditemukan tanpa perhitungan tambahan (tapi kemudian tidak mendukung deskriptor). Anda dapat membaca lebih lanjut di pos luar biasa ini oleh Guido python-history.blogspot.co.uk/2010/06/… , khususnya bagian slot
xuloChavez
1
tampaknya tidak benar dengan CPython 2.7.2:%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
Benedikt Waldvogel
1
Masih lebih cepat untuk aobj di CPython 2.7.2 di x86-64 Linux untuk saya.
xioxox
41
Mungkin ide yang buruk untuk mengandalkan kode Python murni untuk aplikasi yang sensitif terhadap kinerja. Tidak ada yang mengatakan: "Saya perlu kode cepat jadi saya akan menggunakan kelas Python gaya lama." Numpy tidak dihitung sebagai Python murni.
Phillip Cloud
juga di IPython 2.7.6, ini tidak benar. '' '' 477 ns vs. 456 ns per loop '' ''
kmonsoor
37

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.

Xiao Hanyu
sumber
24

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:

  1. 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>
    >>>
    
  2. 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>
    >>>
ychaouche
sumber
2
apa yang dilakukan '_names_cache'? Bisakah Anda membagikan referensi?
Muatik
4
_names_cacheadalah 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.
ychaouche
4
Penggunaannya salah. Idenya adalah untuk tidak membangun objek baru jika sudah ada, tetapi dalam kasus Anda __new__()selalu dipanggil, dan selalu membangun objek baru, lalu melemparkannya. Dalam hal ini a iflebih disukai daripada .setdefault().
Amit Upadhyay
Tapi, saya tidak mengerti mengapa perbedaan dalam output yaitu di kelas gaya lama dua contoh berbeda sehingga dikembalikan False, tetapi di kelas gaya baru, kedua contoh sama. Bagaimana? Apa perubahan di kelas gaya baru, yang membuat dua contoh sama, yang tidak di kelas gaya lama?
Pabitra Pati
1
@PabitraPati: Ini semacam demonstrasi murah di sini. __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.
ShadowRanger
10

Kelas-kelas gaya baru mewarisi dari objectdan 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.

Lingkaran cahaya
sumber
8

Kelas gaya baru dapat menggunakan di super(Foo, self)mana Fookelas dan selfcontohnya.

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.

jamylak
sumber