2012-06-01 7 views
12

Python'da belirli bir durum son zamanlarda beni endişelendirdi ve sebebi küçük bir araştırmadan sonra hala tamamen net değil. Aşağıdaki sınıf tanımları kusursuz bir şekilde çalışıyor görünüyor ve amaçlanan ne üretecek:__slots__ öğesini tanımlayan birden fazla sınıftan miras alınamaz mı?

class A: __slots__ = 'a', 'b' 
class B(A): __slots__ =() 
class C(A): __slots__ =() 
class D(B, C): __slots__ =() 

bir elmas kalıtım şeklinde düzenlenmiş dört sınıfları vardır. Ancak, biraz benzer bir modele izin verilmez. Aşağıdaki sınıf tanımları birinciyle aynı çalışıp çalışmadığını sanki görünüyor:

class B: __slots__ = 'a', 'b' 
class C: __slots__ = 'a', 'b' 
class D(B, C): __slots__ =() 

Traceback (most recent call last): 
    File "<pyshell#74>", line 1, in <module> 
    class D(B, C): __slots__ =() 
TypeError: multiple bases have instance lay-out conflict 

Ancak, TypeError bu örnekte yükseltilir. Bu yüzden üç soru ortaya çıkıyor: (1) Python'da bu bir hata mı? (2) Böyle bir cevabı haklı kılan nedir? (3) En iyi çözüm hangisidir?


Referanslar:

+2

Bunu itiraf ettim 100% bunu anlamadım, ama bağlandığınız kaynaklara dayanarak, bunun bir hata olduğu görünmüyor.Daha önce de söylediğim gibi, bu konuda% 100 değilim, ancak en iyi "geçici çözüm", __slots__' kullanımını sınırlamak gibi görünüyor. Kullanmak için _have_ belirli bir nedeni var mı? –

+2

Bir sihirli örnek özniteliğini sözlüğünün dışında depolamak için bir meta sınıf tarafından otomatik olarak oluşturulur. Sistem, birden fazla kalıtım sorununa yol açan tüm temel sınıflara otomatik olarak bir dönüşüm gerçekleştirir. –

cevap

2

Hiçbir sınıfın __slots__ tanımladığı bir kısıtlama zorlayarak, tüm alt sınıflar için istenen özelliklere sahip özel bir nesne sınıfı oluşturulabilir. Sınıf, düzenli nesneler için bir takma ad olarak kaydedilir.

class _object: __slots__ = '_MetaSafe__exec', '__dict__' 

class MetaSafe(type): 

    __REGISTRY = {object: _object} 

    @classmethod 
    def clone(cls, old): 
     return cls(old.__name__, old.__bases__, dict(old.__dict__), old) 

    def __new__(cls, name, bases, classdict, old=None): 
     # Check on a few classdict keys. 
     assert '__new__' not in classdict, '__new__ must not be defined!' 
     assert '__slots__' not in classdict, '__slots__ must not be defined!' 
     assert '__module__' in classdict, '__module__ must be defined!' 
     # Validate all the parent classes. 
     valid = [] 
     for base in bases: 
      if base in cls.__REGISTRY: 
       valid.append(cls.__REGISTRY[base]) 
      elif base in cls.__REGISTRY.values(): 
       valid.append(base) 
      else: 
       valid.append(cls.clone(base)) 
     # Wrap callables without thread mark. 
     for key, value in classdict.items(): 
      if callable(value): 
       classdict[key] = cls.__wrap(value) 
     # Fix classdict and create new class. 
     classdict.update({'__new__': cls.__new, '__slots__':(), '__module__': 
          '{}.{}'.format(__name__, classdict['__module__'])}) 
     cls.__REGISTRY[old] = new = \ 
      super().__new__(cls, name, tuple(valid), classdict) 
     return new 

    def __init__(self, name, bases, classdict, old=None): 
     return super().__init__(name, bases, classdict) 

    @staticmethod 
    def __wrap(func): 
     @functools.wraps(func) 
     def safe(self, *args, **kwargs): 
      return self.__exec(func, self, *args, **kwargs) 
     return safe 

    @classmethod 
    def __new(meta, cls, *args, **kwargs): 
     self = object.__new__(cls, *args, **kwargs) 
     if 'master' in kwargs: 
      self.__exec = kwargs['master'].__exec 
     else: 
      array = tuple(meta.__REGISTRY.values()) 
      for value in args: 
       if isinstance(value, array): 
        self.__exec = value.__exec 
        break 
      else: 
       self.__exec = Affinity() 
     return self 

