Nesne örneğinin __class__ özelliğini neden değiştiremiyorum?


10
class A(object):
    pass

class B(A):
    pass

o = object()
a = A()
b = B()

Değişebilirken a.__class__aynı şeyi yapamam o.__class__(bir TypeErrorhata atıyor ). Neden?

Örneğin:

isinstance(a, A) # True
isinstance(a, B) # False
a.__class__ = B
isinstance(a, A) # True
isinstance(a, B) # True

isinstance(o, object) # True
isinstance(o, A) # False
o.__class__ = A # This fails and throws a TypeError
# isinstance(o, object)
# isinstance(o, A)

Bunun genellikle iyi bir fikir olmadığını biliyorum, çünkü yanlış kullanılırsa çok garip davranışlara yol açabilir. Sadece merak uğruna.


3
Yerleşik türler, verimlilik nedeniyle kullanıcı tanımlı bir türün dinamizmini feda eder. Bir başka isteğe bağlı optimizasyon da benzer şekilde bunu önleyecek olan yuvalardır.
juanpa.arrivillaga

Yanıtlar:


6

CPython bir yorum vardır Nesneler / typeobject.c bu konuda:

3.5'ten önceki CPython sürümlerinde, kod, compatible_for_assignmentHEAPTYPE olmayan sınıflar için bellek düzeni / yuva / vb. Uyumluluğunu doğru bir şekilde kontrol edecek şekilde ayarlanmadı, bu nedenle __class__HEAPTYPE -> HEAPTYPE olmayan her durumda atamaya izin vermedik.

3.5 geliştirme döngüsü sırasında, compatible_for_assignmentrastgele türler arasındaki uyumluluğu doğru bir şekilde kontrol etmek için kodu düzelttik ve __class__eski ve yeni türlerin aslında uyumlu yuvalara ve bellek düzenine sahip oldukları tüm durumlarda atamaya izin vermeye başladık (HEAPTYPE olarak uygulandıklarına bakılmaksızın) ya da değil).

3.5 sürümü yayınlanmadan hemen önce, bunun int gibi değişmez türlerle ilgili sorunlara yol açtığını keşfettik. Eskiden bu bir problem değildi, çünkü gerçekten değişmezlerdi - özellikle, tercümanın bu stajyer hileyi uyguladığı tüm türler de statik olarak tahsis edildi, bu nedenle eski HEAPTYPE kuralları "yanlışlıkla" __class__atamaya izin vermiyordu . Ancak __class__atamada yapılan değişikliklerle ,

class MyInt(int):
#   ...
# Modifies the type of *all* instances of 1 in the whole program,
# including future instances (!), because the 1 object is interned.
 (1).__class__ = MyInt

(bkz. https://bugs.python.org/issue24912 ).

Teoride doğru çözüm, hangi sınıfların bu değişmeze bağlı olduğunu tanımlamak ve bir şekilde __class__sadece onlar için ödeve izin vermemek , belki de yeni bir Py_TPFLAGS_IMMUTABLE bayrağı ("kara listeye alma" yaklaşımı) gibi bir mekanizma aracılığıyla. Ancak pratikte, bu sorun 3.5 RC döngüsünün sonlarında fark edilmediğinden, muhafazakar yaklaşımı benimsiyor ve eskiden sahip olduğumuz aynı HEAPTYPE-> HEAPTYPE kontrolünü ve bir de "beyaz liste" yi eski haline getiriyoruz. Şimdilik, beyaz liste sadece ModuleType alt türlerinden oluşuyor, çünkü bunlar yamayı ilk etapta motive eden vakalar - bkz. Https://bugs.python.org/issue22986 - ve modül nesneleri değiştirilebilir olduğundan emin olabiliriz kesinlikle stajyer olmamaları. Şimdi HEAPTYPE-> HEAPTYPE veya ModuleType alt türü -> ModuleType alt türü.

Bildiğimiz kadarıyla, aşağıdaki 'if' ifadesinin ötesindeki tüm kodlar HEAPTYPE olmayan sınıfları doğru bir şekilde işleyecek ve HEAPTYPE denetimi yalnızca yorumlayıcının pişirdiği HEAPTYPE sınıflarının alt kümesini korumak için gereklidir. tüm örnekler gerçekten değişmezdir.

Açıklama:

CPython nesneleri iki şekilde depolar:

Nesneler öbek üzerinde tahsis edilen yapılardır. Düzgün çöp toplandıklarından emin olmak için nesnelerin kullanımına ilişkin özel kurallar geçerlidir. Nesneler hiçbir zaman statik olarak veya yığın üzerinde ayrılmaz; bunlara yalnızca özel makrolar ve işlevler aracılığıyla erişilmelidir. (Yazım nesneleri birinci kuralın istisnasıdır; Python 2.2 için yazım / sınıf birleştirme üzerinde yapılan çalışmalar yığın tahsisli yazım nesnelerine sahip olmayı mümkün kılsa da, standart türler statik olarak başlatılan yazım nesneleriyle temsil edilir).

Include / object.h içindeki yorumdan alınan bilgiler .

Olarak yeni bir değer ayarlamaya çalıştığınızda some_obj.__class__, object_set_classişlev çağrılır. PyBaseObject_Type kaynağından devralınır, alana bakın /* tp_getset */. Bu işlev şunları denetler : yeni tür eski türün yerini alabilir some_objmi?

Örneğinizi alın:

class A:
    pass

class B:
    pass

o = object()
a = A() 
b = B() 

İlk durum:

a.__class__ = B 

Tipi anesnesi olan Adinamik tahsis edilir, çünkü yığın türü. Yanı sıra B. aBireyin tip bir sorun olmadan değiştirilir.

İkinci durum:

o.__class__ = B

Türü oyerleşik türdür object( PyBaseObject_Type). Öbek türü değildir, bu yüzden TypeErroryükseltilir:

TypeError: __class__ assignment only supported for heap types or ModuleType subclasses.

4

Yalnızca __class__aynı dahili (C) düzenine sahip başka bir türle değiştirebilirsiniz . Çalışma zamanı, türün kendisi dinamik olarak ayrılmadıkça (bir "yığın türü") bu düzeni bile bilmez, bu nedenle yerleşik türleri kaynak veya hedef olarak hariç tutan gerekli bir durumdur. Ayrıca __slots__, aynı adlara sahip aynı kümeye sahip olmanız gerekir .

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.