2012-02-06 33 views
13

अद्यतन:संकुचित इलाज के रूप में iterable

एक विचार निर्मित तार बनाने के लिए गैर iterable proposed on python.org in 2006 था। मेरा प्रश्न अलग-अलग है कि मैं थोड़ी देर में इस सुविधा को केवल दबाने की कोशिश कर रहा हूं; अभी भी यह पूरा धागा काफी प्रासंगिक है।

यहाँ महत्वपूर्ण comments by Guido जो एक परीक्षण के आधार पर क्रियान्वित गैर iterable str हैं:

[...] मैं इस कार्यान्वित (यह वास्तव में लिए आसान था) लेकिन फिर पाया मैं ठीक करने के लिए किया था तारों से अधिक स्थानों को फिर से चालू करें। उदाहरण के लिए:

सेट की तरह
  • sre पार्सर और संकलक उपयोग बातें ("") और भी इनपुट regexp के पात्रों से अधिक पुनरावृति यह पार्स करने के लिए।

  • difflib एक API तार की या तो दो सूचियों के लिए परिभाषित किया गया (एक ठेठ पंक्ति-दर-पंक्ति एक फ़ाइल का अंतर), या दो तार (एक ठेठ इंट्रा-लाइन अंतर), या यहाँ तक कि कुछ भी की दो सूचियां (है एक सामान्यीकृत क्रम भिन्नता के लिए)।

  • optparse.py में छोटे बदलाव, textwrap.py, string.py।

और मैं भी बिंदु पर नहीं कर रहा हूँ जहाँ regrtest.py ढांचा भी काम करता है (difflib समस्या के कारण)।

मैं इस परियोजना को छोड़ रहा हूं; पैच एसएफ पैच 14712 9 1 है। मैं इस विचार के पक्ष में नहीं हूं; यह सिर्फ व्यावहारिक नहीं है, और आधार है कि एक स्ट्रिंग पर पुनरावृत्त करने के कुछ अच्छे कारण हैं दोनों sre और difflib में उपयोग किए गए उपयोग मामलों से अस्वीकार कर दिया गया है।

मूल प्रश्न:

हालांकि यह भाषा है कि एक स्ट्रिंग एक iterable है की एक साफ सुविधा है, जब बतख टाइपिंग के साथ संयुक्त, यह आपदा को जन्म दे सकती:

# record has to support [] operation to set/retrieve values 
# fields has to be an iterable that contains the fields to be set 
def set_fields(record, fields, value): 
    for f in fields: 
    record[f] = value 

set_fields(weapon1, ('Name', 'ShortName'), 'Dagger') 
set_fields(weapon2, ('Name',), 'Katana') 
set_fields(weapon3, 'Name', 'Wand') # I was tired and forgot to put parentheses 

कोई अपवाद नहीं उठाया जाएगा, और असंख्य स्थानों में isinstance(fields, str) के परीक्षण के अलावा इसे पकड़ने का कोई आसान तरीका नहीं है। कुछ परिस्थितियों में, इस बग को खोजने में बहुत लंबा समय लगेगा।

मैं स्ट्रिंग को पूरी तरह से अपने प्रोजेक्ट में एक पुनरावर्तनीय के रूप में अक्षम करने से अक्षम करना चाहता हूं। क्या यह एक अच्छा विचार है? क्या यह आसानी से और सुरक्षित तरीके से किया जा सकता है?

शायद मैं str में अंतर्निहित उपclass कर सकता हूं, जैसे कि मैं स्पष्ट रूप से get_iter() पर कॉल करने की आवश्यकता होगी यदि मैं चाहता हूं कि इसकी वस्तु को एक पुनरावर्तनीय माना जाए। फिर जब भी मुझे स्ट्रिंग अक्षर की आवश्यकता होती है, तो मैं इसके बजाय इस वर्ग का ऑब्जेक्ट बनाउंगा।

यहाँ कुछ स्पर्शरेखीय संबंधित प्रश्न हैं:

How can I tell if a python variable is a string or a list?

how to tell a variable is iterable but not a string

+0

मुझे लगता है कि आपने मूल रूप से अपने स्वयं के प्रश्न का उत्तर दिया है। यदि आपको ऐसा करना है तो आपके दो तरीके सबसे अच्छे तरीके हैं, लेकिन सबसे अच्छा जवाब यह सुनिश्चित करना है कि ऐसा नहीं होता है। –

+2

मैं बस 'इंस्टेंसेंस (फ़ील्ड्स, स्ट्र)' चेक के साथ रहूंगा - आपको स्ट्रिंग की तरह क्वाक करने के लिए कभी भी अपने स्वयं के प्रकार बनाने की क्षमता की आवश्यकता नहीं है। वैकल्पिक रूप से, अंतिम, varargs तर्क 'फ़ील्ड' बनाओ। (यद्यपि यह थक गया है और भूल जाता है कि आप * * * को इसके चारों ओर कोष्ठक नहीं रखना चाहते हैं।) – millimoose

+0

