2015-01-12 11 views
18

अजगर में, जब एक दृश्य प्रकार को लागू करने, मैं अक्सर (अपेक्षाकृत बोल) अपने आप को इस तरह कोड लिखने लगता है:__getitem__ को साफ तरीके से कैसे लिखें?

class FooSequence(collections.abc.Sequence): 
    # Snip other methods 

    def __getitem__(self, key): 
     if isinstance(key, int): 
      # Get a single item 
     elif isinstance(key, slice): 
      # Get a whole slice 
     else: 
      raise TypeError('Index must be int, not {}'.format(type(key).__name__)) 

कोड isinstance() साथ स्पष्ट रूप से अपने तर्क के प्रकार की जाँच करता है। पायथन समुदाय के भीतर यह regarded as an antipattern है। मैं इससे कैसे बचूं?

  • मैं, functools.singledispatch उपयोग नहीं कर सकते क्योंकि वह quite deliberately तरीकों के साथ असंगत (यह self पर प्रेषण करने के लिए है, जो पूरी तरह से बेकार है, क्योंकि हम पहले से ही OOP बहुरूपता के माध्यम से self पर भेजने का प्रयास करेंगे रहे हैं)। यह @staticmethod के साथ काम करता है, लेकिन अगर मुझे self से सामान प्राप्त करने की आवश्यकता है तो क्या होगा?
  • int() पर कास्ट करना और फिर TypeError को पकड़ना, एक टुकड़ा की जांच करना, और संभवतः फिर से उठाना अभी भी बदसूरत है, हालांकि शायद थोड़ा कम है।
  • यह पूर्णांक को एक-तत्व स्लाइस में परिवर्तित करने के लिए क्लीनर हो सकता है और दोनों स्थितियों को एक ही कोड के साथ संभाल सकता है, लेकिन इसकी अपनी समस्याएं हैं (0 या [0] वापस करें?)।

उत्तर

13

जितना ज्यादा अजीब लगता है, मुझे संदेह है कि आपके पास जिस तरह से है, वह चीजों के बारे में जाने का सबसे अच्छा तरीका है। आमतौर पर पैटर्न सामान्य उपयोग के मामलों को शामिल करने के लिए मौजूद होते हैं, लेकिन इसका मतलब यह नहीं है कि उन्हें पालन करते समय सुसमाचार के रूप में लिया जाना चाहिए और जीवन को और अधिक कठिन बना देता है। पीईपी 443 स्पष्ट टाइपकेकिंग पर बाल्किंग के लिए मुख्य कारण यह है कि यह "भंगुर और विस्तार के लिए बंद है" का मुख्य कारण यह है कि। हालांकि, यह मुख्य रूप से कस्टम कार्यों पर लागू होता है जो किसी भी समय कई अलग-अलग प्रकार लेते हैं। Python docs on __getitem__ से:

अनुक्रम प्रकारों के लिए, स्वीकृत कुंजी पूर्णांक और स्लाइस ऑब्जेक्ट्स होना चाहिए। ध्यान दें कि नकारात्मक इंडेक्स की विशेष व्याख्या (यदि वर्ग अनुक्रम प्रकार का अनुकरण करना चाहता है) __getitem __() विधि तक है। यदि कुंजी अनुचित प्रकार का है, तो TypeError उठाया जा सकता है; अनुक्रम के लिए इंडेक्स के सेट के बाहर एक मूल्य (ऋणात्मक मूल्यों की किसी भी विशेष व्याख्या के बाद), इंडेक्सरर उठाया जाना चाहिए। मानचित्रण प्रकारों के लिए, यदि कुंजी गुम है (कंटेनर में नहीं), KeyError उठाया जाना चाहिए।

पायथन दस्तावेज स्पष्ट रूप से दो प्रकारों को बताता है जिन्हें स्वीकार किया जाना चाहिए, और क्या करना चाहिए यदि कोई आइटम उन दो प्रकारों से नहीं है। यह देखते हुए कि दस्तावेज स्वयं प्रलेखन द्वारा प्रदान किए जाते हैं, यह बदलने की संभावना नहीं है (ऐसा करने से केवल आपके मुकाबले कहीं अधिक कार्यान्वयन टूट जाएंगे), इसलिए शायद पाइथन के खिलाफ कोड बदलने के आपके रास्ते से बाहर निकलने में परेशानी के लायक नहीं है।

