2012-06-01 4 views
12

पाइथन में एक निश्चित स्थिति ने हाल ही में मुझे आगाह किया, और इसका कारण अभी भी थोड़ा सा शोध के बाद पूरी तरह स्पष्ट नहीं है। निम्नलिखित वर्ग परिभाषाओं दोषरहित काम करने के लिए दिखाई देते हैं और क्या करना है का उत्पादन करेगा:__slots__ को परिभाषित करने वाले कई वर्गों से प्राप्त नहीं हो सकता है?

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

एक हीरे की विरासत पैटर्न में व्यवस्थित चार वर्गों रहे हैं। हालांकि, कुछ हद तक समान पैटर्न की अनुमति नहीं है। निम्नलिखित वर्ग परिभाषाओं जैसे कि वे पहले के रूप में ही कार्य कर रहे हैं लगता है:

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 

हालांकि, एक TypeError इस उदाहरण में उठाया जाता है। तो तीन सवाल उठते हैं: (1) क्या स्लॉट नामों पर विचार करते हुए यह पाइथन में एक बग है? (2) इस तरह के एक जवाब को न्यायसंगत क्या होगा? (3) सबसे अच्छा कामकाज क्या है?


संदर्भ:

+2

मैं कबूल करता हूं कि मैं इसे 100% समझ नहीं रहा हूं, लेकिन आपके द्वारा लिंक किए गए स्रोतों के आधार पर, ऐसा नहीं लगता है कि यह एक बग है।जैसा कि मैंने पहले कहा था, मैं इस पर 100% नहीं हूं, लेकिन सबसे अच्छा "कामकाज" '__slots__' के उपयोग को सीमित करना प्रतीत होता है। क्या आपके पास उनका उपयोग करने के लिए _have_ एक विशिष्ट कारण है? –

+2

वे अपने शब्दकोश के बाहर एक जादू आवृत्ति विशेषता को स्टोर करने के लिए स्वचालित रूप से मेटाक्लास द्वारा जनरेट किए जा रहे हैं। सिस्टम स्वचालित रूप से सभी आधार वर्गों पर एक रूपांतरण चलाता है जो कई विरासत समस्या का कारण बनता है। –

उत्तर

2

किसी भी वर्ग को __slots__ को परिभाषित करने वाली बाधा को मजबूर करने से, एक विशेष वस्तु वर्ग सभी बाल वर्गों के लिए वांछित विशेषताओं के साथ बनाया जा सकता है। वर्ग नियमित वस्तुओं के लिए उपनाम के रूप में पंजीकृत है।

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 

इस कोड tkinter धागा सुरक्षित अपने वर्गों क्लोनिंग द्वारा बनाने के लिए एक निर्माण खंड के रूप में इस्तेमाल किया जा सकता। Affinity कक्षा स्वचालित रूप से सुनिश्चित करती है कि कोड को एक ही थ्रेड पर निष्पादित किया गया है, जो GUI त्रुटियों को रोकता है।

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

शायद ?? सकारात्मक नहीं है कि यह सबसे अच्छा तरीका होगा, लेकिन मुझे लगता है कि यह ठीक काम करेगा

+4

मुझे लगता है कि '__slots__' बहुत जादुई है और विशेष रूप से इसके लिए काम करने के लिए इलाज किया जाता है। मुझे यह भी संदेह है कि आप बिंदु खो रहे हैं। –

+0

सीपीथन 2.7 और 3.2 दोनों कहते हैं 'TypeError: मेटाक्लास बेस' प्रॉपर्टी 'ऑब्जेक्ट को कॉल करते समय त्रुटि' __slots__' को किसी संपत्ति के रूप में परिभाषित करने का प्रयास करते समय त्रुटि नहीं होती है - यह वास्तव में '__slots__' को गतिशील रूप से गणना करने के लिए समझ में नहीं आता है । – James

+0

उत्तर देने का प्रयास करने के लिए धन्यवाद! इस काम के अंतिम परिणाम http://code.activestate.com/recipes/578152 (threadbox.py) के रूप में देखा जा सकता है। यह स्लॉट समस्या हल करता है। –

0

मुझे इस त्रुटि का सामना करना पड़ा है और मैं वास्तव में अपने कस्टम डेटाबेस नोड्स के लिए स्लॉट का उपयोग करना चाहता था। यहाँ है टेस्ट स्वीट मैं (अजगर 3.x में इसकी) कर दिया है:

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) 

यहाँ परिणाम हैं:

    :

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

    आप देख सकते हैं आपके पास दो विकल्प

  1. या तो सभी के लिए __slots__ =() परिभाषित करें, लेकिन माता-पिता वर्गों में से एक,
  2. या माता-पिता में से एक को दूसरे के उप-वर्ग में डालने के लिए परिभाषित करें।

ध्यान दें कि आपको नई कक्षा में भी __slots__ परिभाषित करना चाहिए, अन्यथा यह __dict__ हो जाता है।

संबंधित मुद्दे