Bu kod

sınıflardan klonlanmasıyla tkinter evreli yapmak için bir yapı bloğu olarak da kullanılabilir. Affinity sınıfı otomatik olarak, kodun GUI hatalarını önleyerek tek bir iş parçacığı üzerinde yürütülmesini sağlar.

-2
class superSlots: 
     @property 
     def __slots__(self):return self.MY_SLOTS 
class A(superSlots): 
     MY_SLOTS = "A","B" 
class B(superSlots): 
     MY_SLOTS = "A","B" 
class C(A,B): 
     MY_SLOTS = "X","Y" 

belki ?? Bu, en iyi yöntem olacağından olumlu değil ama işe yarayacağını düşünüyorum.

+4

Bence __slots__' çalışmak için çok büyülü ve özel olarak tedavi edildi. Ayrıca noktayı kaçırdığına da inanıyorum. –

+0

CPython 2.7 ve 3.2 her ikisi de 'TypeError: metaclass tabanları' özelliği 'nesnesini çağırırken hata yinelenmez' __slots__' özelliğini bir özellik olarak tanımlamaya çalışırken - '__slots__' gibi dinamik olarak hesaplamak gerçekten mantıklı olmaz . – James

+0

Bir cevap vermeye çalıştığınız için teşekkürler! Bu çalışmanın nihai sonuçları http://code.activestate.com/recipes/578152 (threadbox.py) olarak görülebilir. Yuva problemini çözer. –

0

Bu hatayla karşılaştım ve özel veritabanı düğümlerim için gerçekten yuva kullanmak istedim. İşte test paketi I (Python 3.x Onun) yaptık edilir: Burada

import logging 

A = None, 'attr1', 'attr2', 'attr3', 'attr4' 

class C12(object): 
    __slots__ = (A[1], A[2]) 

class C1234(object): 
    __slots__ = (A[1], A[2], A[3], A[4]) 

class C34(object): 
    __slots__ = (A[3], A[4]) 

class C3byC12(C12): 
    __slots__ = (A[3]) 

class CEmpty(object): 
    __slots__ =() 

MSG_FRM = '\n\tc1: {}\n\tc2: {}\n\t__slots__: {}' 
NOT_DEF = 'not defined' 

def test(c1, c2, slots): 
    logging.debug('*'*20 + ' new class test ' + '*'*20) 
    msg = MSG_FRM.format(c1, c2, slots) 
    try: 
     if slots == NOT_DEF: 
      class TestClass(c1, c2): pass 
     else:   
      class TestClass(c1, c2): 
       __slots__ = slots 
    except TypeError: 
     logging.exception('BOOM!!! ' + msg) 
    else: 
     logging.debug('No Boom! ' + msg) 
     instance = TestClass() 
     if '__dict__' in dir(instance): 
      logging.warning('Instance has __dict__!') 
     else: 
      logging.debug('Instance __slots__:{}'.format(
          instance.__slots__)) 
     logging.debug('Attributes in instance dir: {}'.format(
      ' '.join(['X' if (a in dir(instance)) else '_' 
        for a in A[1:]]))) 

if __name__ == '__main__': 
    logging.basicConfig(level=logging.DEBUG) 
    test(C12, C34, (A[2], A[4])) 
    test(C12, C3byC12, (A[2],)) 
    test(C3byC12, C12, (A[4],)) 
    test(C1234, C34, (A[2], A[4])) 
    test(C1234, CEmpty, (A[2], A[4])) 
    test(C12, CEmpty, (A[2], A[4])) 
    test(C12, CEmpty, (A[1], A[2])) 
    test(C12, CEmpty,()) 
    test(CEmpty, C1234, (A[2], A[4])) 
    test(CEmpty, C12, (A[3],)) 
    test(C12, C34, NOT_DEF) 
    test(C12, CEmpty, NOT_DEF) 