यदि आप स्पष्ट टाइप चेकिंग से परहेज करते हैं, तो मैं आपको this SO answer पर इंगित करता हूं। इसमें @methdispatch सजावट का एक संक्षिप्त कार्यान्वयन है (मेरा नाम नहीं, लेकिन मैं इसके साथ रोल करूंगा) जो @singledispatch (स्वयं) के बजाय args[1] (arg) की जांच करने के लिए इसे मजबूर कर तरीकों के साथ काम करता है। इसका उपयोग करने से आपको अपने __getitem__ विधि के साथ कस्टम एकल प्रेषण का उपयोग करने की अनुमति देनी चाहिए।

चाहे आप इनमें से किसी भी "पायथनिक" पर विचार करते हैं या नहीं, लेकिन याद रखें कि जेन ऑफ पायथन ने नोट किया है कि "विशेष मामले नियमों को तोड़ने के लिए पर्याप्त नहीं हैं", फिर यह तुरंत नोट करता है कि " व्यावहारिकता शुद्धता धड़कता है "। इस मामले में, दस्तावेज स्पष्ट रूप से बताए गए दो प्रकारों की जांच कर रहे हैं केवल __getitem__ का समर्थन करना चाहिए मेरे लिए व्यावहारिक तरीका जैसा लगता है।

0

मुझे एक बार करने से बचने के तरीके से अवगत नहीं है। इस तरह से एक गतिशील रूप से टाइप की गई भाषा का उपयोग करने का यह सिर्फ व्यापार है। हालांकि, इसका मतलब यह नहीं है कि आपको इसे बार-बार करना है। मैं विधि के नाम बाहर विभाजित के साथ एक अमूर्त वर्ग बनाने के द्वारा यह एक बार का समाधान होगा, तो उस वर्ग से के बजाय सीधे Sequence से विरासत, जैसे:

class UnannoyingSequence(collections.abc.Sequence): 

    def __getitem__(self, key): 
     if isinstance(key, int): 
      return self.getitem(key) 
     elif isinstance(key, slice): 
      return self.getslice(key) 
     else: 
      raise TypeError('Index must be int, not {}'.format(type(key).__name__)) 

    # default implementation in terms of getitem 
    def getslice(self, key): 
     # Get a whole slice 

class FooSequence(UnannoyingSequence): 
    def getitem(self, key): 
     # Get a single item 

    # optional efficient, type-specific implementation not in terms of getitem 
    def getslice(self, key): 
     # Get a whole slice 

इस को साफ FooSequence पर्याप्त है कि मैं इसे इस तरह अगर भी कर सकते हैं मेरे पास केवल एक व्युत्पन्न वर्ग था। मैं आश्चर्यचकित हूं कि मानक लाइब्रेरी पहले से ही इस तरह से काम नहीं कर रही है।

+1

बेशक, भाषा * * __getslice__' के साथ हमारे लिए यह करने के लिए * का उपयोग किया जाता है, लेकिन यह बहिष्कृत है, जो मुझे पूरी तकनीक की सुदृढ़ता के बारे में आश्चर्यचकित करता है। – Kevin

+0

@ केविन, इसे विभिन्न कारणों से बहिष्कृत किया गया था। वे 'स्लाइस' ऑब्जेक्ट बनाना चाहते थे, लेकिन '__getslice__' ने' i' और 'j' तर्क लिया। यह पिछड़ा संगतता टूट गया होगा। –

-1

पायथनिक रहने के लिए, आप वस्तुओं के प्रकार के बजाय अर्थशास्त्र के साथ काम करते हैं। तो यदि आपके पास अनुक्रम के लिए एक्सेसर के रूप में कुछ पैरामीटर है, तो बस इसका उपयोग करें। यथासंभव एक पैरामीटर के लिए अमूर्तता का प्रयोग करें। यदि आप उपयोगकर्ता पहचानकर्ताओं के सेट की अपेक्षा करते हैं, तो सेट की अपेक्षा न करें, बल्कि add विधि के साथ कुछ डेटा संरचना। यदि आप कुछ पाठ की अपेक्षा करते हैं, तो unicode ऑब्जेक्ट की अपेक्षा न करें, बल्कि encode और decode विधियों वाले वर्णों के लिए कुछ कंटेनर की अपेक्षा न करें।