तारों की जेनेरिक सूचियों के रूप में स्ट्रिंग को परिभाषित करने वाली किसी भी लाइब्रेरी/भाषा में यह समस्या होगी। यह एक पायथन चीज की तरह प्रतीत नहीं होता है। – Apalala

उत्तर

8

दुर्भाग्यवश, इसे स्वचालित रूप से करने के कोई तरीके नहीं हैं। आपके द्वारा प्रस्तावित समाधान (str सबक्लास जो पुनरावृत्त नहीं है) isinstance() के समान समस्या से पीड़ित है ... अर्थात्, आपको हर जगह इसका उपयोग करना याद रखना होगा जब आप स्ट्रिंग का उपयोग करते हैं, क्योंकि पाइथन इसे जगह में उपयोग करने का कोई तरीका नहीं है देशी वर्ग के। और निश्चित रूप से आप निर्मित वस्तुओं को बंदर-पैच नहीं कर सकते हैं।

मैं सुझाव है कि शायद आप अपने आप को लगता है कि अगर एक समारोह है कि या तो एक iterable कंटेनर या एक स्ट्रिंग लेता लेखन, हो सकता है कि वहाँ कुछ अपने डिजाइन के साथ गलत क्या है कि। कभी-कभी आप इससे बच नहीं सकते हैं।

मेरे दिमाग में, कम से कम घुसपैठ करने वाली चीज को चेक में एक फ़ंक्शन में रखना और कॉल करना है कि जब आप लूप में जाते हैं। यह कम से कम व्यवहार में परिवर्तन डालता है जहां आपको इसे देखने की अधिक संभावना होती है: for कथन में, किसी वर्ग में कहीं कहीं दफन नहीं किया जाता है।

def iterate_no_strings(item): 
    if issubclass(item, str): # issubclass(item, basestring) for Py 2.x 
     return iter([item]) 
    else: 
     return iter(item) 

for thing in iterate_no_strings(things): 
    # do something... 
+0

+1। यदि आपके पास * ऐसा करने के लिए यह एक अच्छा जवाब है। हालांकि, मैं इसके खिलाफ अभी भी सिफारिश करता हूं। –

+0

उदाहरण के रूप में मैंने जो फ़ंक्शन दिया है उसके बारे में क्या? क्या आप कहते हैं कि यह "गलत डिजाइन" का मामला है या "इससे बच नहीं सकता"? – max

+0

मैं थोड़ी देर आगे डरता हूं। कभी-कभी मैं कहना चाहता हूं कि "जो कुछ आप स्वीकार करते हैं उसमें उदार रहें" और "यदि संभव हो तो उपयोगकर्ता जो कुछ भी चाहता है उसे करने का प्रयास करें।" हालांकि, अपने विशेष मामले में, शायद पहले मूल्य और नाम जिन्हें आप '* args' के रूप में सेट करना चाहते हैं? फिर आप हमेशा एक पुनरावृत्ति प्राप्त करेंगे और कॉलर बस उनके नाम के रूप में निर्दिष्ट करता है। यदि उनके पास पहले से ही एक ट्यूपल है तो वे आपको कॉल करते समय इसे अनपैक करते हैं। – kindall

6

का विस्तार करने के लिए, और इसे से बाहर एक जवाब है:

नहीं है, यदि आप ऐसा नहीं करना चाहिए।

  1. यह स्ट्रिंग से लोगों की अपेक्षा कार्यक्षमता को बदलता है।
  2. इसका मतलब है आपके पूरे कार्यक्रम में अतिरिक्त ओवरहेड।
  3. यह काफी हद तक अनावश्यक है।
  4. जांच प्रकार बहुत अस्पष्ट है।

तुम कर सकते हो, और विधियों आप दे दिया है शायद सबसे अच्छा तरीके हैं (रिकार्ड के लिए , मुझे लगता है कि आप यह करने के लिए है, तो उप-classing बेहतर विकल्प है, @ kindall की विधि देखें) लेकिन यह बस करने लायक नहीं है, और यह बहुत पाइथनिक नहीं है। पहली जगह में बग से बचें। आपके उदाहरण में, आप खुद से पूछना चाहेंगे कि क्या आपके तर्कों में स्पष्टता के साथ यह एक मुद्दा है, और नामित तर्क या स्प्लट बेहतर समाधान हो सकता है या नहीं।

E.g: ऑर्डरिंग बदलें।

def set_fields(record, value, *fields): 
    for f in fields: 
    record[f] = value 

set_fields(weapon1, 'Dagger', *('Name', 'ShortName')) #If you had a tuple you wanted to use. 
set_fields(weapon2, 'Katana', 'Name') 
set_fields(weapon3, 'Wand', 'Name') 

E.g: नामांकित तर्क।

def set_fields(record, fields, value): 
    for f in fields: 
    record[f] = value 

set_fields(record=weapon1, fields=('Name', 'ShortName'), value='Dagger') 
set_fields(record=weapon2, fields=('Name'), value='Katana') 
set_fields(record=weapon3, fields='Name', value='Wand') #I find this easier to spot. 