sonuçlar şöyle:

    : Gördüğünüz gibi

    DEBUG:root:******************** new class test ******************** 
    ERROR:root:BOOM!!! 
         c1: <class '__main__.C12'> 
         c2: <class '__main__.C34'> 
         __slots__: ('attr2', 'attr4') 
    Traceback (most recent call last): 
        File "boom.py", line 30, in test 
        class TestClass(c1, c2): 
    TypeError: multiple bases have instance lay-out conflict 
    DEBUG:root:******************** new class test ******************** 
    ERROR:root:BOOM!!! 
         c1: <class '__main__.C12'> 
         c2: <class '__main__.C3byC12'> 
         __slots__: ('attr2',) 
    Traceback (most recent call last): 
        File "boom.py", line 30, in test 
        class TestClass(c1, c2): 
    TypeError: Cannot create a consistent method resolution 
    order (MRO) for bases C3byC12, C12 
    DEBUG:root:******************** new class test ******************** 
    DEBUG:root:No Boom! 
         c1: <class '__main__.C3byC12'> 
         c2: <class '__main__.C12'> 
         __slots__: ('attr4',) 
    DEBUG:root:Instance __slots__:('attr4',) 
    DEBUG:root:Attributes in instance dir: X X X X 
    DEBUG:root:******************** new class test ******************** 
    ERROR:root:BOOM!!! 
         c1: <class '__main__.C1234'> 
         c2: <class '__main__.C34'> 
         __slots__: ('attr2', 'attr4') 
    Traceback (most recent call last): 
        File "boom.py", line 30, in test 
        class TestClass(c1, c2): 
    TypeError: multiple bases have instance lay-out conflict 
    DEBUG:root:******************** new class test ******************** 
    DEBUG:root:No Boom! 
         c1: <class '__main__.C1234'> 
         c2: <class '__main__.CEmpty'> 
         __slots__: ('attr2', 'attr4') 
    DEBUG:root:Instance __slots__:('attr2', 'attr4') 
    DEBUG:root:Attributes in instance dir: X X X X 
    DEBUG:root:******************** new class test ******************** 
    DEBUG:root:No Boom! 
         c1: <class '__main__.C12'> 
         c2: <class '__main__.CEmpty'> 
         __slots__: ('attr2', 'attr4') 
    DEBUG:root:Instance __slots__:('attr2', 'attr4') 
    DEBUG:root:Attributes in instance dir: X X _ X 
    DEBUG:root:******************** new class test ******************** 
    DEBUG:root:No Boom! 
         c1: <class '__main__.C12'> 
         c2: <class '__main__.CEmpty'> 
         __slots__: ('attr1', 'attr2') 
    DEBUG:root:Instance __slots__:('attr1', 'attr2') 
    DEBUG:root:Attributes in instance dir: X X _ _ 
    DEBUG:root:******************** new class test ******************** 
    DEBUG:root:No Boom! 
         c1: <class '__main__.C12'> 
         c2: <class '__main__.CEmpty'> 
         __slots__:() 
    DEBUG:root:Instance __slots__:() 
    DEBUG:root:Attributes in instance dir: X X _ _ 
    DEBUG:root:******************** new class test ******************** 
    DEBUG:root:No Boom! 
         c1: <class '__main__.CEmpty'> 
         c2: <class '__main__.C1234'> 
         __slots__: ('attr2', 'attr4') 
    DEBUG:root:Instance __slots__:('attr2', 'attr4') 
    DEBUG:root:Attributes in instance dir: X X X X 
    DEBUG:root:******************** new class test ******************** 
    DEBUG:root:No Boom! 
         c1: <class '__main__.CEmpty'> 
         c2: <class '__main__.C12'> 
         __slots__: ('attr3',) 
    DEBUG:root:Instance __slots__:('attr3',) 
    DEBUG:root:Attributes in instance dir: X X X _ 
    DEBUG:root:******************** new class test ******************** 
    ERROR:root:BOOM!!! 
         c1: <class '__main__.C12'> 
         c2: <class '__main__.C34'> 
         __slots__: not defined 
    Traceback (most recent call last): 
        File "boom.py", line 28, in test 
        class TestClass(c1, c2): pass 
    TypeError: multiple bases have instance lay-out conflict 
    DEBUG:root:******************** new class test ******************** 
    DEBUG:root:No Boom! 
         c1: <class '__main__.C12'> 
         c2: <class '__main__.CEmpty'> 
         __slots__: not defined 
    WARNING:root:Instance has __dict__! 
    DEBUG:root:Attributes in instance dir: X X _ _ 
    

    iki seçeneğiniz var

  1. , üst sınıflardan biri hariç tümü için,
  2. 'u tanımlayın veya ebeveynlerden birini diğerinin alt sınıfını yapın. Eğer çok yeni sınıfta __slots__ tanımlamak gerekir

Not aksi takdirde __dict__ alır.