मैं सामान्य रूप से मानता हूं कि "कुछ कार्यान्वयन के व्यवहार का उपयोग करें जब तक कि कुछ विशेष मूल्य प्रदान नहीं किया जाता है। यदि आप __getitem__ को कार्यान्वित करना चाहते हैं, तो आप एक केस भेद का उपयोग कर सकते हैं जहां कुछ विशेष होता है यदि एक विशेष मूल्य । प्रदान की है मैं निम्नलिखित पैटर्न का उपयोग करेंगे:

class FooSequence(collections.abc.Sequence): 
    # Snip other methods 

    def __getitem__(self, key): 
     try: 
      if key == SPECIAL_VALUE: 
       return SOMETHING_SPECIAL 
      else: 
       return self.our_baseclass_instance[key] 
     except AttributeError: 
      raise TypeError('Wrong type: {}'.format(type(key).__name__)) 

आप एक ही मूल्य के बीच अंतर करना चाहते हैं (पर्ल शब्दावली में "अदिश") और एक दृश्य (जावा शब्दावली "संग्रह" में), तो यह है यह निर्धारित करने के लिए कि यह एक इटरेटर लागू किया गया है या नहीं। आप या तो कोशिश-पकड़ पैटर्न या hasattr का उपयोग कर सकते हैं जैसा कि अब मैं करता हूं:

अजगर और गहरे लाल रंग का उपयोग बतख टाइपिंग की तरह

class FooSequence(collections.abc.Sequence): 
    # Snip other methods 

    def __getitem__(self, key): 
     try: 
      if hasattr(key, "__iter__"): 
       return map(lambda x: WHATEVER(x), key) 
      else: 
       return self.our_baseclass_instance[key] 
     except AttributeError: 
      raise TypeError('Wrong type: {}'.format(type(key).__name__)) 

गतिशील प्रोग्रामिंग भाषाओं:

>>> a = 42 
>>> b = [1, 3, 5, 7] 
>>> c = slice(1, 42) 
>>> hasattr(a, "__iter__") 
False 
>>> hasattr(b, "__iter__") 
True 
>>> hasattr(c, "__iter__") 
False 
>>> 

हमारे उदाहरण के लिए लागू होता है। और एक बतख एक जानवर है, जो एक बतख की तरह चलता है, एक बतख की तरह तैरता है और एक बतख की तरह quacks। ऐसा नहीं है क्योंकि कोई इसे "बतख" कहते हैं।

+2

मैं सहमत हूं, लेकिन गेटिटम के लिए प्रलेखन स्पष्ट रूप से बताता है "केवल int और स्लाइस प्रकार ऑब्जेक्ट्स को अनुमति दें"। सरल कारणों के लिए पुनरावृत्तियों को अनुमति देने या स्वीकार करने का कोई कारण नहीं है कि getitem को iterables स्वीकार नहीं किया जाना चाहिए, और '[]' ऑपरेटर के प्रयोजनों के लिए, एक सूची कभी प्रदान नहीं की जाएगी। बतख टाइपिंग सभी अच्छी और अच्छी है, लेकिन दस्तावेज़ीकरण बिल्कुल स्पष्ट है कि कौन से आइटम स्वीकार करना है। अधिक जानकारी के लिए [getitem के लिए पायथन दस्तावेज़] देखें (https://docs.python.org/3.4/reference/datamodel.html#object.__getitem__)। –

+0

मेरे पास आधार कार्यान्वयन नहीं है। मैं हाथ से सब कुछ कर रहा हूँ। कोड को एक्सपोजिटरी उद्देश्यों के लिए सरलीकृत किया गया है; मेरा वास्तविक कोड 3 डी-स्लाइसेबल है, न्यूमपी का उपयोग नहीं करता है, और उसे इंडेक्स प्रीप्रोकैसिंग की उचित मात्रा में करना है जो अन्यथा विचलित हो जाएगा। – Kevin

+0

उस मामले में मैं @ SevenDeadlySins का उत्तर सबसे सहायक मानता हूं। – meisterluk

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