तुम सच में आदेश में एक ही चाहते हैं, लेकिन नहीं लगता कि नामित तर्क विचार (एक dict की एक dict की तरह आइटम के बजाय प्रत्येक रिकॉर्ड बनाने यदि ऐसा नहीं है के बारे में पर्याप्त स्पष्ट है, तो क्या करते हैं पहले से ही) और होने:

class Record: 
    ... 
    def set_fields(self, *fields, value): 
     for f in fileds: 
      self[f] = value 

weapon1.set_fields("Name", "ShortName", value="Dagger") 

यहाँ केवल मुद्दा है, शुरू की कक्षा और तथ्य यह है value पैरामीटर कीवर्ड के द्वारा किया जा सकता है कि है, हालांकि यह यह स्पष्ट रहता है।

वैकल्पिक रूप से, अगर आप अजगर 3 का उपयोग कर रहे हैं, आप हमेशा बढ़ाया टपल unpacking का उपयोग करने का विकल्प होता है:

def set_fields(*args): 
     record, *fields, value = args 
     for f in fields: 
     record[f] = value 

set_fields(weapon1, 'Name', 'ShortName', 'Dagger') 
set_fields(weapon2, 'Name', 'Katana') 
set_fields(weapon3, 'Name', 'Wand') 

या, मेरे पिछले उदाहरण के लिए:

class Record: 
    ... 
    def set_fields(self, *args): 
     *fields, value = args 
     for f in fileds: 
      self[f] = value 

weapon1.set_fields("Name", "ShortName", "Dagger") 

हालांकि, इन छोड़ दिया फंक्शन कॉल पढ़ने पर कुछ अजीबता, इस तथ्य के कारण आम तौर पर यह मानता है कि तर्क इस तरह से नहीं संभाले जाएंगे।

+2

मुझे पता है कि यह अवांछित है, इसलिए मुझे ऐसा बुरा लगता है ... लेकिन मैं इन बगों से कैसे बच सकता हूं? हम सचमुच ब्रांड्स की एक जोड़ी खोने के बारे में बात कर रहे हैं .. थोड़ी देर में बचने के लिए लगभग असंभव है, नहीं? – max

+1

@max जैसा कि मैंने कहा, मुझे लगता है कि यह एक समस्या है कि आप अपनी विधि में स्ट्रिंग पुनरावृत्ति के साथ किसी समस्या से अधिक अपने तर्कों को कैसे बना रहे हैं। –

1

गैर-पुनरावर्तनीय स्ट्रिंग बनाने के बारे में आप क्या सोचते हैं?

class non_iter_str(str): 
    def __iter__(self): 
     yield self 

>>> my_str = non_iter_str('stackoverflow') 
>>> my_str 
'stackoverflow' 
>>> my_str[5:] 
'overflow' 
>>> for s in my_str: 
... print s 
... 
stackoverflow 
+0

यही वह है जो मैं मूल रूप से सोच रहा था; लेकिन @kindall ने इस नुकसान का उल्लेख दूसरों के बीच किया: "आपको हर जगह इसका उपयोग करना याद रखना होगा जो आप स्ट्रिंग का उपयोग करते हैं", जिसमें मेरे कोड के अन्य उपयोगकर्ताओं द्वारा भी शामिल है। – max

0
इसके बजाय अपने तार गैर iterable बनाने के लिए, जिस तरह से आप समस्या को देख रहे हैं स्विच कोशिश कर के

: अपने मानकों में से एक या तो एक iterable, या एक है ...

  • स्ट्रिंग
  • पूर्णांक
  • कस्टम वर्ग
  • आदि

जब आप अपना कार्य लिखते हैं, तो सबसे पहले आप अपने पैरामीटर को मान्य करते हैं, है ना?

def set_fields(record, fields, value): 
    if isinstance(fields, str): 
     fields = (fields,) # tuple-ize it! 
    for f in fields: 
     record[f] = value 

यह आपको अच्छी तरह से काम के रूप में आप अन्य कार्यों और पैरामीटर है कि या तो एकवचन, या इनका बहुवचन हो सकता है के साथ सौदा होगा।

+0

यह बहुत अस्पष्ट है। इस बात पर विचार करें कि आप एक सूची, या किसी अन्य इटरेटर को टुपल के बजाय उपयोग करना चाहते हैं? पायथन एक बतख-टाइप की गई भाषा है, टाइप-चेक करना एक अच्छा विचार नहीं है, यह भाषा के आदर्शों को निंदा करता है। –

+0

यह जांच न करें कि यह एक टुपल है। जांचें कि यह एक स्ट्रिंग या बाइट्स नहीं है। –

+0

@LennartRegebro: धन्यवाद - इसे एक अलग तरीके से सुनकर यह मेरे लिए क्लिक किया गया। उत्तर अपडेट किया गया। –

3

इस मामले में जांच प्रकार अनपेक्षित या खराब नहीं है। बस एक करें:

if isinstance(var, (str, bytes)): 
    var = [var] 

कॉल की शुरुआत में। या, यदि आप कॉलर को शिक्षित करना चाहते हैं:

if isinstance(var, (str, bytes)): 
    raise TypeError("Var should be an iterable, not str or bytes") 
संबंधित मुद्दे