Python'daki eski stil ile yeni stil sınıfları arasındaki fark nedir?


Yanıtlar:


559

Gönderen Yeni tarzı ve klasik sınıflar :

Python 2.1'e kadar, eski stil sınıfları kullanıcının kullanabileceği tek lezzetti.

(Eski stil) sınıf kavramı, tip kavramıyla ilişkisizdir: eğer xeski stil sınıfının bir x.__class__ örneğiyse, sınıfını belirtir x, ancak type(x)her zaman öyle olur <type 'instance'>.

Bu, tüm eski stil örneklerinin, sınıflarından bağımsız olarak, örnek adı verilen tek bir yerleşik türle uygulandığını yansıtır.

Sınıf ve tür kavramlarını birleştirmek için Python 2.2'de yeni stil sınıfları tanıtıldı . Yeni stil sınıfı, yalnızca kullanıcı tanımlı bir türdür, daha fazla, daha az değil.

X, yeni stil sınıfının bir örneğiyse type(x), genellikle aynıdır x.__class__(bu garanti edilmese de - yeni stil sınıf örneğinin döndürülen değeri geçersiz kılmasına izin verilir x.__class__).

Yeni stil sınıflarını tanıtmak için en büyük motivasyon, tam bir meta model ile birleştirilmiş bir nesne modeli sağlamaktır .

Ayrıca, en yerleşik türlerin alt sınıflandırma yeteneği veya hesaplanan özellikleri etkinleştiren "tanımlayıcıların" tanıtılması gibi bir dizi acil faydası vardır.

Uyumluluk nedeniyle, sınıflar varsayılan olarak hala eski stildir .

Yeni stil sınıfları, üst sınıf olarak başka bir yeni stil sınıfı (yani bir tür) veya başka bir üst öğeye ihtiyaç duyulmadığında "üst düzey tür" nesnesi belirtilerek oluşturulur.

Yeni stil sınıflarının davranışı, eski tip sınıfların davranışından, hangi türün döndüğüne ek olarak bir dizi önemli ayrıntıda farklıdır.

Bu değişikliklerin bazıları, özel yöntemlerin çağrılma şekli gibi yeni nesne modeli için temeldir. Diğerleri, çoklu kalıtım durumunda yöntem çözüm sırası gibi uyumluluk endişeleri için daha önce uygulanamayan "düzeltmelerdir".

Python 3 yalnızca yeni stil sınıflarına sahiptir .

objectPython 3'te alt sınıf olsun ya da olmasın sınıflar yeni bir stildir.


41
Bu farklılıkların hiçbiri yeni stil sınıflarını kullanmak için zorlayıcı nedenlere benzemiyor, ancak herkes her zaman yeni stili kullanmanız gerektiğini söylüyor. Ördek yazmayı gerektiği gibi kullanıyorsam, asla kullanmam gerekmiyor type(x). Yerleşik bir türü alt sınıflara ayırmıyorsam, yeni stil sınıflarından görebildiğim bir avantaj yok gibi görünüyor. Bir dezavantajı var, ki bu da ekstra yazıyor (object).
yinelemeli

78
Gibi bazı özellikler super()eski stil sınıfları üzerinde çalışmıyor. Bahsetmemek gerekirse, bu makalede belirtildiği gibi, MRO gibi temel düzeltmeler ve bunu kullanmak için iyi bir neden olmaktan çok özel yöntemler vardır.
John Doe

21
@Kullanıcı: Eski stil sınıfları, 2.1'de olduğu gibi 2.7'de aynı şekilde davranıyor ve birkaç kişi tuhaflıkları bile hatırladığı için ve belgeler artık çoğunu tartışmıyor, daha da kötüsü. Yukarıdaki belge alıntısı doğrudan şunu söyler: eski stil sınıflarına uygulanamayan "düzeltmeler" vardır. Python 2.1'den bu yana kimsenin ilgilenmediği ve belgelerin artık açıklanmadığı tuhaflıklara girmek istemiyorsanız, eski stil sınıflarını kullanmayın.
abarnert

10
2.7'de eski stil sınıfları kullanırsanız karşılaşabileceğiniz bir tuhaflık örneği
KT.

5
Merak eden herkes için, Python 3'teki nesneden açıkça miras almanın iyi bir nedeni, Python'un birden fazla sürümünü desteklemesini kolaylaştırmasıdır.
jpmc26

308

Beyan-bilge:

Yeni stil sınıfları nesneden veya başka bir yeni stil sınıfından miras alır .

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

