2012-06-25 4 views
22

सरल प्रश्न:क्या पाइथन में लगातार "या" कथन लिखने का कोई अच्छा तरीका है? जो करने के लिए मैं अपने आप को करके किसी भी "अच्छा" उत्तर नहीं मिलता है

if 'foo' in mystring or 'bar' in mystring or 'hello' in mystring: 
    # Do something 
    pass 

or बयान की संख्या कहाँ काफी लंबे समय तक के आधार पर किया जा सकता है:

चलो कहते हैं कि मैं निम्नलिखित शर्त करते हैं स्थिति।

क्या प्रदर्शन करने के बलिदान के बिना इसे लिखने का एक "अच्छा" (अधिक पायथनिक) तरीका है?

यदि any() का उपयोग करने का विचार किया गया है लेकिन यह बूलियन-जैसी तत्वों की एक सूची लेता है, तो मुझे पहले उस सूची को बनाना होगा (प्रक्रिया में शॉर्ट सर्किट मूल्यांकन देना), इसलिए मुझे लगता है कि यह कम कुशल है।

बहुत बहुत धन्यवाद।

उत्तर

30

एक तरीका हो सकता है

if any(s in mystring for s in ('foo', 'bar', 'hello')): 
    pass 

बात तुम पर पुनरावृति एक टपल, जो समारोह के संकलन पर बनाया गया है, तो यह अपने मूल संस्करण से हीन नहीं होना चाहिए।

यदि आपको लगता है कि टपल बहुत लंबा हो जाएगा, तो आप

def mystringlist(): 
    yield 'foo' 
    yield 'bar' 
    yield 'hello' 
if any(s in mystring for s in mystringlist()): 
    pass 
+1

धन्यवाद। लेकिन क्या तकनीक शॉर्ट सर्किट अनुकूलन को रोकती है? – ereOn

+2

यह एक जनरेटर है, सूची नहीं। – johv

+9

नहीं। '(' foo ',' bar ',' हैलो 'में एस के लिए mystring में है' 'एक जनरेटर अभिव्यक्ति है, जिसका अर्थ है कि यह पूरी तरह से पूरी तरह से गणना के रूप में तुरंत गणना नहीं की जाती है। 'कोई भी()' पहले वास्तविक मूल्य को देखने पर पुनरावृत्ति को रोकता है, इसलिए बाकी की जांच कभी नहीं की जाएगी। जनरेटर अभिव्यक्तियों पर पढ़ें। – Kos

7

यह एक regex के लिए एक नौकरी की तरह लगता है कर सकता है।

import re 

if re.search("(foo|bar|hello)", mystring): 
    # Do something 
    pass 

यह भी तेज होना चाहिए। विशेष रूप से यदि आप समय से पहले regex संकलित करते हैं।

यदि आप नियमित रूप से नियमित अभिव्यक्ति उत्पन्न कर रहे हैं, तो आप यह सुनिश्चित करने के लिए re.escape() का उपयोग कर सकते हैं कि कोई विशेष वर्ण आपके रेगेक्स को तोड़ न दे। उदाहरण के लिए, यदि words, तार आप के लिए खोज करना चाहते हैं की एक सूची है आप इस तरह अपने पैटर्न उत्पन्न कर सकता है:

pattern = "(%s)" % ("|".join(re.escape(word) for word in words),) 

तुम भी ध्यान देना चाहिए कि अपने स्ट्रिंग n कैरेक्टर हैं आप m शब्द है और यदि, अपने मूल कोड में O(n*m) जटिलता है, जबकि नियमित अभिव्यक्ति में O(n) जटिलता है। यद्यपि पाइथन रेगेक्स वास्तव में सैद्धांतिक कॉम्प-साइंस नियमित अभिव्यक्ति नहीं हैं, और हमेशाO(n) जटिलता नहीं हैं, इस साधारण मामले में वे हैं।

+4

लेकिन आपको सावधान रहना होगा कि "शब्द" में से कोई भी विशेष रेगेक्स वर्ण –

+0

