2012-05-14 5 views
11

पर रोक मैं एक समारोह है कि यह सच है लौटाता है यदि एक स्ट्रिंग अन्यथा एक सूची और झूठी में कम से कम एक नियमित अभिव्यक्ति से मेल खाता है। समारोह कहा जाता है अक्सर पर्याप्त है कि प्रदर्शन एक मुद्दा है।अजगर, सबसे तेज़ तरीका नियमित अभिव्यक्ति से अधिक पुनरावृति लेकिन पहले मैच

सीप्रोफाइल के माध्यम से इसे चलाने पर, फ़ंक्शन का लगभग 65% खर्च कर रहा है और इसकी समय सूची में 35% समय है।

मुझे लगता है कि मानचित्र() या कुछ का उपयोग करने का कोई तरीका होगा लेकिन मैं नहीं कर सकता क्योंकि यह एक मैच खोजने के बाद इसे फिर से चालू करने के तरीके के बारे में सोचता है।

वहां जल्द समारोह बनाने के लिए, जबकि अभी भी यह पहला मैच खोजने पर वापसी होने के लिए एक रास्ता है?

def matches_pattern(str, patterns): 
    for pattern in patterns: 
     if pattern.match(str): 
      return True 
    return False 

उत्तर

19

पहली बात यह है कि मन में आता है एक जनरेटर अभिव्यक्ति का उपयोग करके सी ओर करने के लिए लूप को आगे बढ़ाने जाता है:

def matches_pattern(s, patterns): 
    return any(p.match(s) for p in patterns) 

शायद तुम नहीं इसके लिए भी एक अलग समारोह की जरूरत है।

एक और चीज जिसे आप आजमा सकते हैं, | वैकल्पिक ऑपरेटर का उपयोग करके एकल, समग्र रेगेक्स बनाना है, ताकि इंजन को आपके लिए इसे अनुकूलित करने का मौका मिले। तुम भी, स्ट्रिंग प्रतिमानों की सूची से गतिशील रूप से regex बना सकते हैं अगर यह आवश्यक है:

def matches_pattern(s, patterns): 
    return re.match('|'.join('(?:%s)' % p for p in patterns), s) 

बेशक आप काम करने के लिए उस के लिए स्ट्रिंग के रूप में अपने regexes की आवश्यकता है। बस इन दोनों के प्रोफ़ाइल और जाँच जो एक है तेजी से :)

तुम भी एक general tip for debugging regular expressions in Python पर एक नजर है सकते हैं। यह अनुकूलित करने के अवसर खोजने में भी मदद कर सकता है।

अद्यतन: मैं उत्सुक था और एक छोटे से बेंचमार्क लिखा है:

import timeit 

setup = """ 
import re 
patterns = [".*abc", "123.*", "ab.*", "foo.*bar", "11010.*", "1[^o]*"]*10 
strings = ["asdabc", "123awd2", "abasdae23", "fooasdabar", "111", "11010100101", "xxxx", "eeeeee", "dddddddddddddd", "ffffff"]*10 
compiled_patterns = list(map(re.compile, patterns)) 

def matches_pattern(str, patterns): 
    for pattern in patterns: 
     if pattern.match(str): 
      return True 
    return False 

def test0(): 
    for s in strings: 
     matches_pattern(s, compiled_patterns) 

def test1(): 
    for s in strings: 
     any(p.match(s) for p in compiled_patterns) 

def test2(): 
    for s in strings: 
     re.match('|'.join('(?:%s)' % p for p in patterns), s) 

def test3(): 
    r = re.compile('|'.join('(?:%s)' % p for p in patterns)) 
    for s in strings: 
     r.match(s) 
""" 

import sys 
print(timeit.timeit("test0()", setup=setup, number=1000)) 
print(timeit.timeit("test1()", setup=setup, number=1000)) 
print(timeit.timeit("test2()", setup=setup, number=1000)) 
print(timeit.timeit("test3()", setup=setup, number=1000)) 

मेरी मशीन पर उत्पादन:

1.4120500087738037 
1.662621021270752 
4.729579925537109 
0.1489570140838623 

तो any अपने मूल दृष्टिकोण की तुलना में तेजी होने के लिए प्रतीत नहीं होता । गतिशील रूप से एक रेगेक्स का निर्माण करना भी वास्तव में तेज़ नहीं है। लेकिन अगर आप कई बार अग्रिम एक regex का निर्माण और इसका इस्तेमाल करने के लिए प्रबंधन कर सकते हैं, यह बेहतर प्रदर्शन हो सकती है। तुम भी कुछ अन्य विकल्पों :)

+0

''|'। जॉइन (पैटर्न) 'सुरक्षित नहीं है। रेगेक्स एक जटिल भाषा है। आप सुरक्षित होने के लिए प्रत्येक शब्द को '(?: ...)' में लपेटना चाहते हैं। –

+0

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

+0

धन्यवाद एक गुच्छा। मैं रेगेक्स को आगे के सामने बनाने की कोशिश करूंगा। इसके अलावा मुझे विश्वास नहीं है कि मैं 'किसी भी' के बारे में भूल गया था। – puffenstuff

7

तरीका यह सबसे तेजी से उन दोनों के बीच "|" साथ एक में सब regexes गठबंधन करने के लिए है करने के लिए है, तो एक regex मैच फोन करना। साथ ही, आप यह सुनिश्चित करने के लिए एक बार संकलित करना चाहेंगे कि आप दोहराए गए रेगेक्स संकलन से परहेज कर रहे हैं।

उदाहरण के लिए:

def matches_pattern(s, pats): 
    pat = "|".join("(%s)" % p for p in pats) 
    return bool(re.match(pat, s)) 

इस रूप में तार, पैटर्न संकलित नहीं pats के लिए है। क्या तुम सच में ही संकलित regexes है, तो फिर:

def matches_pattern(s, pats): 
    pat = "|".join("(%s)" % p.pattern for p in pats) 
    return bool(re.match(pat, s)) 
+0

आप उन्हें ओवरलैप के लिए विश्लेषण करके और कुल पैटर्न की संख्या को कम करके उन्हें अनुकूलित करने में सक्षम हो सकते हैं। –

2

ऊपर उत्कृष्ट उत्तर देने के लिए जोड़ा जा रहा है परीक्षण करने के लिए इस बेंचमार्क अनुकूलन, सुनिश्चित करें कि आप फिर से के उत्पादन की तुलना कर सकते हैं।किसी के साथ मेल नहीं:

>>> timeit('None is None') 
0.03676295280456543 
>>> timeit('bool(None)') 
0.1125330924987793 
>>> timeit('re.match("a","abc") is None', 'import re') 
1.0200879573822021 
>>> timeit('bool(re.match("a","abc"))', 'import re') 
1.134294033050537 
संबंधित मुद्दे