Eski tarz dersler yoktur.

class OldStyleClass():
    pass

Python 3 Not:

Python 3 eski stil sınıflarını desteklemediğinden, yukarıda belirtilen formlardan herhangi biri yeni bir stil sınıfıyla sonuçlanır.


24
yeni bir stil sınıfı başka bir yeni stil sınıfından miras alırsa, uzantı tarafından, miras alır object.
aaronasterling

2
Bu eski stil python sınıfının yanlış bir örneği mi? class AnotherOldStyleClass: pass
Ankur Agarwal

11
@abc Buna inanıyorum class A: passve class A(): passkesinlikle eşdeğer. Birincisi "A, hiçbir ana sınıfı miras almaz" ve ikincisi "A hiçbir ana sınıfı olmayan miras" anlamına gelir . Bu oldukça benzer not isveis not
eyquem

5
Bir yan not olarak, 3.X için, "nesne" nin mirası otomatik olarak kabul edilir (yani, 3.X'te "nesne" yi devralmanın bir yolumuz yoktur). Geriye dönük uyumluluk nedeniyle, orada "(nesne)" tutmak kötü değil.
Yo Hsiao

1
Devralınan sınıflar hakkında teknik bilgi alacaksak, bu cevap, eski bir stil sınıfından miras alarak başka bir eski stil sınıfı oluşturduğunuzu belirtmelidir. (
Yazıldığı

224

Eski ve yeni stil sınıfları arasında önemli davranış değişiklikleri

  • süper eklendi
  • MRO değişti (aşağıda açıklandı)
  • tanımlayıcılar eklendi
  • yeni stil sınıfı nesneleri türetilmedikçe yükseltilemez Exception(aşağıdaki örnek)
  • __slots__ katma

MRO (Yöntem Çözünürlük Sırası) değişti

Diğer cevaplarda bahsedildi, ancak burada klasik MRO ve C3 MRO (yeni stil sınıflarında kullanılan) arasındaki farkın somut bir örneği var.

Soru, özniteliklerin (yöntemler ve üye değişkenleri içeren) çoklu kalıtımda aranma sırasıdır.

Klasik sınıflar soldan sağa doğru derinlikte bir arama yapar. İlk maçta dur. Bu __mro__özelliklere sahip değiller .

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

Yeni stil sınıfları MRO, tek bir İngilizce cümlede sentezlemek için daha karmaşıktır. Bu ayrıntılı olarak açıklanmıştır burada . Özelliklerinden biri, temel sınıfın yalnızca türetilmiş tüm sınıflar arandıktan sonra aranmasıdır. Onlar sahip __mro__arama sırasını gösterir niteliğini.

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)

Yeni stil sınıfı nesneleri türetilmedikçe yükseltilemez Exception

Python 2.5 civarında birçok sınıf yükseltilebilir ve Python 2.6 civarında bu kaldırıldı. Python 2.7.3'te:

# 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

8
Güzel açık özet, teşekkürler. "İngilizce açıklamak zor" dediğinizde, ilk önce bir derinlik araması kullanan eski stil sınıfının aksine, önce postorder önce derinlik aramasını tanımladığınızı düşünüyorum. (ön sipariş, ilk çocuğumuzdan önce kendimizi aradığımız, postacı ise son çocuğumuzdan sonra kendimizi aradığımız anlamına gelir).
Steve Carter

40

Eski stil sınıfları, özellik araması için hala biraz daha hızlıdır. Bu genellikle önemli değildir, ancak performansa duyarlı Python 2.x kodunda yararlı olabilir:

[3]: sınıf A'da:
   ...: def __init __ (kendi kendine):
   ...: self.a = 'merhaba'
   ...:

[4] 'te B sınıfı (nesne):
   ...: def __init __ (kendi kendine):
   ...: self.a = 'merhaba'
   ...:

[6] 'da: aobj = A ()
[7] 'de: bobj = B ()

[8] 'de:% timeit aobj.a
10000000 döngü, döngü başına en iyi 3: 78,7 ns

[10] 'da:% timeit bobj.a
10000000 döngü, döngü başına en iyi 3: 86,9 ns

5
Pratikte fark ettiğiniz ilginçtir, bunun yeni stil sınıflarının, örnek dict'deki niteliği bulduktan sonra, bir açıklama olup olmadığını anlamak için ek bir arama yapması gerektiği için okudum. döndürülecek değeri almak için çağrılması gereken get yöntemi. Eski stil sınıfları basit olarak bulunan nesneyi ek hesaplama olmadan döndürür (ancak tanımlayıcıları desteklemez). Guido python-history.blogspot.co.uk/2010/06/… tarafından bu mükemmel yayında daha fazla bilgi bulabilirsiniz , özellikle yuvalardaki
xuloChavez

1
CPython 2.7.2 ile doğru görünmüyor:%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
Hala benim için x86-64 Linux üzerinde CPython 2.7.2'de aobj için daha hızlı.
xioxox

41
Performansa duyarlı uygulamalar için saf Python koduna güvenmek muhtemelen kötü bir fikirdir. Kimse demiyor: "Hızlı koda ihtiyacım var, bu yüzden eski tip Python sınıflarını kullanacağım." Numpy saf Python olarak sayılmaz.
Phillip Cloud

IPython 2.7.6'da da bu doğru değil. '' '' 477 ns vs. döngü başına 456 ns '' ''
kmonsoor

37

Guido, Python'daki yeni stil ve eski stil sınıfı hakkında gerçekten harika bir makale olan Yeni Stil Sınıfları'nın İç Hikayesini yazdı .

Python 3 sadece yeni stil sınıfına sahiptir. Bir 'eski stil sınıfı' yazsanız bile, dolaylı olarak türetilir object.

Yeni stil sınıfları, superyeni C3 mro , bazı sihirli yöntemler vb.Gibi eski stil sınıflarında eksik olan bazı gelişmiş özelliklere sahiptir .


24

İşte çok pratik, doğru / yanlış bir fark. Aşağıdaki kodun iki sürümü arasındaki tek fark, ikinci sürümde Kişinin nesneden miras almasıdır . Bunun dışında, iki versiyon aynı, ancak farklı sonuçlarla:

  1. Eski tarz sınıflar

    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. Yeni stil sınıfları

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

2
'_names_cache' ne yapar? Bir referans paylaşabilir misiniz?
Muatik

4
_names_cacheilettiğiniz her adı önbelleğe alan (gelecekteki erişim için depolayan) bir sözlüktür Person.__new__. Setdefault yöntemi (herhangi bir sözlükte tanımlanır) iki bağımsız değişken alır: bir anahtar ve bir değer. Anahtar dikte ise, değerini döndürür. Dikte değilse, önce ikinci argüman olarak iletilen değere ayarlar ve sonra döndürür.
ychaouche

4
Kullanım yanlış. Fikir zaten varsa yeni bir nesne inşa etmemek, ancak sizin durumunuzda __new__()her zaman çağrılır ve her zaman yeni bir nesne oluşturur ve sonra atar. Bu durumda a iftercih edilir .setdefault().
Amit Upadhyay

Ancak, çıktıdaki farkın neden olduğunu anlamadım, yani eski stil sınıfında iki örnek farklıydı, böylece Yanlış döndürüldü, ancak yeni stil sınıfında, her iki örnek de aynı. Nasıl ? Eski stil sınıfında olmayan iki örneği aynı kılan yeni stil sınıfındaki değişiklik nedir?
Pabitra Pati

1
@PabitraPati: Burada ucuz bir gösteri. __new__aslında eski tarz sınıflar için bir şey değildir, örneğin inşaatta kullanılmaz (tanımlamak gibi özel görünen rastgele bir addır __spam__). Bu nedenle, eski stil sınıfının oluşturulması sadece çağrılırken __init__, yeni stil konstrüksiyon __new__, inşa __init__etmek ve başlatmak için (tekton örneğine göre birleşerek) çağırır .
ShadowRanger

10

Yeni stil sınıfları objectPython 2.2'den itibaren miras alınır ve böyle yazılmalıdır (yani class Classname(object):yerine class Classname:). Temel değişiklik türleri ve sınıfları birleştirmektir ve bunun güzel yan etkisi, yerleşik türlerden miras almanıza izin vermesidir.

Daha fazla bilgi için descrintro sayfasını okuyun .


8

Yeni stil sınıfları , bir sınıf ve örnek super(Foo, self)olduğu yerlerde kullanabilir .Fooself

super(type[, object-or-type])

Bir üst veya kardeş sınıf sınıfına yöntem çağrılarını devreten bir proxy nesnesi döndürün. Bu, bir sınıfta geçersiz kılınan kalıtsal yöntemlere erişmek için kullanışlıdır. Arama sırası, türün kendisinin atlanması dışında getattr () tarafından kullanılanla aynıdır.

Ve Python 3.x'te super()herhangi bir parametre olmadan bir sınıfın içinde kullanabilirsiniz .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.