@gnibbler: True है। इसके विपरीत, आप पैटर्न मिलान का उपयोग करके कम कोड लिखने में सक्षम हो सकते हैं। यदि आप रेगेक्स को स्वतः उत्पन्न करने की तरह कुछ कर रहे हैं, तो आप 're.escape()' का उपयोग कर सकते हैं। – cha0site

+0

वास्तव में आप कर सकते हैं, आपको इसे अपने उत्तर –

2

चूंकि आप mystring के विरुद्ध शब्द-दर-शब्द संसाधित कर रहे हैं, निश्चित रूप से mystring को सेट के रूप में उपयोग किया जा सकता है। तो बस mystring में शब्द और शब्दों के समूहों को लक्षित युक्त सेट के बीच चौराहे ले:

In [370]: mystring=set(['foobar','barfoo','foo']) 

In [371]: mystring.intersection(set(['foo', 'bar', 'hello'])) 
Out[371]: set(['foo']) 

आपका तार्किक 'या' दो सेट के चौराहे के सदस्यों है।

एक सेट का उपयोग करना भी तेज़ है। यहाँ एक जनरेटर और नियमित अभिव्यक्ति बनाम रिश्तेदार समय कर रहे हैं:

f1: generator to test against large string 
f2: re to test against large string 
f3: set intersection of two sets of words 

    rate/sec  f2  f1  f3 
f2 101,333  -- -95.0% -95.5% 
f1 2,026,329 1899.7%  -- -10.1% 
f3 2,253,539 2123.9% 11.2%  -- 

तो एक जनरेटर और in आपरेशन के लिए रेगुलर एक्सप्रेशन से 19x तेजी से होता है और एक सेट चौराहे 21x एक regex की तुलना में तेजी और 11% एक जनरेटर की तुलना में तेजी है।

यहाँ कोड है कि समय उत्पन्न होता है:

import re 

with open('/usr/share/dict/words','r') as fin: 
    set_words={word.strip() for word in fin} 

s_words=' '.join(set_words) 
target=set(['bar','foo','hello']) 
target_re = re.compile("(%s)" % ("|".join(re.escape(word) for word in target),)) 

gen_target=(word for word in ('bar','foo','hello')) 

def f1(): 
    """ generator to test against large string """   
    if any(s in s_words for s in gen_target): 
     return True 

def f2(): 
    """ re to test against large string """ 
    if re.search(target_re, s_words): 
     return True 

def f3(): 
    """ set intersection of two sets of words """ 
    if target.intersection(set_words): 
     return True 

funcs=[f1,f2,f3] 
legend(funcs) 
cmpthese(funcs)   
+0

यह स्वीकृत उत्तर से अलग कैसे है? केवल अंतर यह है कि आप इसके बजाय 'सेट' का उपयोग करते हैं 'tuple' का, अन्यथा यह वही है। – cha0site

+0

@ cha0site: स्वीकृत उत्तर भी एक बड़ी सूची के लिए एक फ़ंक्शन का प्रस्ताव करता है। मुझे लगता है कि एक सेट ऐसा करने का बेहतर तरीका है।यह दो सेट का प्रस्ताव भी दे रहा है - 'किसी भी –

2

आप आइटम का एक ज्ञात सूची के खिलाफ जांच करने के लिए है, तो आप भी यह के रूप में

if mystring in ['foo', 'bar', 'hello']: 

लिख सकता है आप नहीं मिल सकता है तुलना आदेश सुनिश्चित करने के लाभ (मुझे नहीं लगता कि पायथन को सूची तत्वों को बाएं से दाएं जांचने की आवश्यकता है) लेकिन यह केवल एक समस्या है यदि आप जानते हैं कि 'foo' 'बार' से अधिक संभावना है।

+0

के उपयोग के बिना यह बिल्कुल वही बात नहीं है। प्रश्न में स्थिति भी सच होगी जब 'mystring =' हैलो वर्ल्ड '। यह एक नहीं होगा। – Izkata

+0

अच्छा बिंदु, धन्यवाद - जैसा कि आप कहते हैं, बिल्कुल वही बात नहीं है, और शायद विशिष्ट समस्या के लिए उपयुक्त नहीं है। – kimvanwyk

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

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