क्या यह पायथन के re.compile का उपयोग करने के लायक है?


461

क्या पायथन में नियमित अभिव्यक्ति के लिए संकलन का उपयोग करने में कोई लाभ है?

h = re.compile('hello')
h.match('hello world')

बनाम

re.match('hello', 'hello world')

8
अन्य तो तथ्य यह है कि 2.6 में re.subएक झंडे का तर्क नहीं लगेगा ...
new123456

58
मैं सिर्फ एक मामले में भाग गया, जहां उपयोग re.compileकरने से 10-50x सुधार हुआ। नैतिक यह है कि यदि आपके पास बहुत सारे रेगेक्स (MAXCACHE = 100 से अधिक) हैं और आप उन्हें प्रत्येक बार बहुत अधिक उपयोग करते हैं (और MAXCACHE regexes से अधिक के बीच में अलग किया जाता है, ताकि हर एक कैश से फ्लश हो जाए: तो उपयोग करना वही एक बहुत बार और फिर अगले एक पर चलते हुए गिनती नहीं होती है), तो यह निश्चित रूप से उन्हें संकलित करने में मदद करेगा। अन्यथा, इससे कोई फर्क नहीं पड़ता।
श्रीवत्सआर

8
ध्यान देने वाली एक छोटी बात यह है कि स्ट्रिंग्स के लिए जिसे रेगेक्स की आवश्यकता नहीं है, inस्ट्रिंग सबस्ट्रिंग टेस्ट बहुत तेजी से होता है:>python -m timeit -s "import re" "re.match('hello', 'hello world')" 1000000 loops, best of 3: 1.41 usec per loop >python -m timeit "x = 'hello' in 'hello world'" 10000000 loops, best of 3: 0.0513 usec per loop
गेमरिक्स

@ श्रीवत्स दिलचस्प है! क्या आप एक उदाहरण के साथ उत्तर पोस्ट कर सकते हैं जो 10x-50x सुधार दिखाता है? यहां दिए गए अधिकांश उत्तर वास्तव में कुछ सटीक मामलों में 3x सुधार दिखाते हैं, और अन्य मामलों में लगभग कोई सुधार नहीं होता है।
बसज

1
@ बस्ज दोन, एक जवाब पोस्ट किया । मैंने दिसंबर 2013 में पाइथन का उपयोग करने के लिए खुदाई करने की जहमत नहीं उठाई, लेकिन मैंने जो पहली सीधी-सादी चीज़ आजमाई, वही व्यवहार दिखाता है।
श्रीवत्सआर

जवाबों:


436

मेरे पास बहुत-सा अनुभव है जो संकलित रेगे के कई बार बनाम-ऑन-फ्लाई को संकलित करने का है, और किसी भी विचारनीय अंतर पर ध्यान नहीं दिया है। जाहिर है, यह किस्सा है, और निश्चित रूप से संकलन के खिलाफ एक महान तर्क नहीं है , लेकिन मैंने अंतर को नगण्य पाया है।

संपादित करें: वास्तविक पायथन 2.5 लाइब्रेरी कोड पर त्वरित नज़र डालने के बाद, मैं देखता हूं कि पायथन आंतरिक रूप से संकलन करता है और जब भी आप उन्हें उपयोग करते हैं, तब भी कॉल (जिसमें कॉल शामिल हैं re.match()) को रीग्रैक्सेज़ करते हैं , इसलिए आप वास्तव में केवल तभी बदल रहे हैं जब रीजन संकलित हो जाता है, और चाहिए ' t ज्यादा समय की बचत करना - केवल कैश की जांच करने में लगने वाला समय (आंतरिक dictप्रकार पर एक महत्वपूर्ण खोज )।

मॉड्यूल re.py (टिप्पणी मेरी है) से:

def match(pattern, string, flags=0):
    return _compile(pattern, flags).match(string)

def _compile(*key):

    # Does cache check at top of function
    cachekey = (type(key[0]),) + key
    p = _cache.get(cachekey)
    if p is not None: return p

    # ...
    # Does actual compilation on cache miss
    # ...

    # Caches compiled regex
    if len(_cache) >= _MAXCACHE:
        _cache.clear()
    _cache[cachekey] = p
    return p

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


12
आपका निष्कर्ष आपके उत्तर के साथ असंगत है। यदि रीगेक्स संकलित और स्वचालित रूप से संग्रहीत होते हैं, तो ज्यादातर मामलों में इसे हाथ से करने की कोई आवश्यकता नहीं है।
jfs

84
जेएफ सेबेस्टियन, यह प्रोग्रामर को एक संकेत के रूप में कार्य करता है कि प्रश्न में रेगेक्स बहुत उपयोग किया जाएगा और इसका मतलब नहीं है।
कलिसिन

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

20
यदि आप एक ही regex को कई बार री-यूज़ करते हैं, तो मुझे कंपोज़ रेगेक्स का उपयोग करने का मुख्य लाभ दिखाई देता है, जिससे टाइपोस के लिए संभावना कम हो जाती है। यदि आपका सिर्फ एक बार कॉल किया जाता है, तो अनकैप्ड अधिक पठनीय है।
1

18
इसलिए, मुख्य अंतर तब होगा जब आप बहुत से अलग-अलग रेगेक्स (_MAXCACHE से अधिक) का उपयोग कर रहे हों, उनमें से कुछ बस एक बार और दूसरे बहुत बार ... तो उन लोगों के लिए अपने संकलित अभिव्यक्तियों को रखना महत्वपूर्ण है जो अधिक उपयोग किए जाते हैं। जब यह भरा हुआ है तो कैश से बाहर नहीं निकाला जाता है।
फोरट्रान

133

मेरे लिए, सबसे बड़ा फायदा re.compile रेगेक्स को इसके उपयोग से अलग करने में सक्षम होना है।

यहां तक ​​कि एक सरल अभिव्यक्ति जैसे कि 0|[1-9][0-9]*(आधार शून्य में अग्रणी 10 के बिना पूर्णांक) इतना जटिल हो सकता है कि आपको इसे फिर से लिखना नहीं पड़ेगा, यह जांचना होगा कि क्या आपने कोई टाइपो बनाया है, और बाद में यदि डिबगिंग शुरू करते हैं तो टाइपोस हैं फिर से जाँचें। । इसके अलावा, यह संख्या या num_b10 जैसे चर नाम का उपयोग करने के लिए अच्छा है 0|[1-9][0-9]*

स्ट्रिंग्स को स्टोर करना और उन्हें फिर से इंस्टॉल करना निश्चित रूप से संभव है; हालाँकि, यह कम पठनीय है:

num = "..."
# then, much later:
m = re.match(num, input)

बनाम संकलन:

num = re.compile("...")
# then, much later:
m = num.match(input)

यद्यपि यह काफी करीब है, बार-बार उपयोग किए जाने पर दूसरी की अंतिम पंक्ति अधिक प्राकृतिक और सरल महसूस करती है।


5
मैं इस जवाब से सहमत हूं; बार-बार re.compile के परिणाम का उपयोग करने वाले अधिक, कम पठनीय कोड नहीं।
कार्ल मेयर

1
कभी-कभी विपरीत सच होता है, हालांकि - उदाहरण के लिए यदि आप एक स्थान पर रेगेक्स को परिभाषित करते हैं और इसके मिलान समूहों का उपयोग किसी अन्य दूर-दूर के स्थान पर करते हैं।
केन विलियम्स

1
@KenWilliams जरूरी नहीं है, एक विशिष्ट उद्देश्य के लिए एक अच्छी तरह से नामित रेगेक्स भी मूल परिभाषा से दूर इस्तेमाल होने पर भी स्पष्ट होना चाहिए। उदाहरण के लिए us_phone_numberया social_security_numberआदि
ब्रायन एम। शेल्डन

2
@ BrianM.Sheldon ने regex का नामकरण वास्तव में आपको यह जानने में मदद नहीं करता है कि इसके विभिन्न कैप्चरिंग समूह क्या दर्शाते हैं।
केन विलियम्स

68

FWIW:

$ python -m timeit -s "import re" "re.match('hello', 'hello world')"
100000 loops, best of 3: 3.82 usec per loop

$ python -m timeit -s "import re; h=re.compile('hello')" "h.match('hello world')"
1000000 loops, best of 3: 1.26 usec per loop

इसलिए, यदि आप एक ही रेगेक्स का उपयोग करने जा रहे हैं, तो यह करने के लिए लायक हो सकता है re.compile(विशेष रूप से अधिक जटिल रेगेक्स के लिए)।

समय से पहले अनुकूलन के खिलाफ मानक तर्क लागू होते हैं, लेकिन मुझे नहीं लगता कि आप वास्तव में बहुत स्पष्टता / सीधेपन खो देते हैं re.compileयदि आप संदेह करते हैं कि आपके regexps एक प्रदर्शन अड़चन बन सकते हैं।

अपडेट करें:

पायथन 3.6 के तहत (मुझे संदेह है कि उपरोक्त समय Python 2.x) और 2018 हार्डवेयर (मैकबुक प्रो) का उपयोग करके किया गया था, मुझे अब निम्नलिखित समय प्राप्त होता है:

% python -m timeit -s "import re" "re.match('hello', 'hello world')"
1000000 loops, best of 3: 0.661 usec per loop

% python -m timeit -s "import re; h=re.compile('hello')" "h.match('hello world')"
1000000 loops, best of 3: 0.285 usec per loop

% python -m timeit -s "import re" "h=re.compile('hello'); h.match('hello world')"
1000000 loops, best of 3: 0.65 usec per loop

% python --version
Python 3.6.5 :: Anaconda, Inc.

मैंने एक मामला भी जोड़ा (पिछले दो रनों के बीच अवतरण चिह्न अंतर देखें) जो दिखाता है कि re.match(x, ...)शाब्दिक [लगभग] के बराबर है re.compile(x).match(...), यानी संकलित प्रतिनिधित्व के पीछे कोई दृश्य-कैशिंग नहीं लगता है।


5
आपकी कार्यप्रणाली के साथ प्रमुख समस्याएं यहां हैं, क्योंकि सेटअप तर्क समय में शामिल नहीं है। इस प्रकार, आपने संकलन समय को दूसरे उदाहरण से हटा दिया है, और इसे पहले उदाहरण में औसत कर दिया है। इसका मतलब यह नहीं है कि पहला उदाहरण हर बार संकलन करता है।
त्रिफलक

1
हां, मैं मानता हूं कि यह दो मामलों की तुलना नहीं है।
किव

7
मैं देख रहा हूं कि आपका क्या मतलब है, लेकिन वास्तव में ऐसा नहीं है कि वास्तविक अनुप्रयोग में क्या होगा जहां regexp का उपयोग कई बार किया जाता है?
डीएफ।

26
@Triptych, @Kiv: संकलन के पूरे मुद्दे regexps उपयोग से अलग है संकलन कम करने के लिए; इसे समय से हटाना वास्तव में वही है जो dF को करना चाहिए था, क्योंकि यह वास्तविक दुनिया के उपयोग का सबसे सटीक रूप से प्रतिनिधित्व करता है। संकलन समय विशेष रूप से अप्रासंगिक है जिस तरह से timeit.py अपनी टाइमिंग यहाँ करता है; यह कई रन बनाता है और केवल सबसे छोटे से रिपोर्ट करता है, जिस बिंदु पर संकलित रेगेक्सपी को कैश किया जाता है। यहां आप जो अतिरिक्त लागत देख रहे हैं, वह regexp को संकलित करने की लागत नहीं है, बल्कि संकलित regexp कैश (एक शब्दकोश) में इसे देखने की लागत है।
jemfinch

3
@ ट्रीपिक import reको सेटअप से बाहर ले जाना चाहिए ? यह वह सब है जहाँ आप मापना चाहते हैं। अगर मैं कई बार एक पायथन स्क्रिप्ट चलाता हूं, तो यह import reसमय हिट होगा। दोनों की तुलना करते समय समय के लिए दो लाइनों को अलग करना महत्वपूर्ण है। हां जैसा कि आप कहते हैं कि यह तब है जब आपके पास समय होगा। तुलना से पता चलता है कि या तो आप एक बार हिट करने के लिए समय निकालते हैं और संकलन द्वारा कम हिट समय को दोहराते हैं या आप हर बार हिट लेते हुए मानते हैं कि कैश कॉल के बीच साफ हो जाता है, जैसा कि बताया गया है कि ऐसा हो सकता है। एक समय जोड़ने h=re.compile('hello')से स्पष्ट करने में मदद मिलेगी।
टॉम मायडेल्टन

39

यहाँ एक साधारण परीक्षण मामला है:

~$ for x in 1 10 100 1000 10000 100000 1000000; do python -m timeit -n $x -s 'import re' 're.match("[0-9]{3}-[0-9]{3}-[0-9]{4}", "123-123-1234")'; done
1 loops, best of 3: 3.1 usec per loop
10 loops, best of 3: 2.41 usec per loop
100 loops, best of 3: 2.24 usec per loop
1000 loops, best of 3: 2.21 usec per loop
10000 loops, best of 3: 2.23 usec per loop
100000 loops, best of 3: 2.24 usec per loop
1000000 loops, best of 3: 2.31 usec per loop

re.compile के साथ:

~$ for x in 1 10 100 1000 10000 100000 1000000; do python -m timeit -n $x -s 'import re' 'r = re.compile("[0-9]{3}-[0-9]{3}-[0-9]{4}")' 'r.match("123-123-1234")'; done
1 loops, best of 3: 1.91 usec per loop
10 loops, best of 3: 0.691 usec per loop
100 loops, best of 3: 0.701 usec per loop
1000 loops, best of 3: 0.684 usec per loop
10000 loops, best of 3: 0.682 usec per loop
100000 loops, best of 3: 0.694 usec per loop
1000000 loops, best of 3: 0.702 usec per loop

तो, यह प्रतीत होता है कि संकलन करना इस सरल मामले के साथ तेज़ है, भले ही आप केवल एक बार मेल खाते हों


2
यह पायथन का कौन सा संस्करण है?
काइल स्ट्रैंड

2
यह वास्तव में मायने नहीं रखता है, बिंदु उस वातावरण में बेंचमार्क आज़माने के लिए है जहां आप कोड चला रहे होंगे
david

1
मेरे लिए प्रदर्शन लगभग 1000 छोरों या अधिक के लिए लगभग समान है। संकलित संस्करण 1-100 छोरों के लिए तेज है। (दोनों अजगर 2.7 और 3.4 पर)।
Zitrax

2
मेरे पायथन 2.7.3 सेटअप पर शायद ही कोई अंतर है। कभी-कभी संकलन तेज होता है, कभी-कभी ist का धीमा। अंतर हमेशा <5% होता है, इसलिए मैं अंतर को अनिश्चितता को मापने के रूप में गिनता हूं, क्योंकि डिवाइस में केवल एक सीपीयू है।
दक्खन १on

1
पायथन में 3.4.3 को दो अलग-अलग रनों में देखा गया: संकलित का उपयोग करना संकलित नहीं की तुलना में भी धीमा था।
ज़ेल्फिर कल्टस्टाहल

17

मैंने बस खुद यह कोशिश की। एक स्ट्रिंग से एक संख्या को पार्स करने के सरल मामले के लिए और इसे समेटें, संकलित नियमित अभिव्यक्ति ऑब्जेक्ट का उपयोग करते हुए reविधियों का उपयोग करते हुए लगभग दोगुना है ।

जैसा कि अन्य लोगों ने बताया है, पहले संकलित अभिव्यक्तियों के reतरीकों में तरीकों (सहित re.compile) नियमित अभिव्यक्ति स्ट्रिंग को कैश में देखते हैं। इसलिए, सामान्य स्थिति में, reतरीकों का उपयोग करने की अतिरिक्त लागत केवल कैश लुकअप की लागत है।

हालांकि, कोड की जांच से पता चलता है कि कैश 100 अभिव्यक्तियों तक सीमित है। यह सवाल है, कैश ओवरफ्लो करना कितना दर्दनाक है? कोड नियमित अभिव्यक्ति संकलक करने के लिए एक आंतरिक इंटरफ़ेस शामिल हैं, re.sre_compile.compile। यदि हम इसे कॉल करते हैं, तो हम कैश को बायपास करते हैं। यह बुनियादी नियमित अभिव्यक्ति के लिए परिमाण धीरज के दो आदेशों के बारे में बताता है, जैसे किr'\w+\s+([0-9_]+)\s+\w*'

यहाँ मेरा परीक्षण है:

#!/usr/bin/env python
import re
import time

def timed(func):
    def wrapper(*args):
        t = time.time()
        result = func(*args)
        t = time.time() - t
        print '%s took %.3f seconds.' % (func.func_name, t)
        return result
    return wrapper

regularExpression = r'\w+\s+([0-9_]+)\s+\w*'
testString = "average    2 never"

@timed
def noncompiled():
    a = 0
    for x in xrange(1000000):
        m = re.match(regularExpression, testString)
        a += int(m.group(1))
    return a

@timed
def compiled():
    a = 0
    rgx = re.compile(regularExpression)
    for x in xrange(1000000):
        m = rgx.match(testString)
        a += int(m.group(1))
    return a

@timed
def reallyCompiled():
    a = 0
    rgx = re.sre_compile.compile(regularExpression)
    for x in xrange(1000000):
        m = rgx.match(testString)
        a += int(m.group(1))
    return a


@timed
def compiledInLoop():
    a = 0
    for x in xrange(1000000):
        rgx = re.compile(regularExpression)
        m = rgx.match(testString)
        a += int(m.group(1))
    return a

@timed
def reallyCompiledInLoop():
    a = 0
    for x in xrange(10000):
        rgx = re.sre_compile.compile(regularExpression)
        m = rgx.match(testString)
        a += int(m.group(1))
    return a

r1 = noncompiled()
r2 = compiled()
r3 = reallyCompiled()
r4 = compiledInLoop()
r5 = reallyCompiledInLoop()
print "r1 = ", r1
print "r2 = ", r2
print "r3 = ", r3
print "r4 = ", r4
print "r5 = ", r5
</pre>
And here is the output on my machine:
<pre>
$ regexTest.py 
noncompiled took 4.555 seconds.
compiled took 2.323 seconds.
reallyCompiled took 2.325 seconds.
compiledInLoop took 4.620 seconds.
reallyCompiledInLoop took 4.074 seconds.
r1 =  2000000
r2 =  2000000
r3 =  2000000
r4 =  2000000
r5 =  20000

'वास्तव मेंCompiled' तरीके आंतरिक इंटरफ़ेस का उपयोग करते हैं, जो कैश को बायपास करता है। ध्यान दें कि प्रत्येक लूप पुनरावृत्ति पर संकलन केवल 10,000 बार पुनरावृत्त होता है, एक मिलियन नहीं।


मैं आपसे सहमत हूं कि संकलित रेगेक्स गैर-संकलित की तुलना में बहुत तेजी से चलते हैं। मैं १०,००० से अधिक वाक्य चलाता हूं और रेगेक्स के लिए पुनरावृत्ति के लिए उनके लिए एक लूप बनाया जाता है जब रेगेक्स संकलित नहीं किए गए थे और हर बार गणना की गई थी कि पूर्ण रन की भविष्यवाणी 8 घंटे थी, कम्पोजिट किए गए बीजीएक्स पैटर्न के साथ इंडेक्स के अनुसार एक शब्दकोश बनाने के बाद मैं चलाता हूं। 2 मिनट के लिए पूरी बात। मैं ऊपर दिए गए उत्तरों को नहीं समझ सकता ...
एली बोरोदाच

12

मैं ईमानदार अबे से सहमत हूं कि match(...)दिए गए उदाहरण अलग हैं। वे एक-से-एक तुलना नहीं हैं और इस प्रकार, परिणाम अलग-अलग हैं। मेरे उत्तर को आसान बनाने के लिए, मैं प्रश्न में उन कार्यों के लिए A, B, C, D का उपयोग करता हूं। अरे हाँ, हम re.py3 के बजाय 4 कार्यों के साथ काम कर रहे हैं ।

इस कोड का टुकड़ा चलाना:

h = re.compile('hello')                   # (A)
h.match('hello world')                    # (B)

इस कोड को चलाने के समान है:

re.match('hello', 'hello world')          # (C)

क्योंकि, जब स्रोत में देखा जाता है re.py, तो (A + B) का अर्थ है:

h = re._compile('hello')                  # (D)
h.match('hello world')

और (C) वास्तव में है:

re._compile('hello').match('hello world')

तो, (C) (B) के समान नहीं है। वास्तव में, (C) कॉल (B) कॉल करने के बाद (D) जिसे (A) भी कहते हैं। दूसरे शब्दों में,(C) = (A) + (B)। इसलिए, लूप के अंदर (ए + बी) की तुलना लूप के अंदर (सी) के समान परिणाम है।

जॉर्ज regexTest.pyने हमारे लिए यह साबित किया।

noncompiled took 4.555 seconds.           # (C) in a loop
compiledInLoop took 4.620 seconds.        # (A + B) in a loop
compiled took 2.323 seconds.              # (A) once + (B) in a loop

सभी की रुचि है, 2.323 सेकंड का परिणाम कैसे प्राप्त करें। यह सुनिश्चित करने के लिए कि compile(...)केवल एक बार कॉल किया जाए, हमें मेमोरी में संकलित रेगेक्स ऑब्जेक्ट को स्टोर करने की आवश्यकता है। यदि हम एक कक्षा का उपयोग कर रहे हैं, तो हम ऑब्जेक्ट को स्टोर कर सकते हैं और हर बार जब हमारे फ़ंक्शन को कॉल किया जाता है, तब पुन: उपयोग कर सकते हैं।

class Foo:
    regex = re.compile('hello')
    def my_function(text)
        return regex.match(text)

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

एक और बात, मेरा मानना ​​है कि (A) + (B)दृष्टिकोण का उपयोग करने का एक ऊपरी हाथ है। यहाँ कुछ तथ्य दिए गए हैं जैसे मैंने देखा (कृपया मुझे गलत होने पर सही करें):

  1. एक बार कॉल, यह एक के _cacheबाद sre_compile.compile()एक regex ऑब्जेक्ट बनाने के लिए एक खोज करेंगे । ए को दो बार कॉल करता है, यह दो खोज और एक संकलन करेगा (क्योंकि रेगेक्स ऑब्जेक्ट कैश्ड है)।

  2. यदि _cacheबीच में प्रवाहित हो जाता है, तो रेगेक्स ऑब्जेक्ट को मेमोरी से जारी किया जाता है और अजगर को फिर से संकलित करने की आवश्यकता होती है। (कोई व्यक्ति सुझाव देता है कि पायथन पुनर्मिलन नहीं करेगा।)

  3. यदि हम regex ऑब्जेक्ट को (A) का उपयोग करके रखते हैं, तो regex ऑब्जेक्ट अभी भी _cache में आ जाएगा और किसी भी तरह से फ्लश हो जाएगा। लेकिन हमारा कोड इस पर एक संदर्भ रखता है और रेगेक्स ऑब्जेक्ट को मेमोरी से जारी नहीं किया जाएगा। उन, पायथन को फिर से संकलन करने की आवश्यकता नहीं है।

  4. जॉर्ज के परीक्षण में 2 सेकंड के अंतर को संकलित किया गया है। बनाम संकलित मुख्य रूप से कुंजी बनाने और _cache की खोज करने के लिए आवश्यक समय है। इसका मतलब यह नहीं है कि रेगेक्स का संकलन समय।

  5. जॉर्ज के वास्तव में परीक्षण से पता चलता है कि क्या होता है अगर यह वास्तव में हर बार संकलन करता है: यह 100x धीमा होगा (उसने लूप को 1,000,000 से 10,000 तक घटा दिया)।

यहाँ केवल ऐसे मामले हैं जो (A + B) (C) से बेहतर है:

  1. अगर हम किसी वर्ग के अंदर रेगेक्स ऑब्जेक्ट के संदर्भ को कैश कर सकते हैं।
  2. अगर हमें (B) बार-बार (लूप या कई बार के अंदर) कॉल करने की आवश्यकता है, तो हमें लूप के बाहर रेगेक्स ऑब्जेक्ट को रेफरेंस कैश करना होगा।

मामला (सी) काफी अच्छा है:

  1. हम संदर्भ को कैश नहीं कर सकते।
  2. हम केवल एक बार ही इसका उपयोग करते हैं।
  3. कुल मिलाकर, हमारे पास बहुत अधिक रेगेक्स नहीं हैं (मान लें कि संकलित व्यक्ति कभी भी प्रवाहित नहीं होता है)

बस एक पुनर्कथन, यहाँ एबीसी हैं:

h = re.compile('hello')                   # (A)
h.match('hello world')                    # (B)
re.match('hello', 'hello world')          # (C)

पढ़ने के लिए धन्यवाद।


8

अधिकतर, इस बात में कोई अंतर नहीं है कि आप re.compile का उपयोग करते हैं या नहीं। आंतरिक रूप से, सभी कार्यों को एक संकलन कदम के रूप में कार्यान्वित किया जाता है:

def match(pattern, string, flags=0):
    return _compile(pattern, flags).match(string)

def fullmatch(pattern, string, flags=0):
    return _compile(pattern, flags).fullmatch(string)

def search(pattern, string, flags=0):
    return _compile(pattern, flags).search(string)

def sub(pattern, repl, string, count=0, flags=0):
    return _compile(pattern, flags).sub(repl, string, count)

def subn(pattern, repl, string, count=0, flags=0):
    return _compile(pattern, flags).subn(repl, string, count)

def split(pattern, string, maxsplit=0, flags=0):
    return _compile(pattern, flags).split(string, maxsplit)

def findall(pattern, string, flags=0):
    return _compile(pattern, flags).findall(string)

def finditer(pattern, string, flags=0):
    return _compile(pattern, flags).finditer(string)

इसके अलावा, re.compile () अतिरिक्त अप्रत्यक्ष और कैशिंग तर्क को दरकिनार करता है:

_cache = {}

_pattern_type = type(sre_compile.compile("", 0))

_MAXCACHE = 512
def _compile(pattern, flags):
    # internal: compile pattern
    try:
        p, loc = _cache[type(pattern), pattern, flags]
        if loc is None or loc == _locale.setlocale(_locale.LC_CTYPE):
            return p
    except KeyError:
        pass
    if isinstance(pattern, _pattern_type):
        if flags:
            raise ValueError(
                "cannot process flags argument with a compiled pattern")
        return pattern
    if not sre_compile.isstring(pattern):
        raise TypeError("first argument must be string or compiled pattern")
    p = sre_compile.compile(pattern, flags)
    if not (flags & DEBUG):
        if len(_cache) >= _MAXCACHE:
            _cache.clear()
        if p.flags & LOCALE:
            if not _locale:
                return p
            loc = _locale.setlocale(_locale.LC_CTYPE)
        else:
            loc = None
        _cache[type(pattern), pattern, flags] = p, loc
    return p

Re.compile का उपयोग करने से होने वाली छोटी गति के लाभ के अलावा , लोग उस पठनीयता को भी पसंद करते हैं जो संभावित जटिल पैटर्न विनिर्देशों के नामकरण से आती है और जहां वे लागू होते हैं, उन्हें व्यावसायिक तर्क से अलग करती हैं:

#### Patterns ############################################################
number_pattern = re.compile(r'\d+(\.\d*)?')    # Integer or decimal number
assign_pattern = re.compile(r':=')             # Assignment operator
identifier_pattern = re.compile(r'[A-Za-z]+')  # Identifiers
whitespace_pattern = re.compile(r'[\t ]+')     # Spaces and tabs

#### Applications ########################################################

if whitespace_pattern.match(s): business_logic_rule_1()
if assign_pattern.match(s): business_logic_rule_2()

ध्यान दें, एक अन्य प्रतिवादी ने गलत तरीके से माना कि pyc फाइलें संकलित पैटर्न को सीधे संग्रहीत करती हैं; हालांकि, वास्तव में वे PYC लोड होने पर हर बार फिर से बनाए जाते हैं:

>>> from dis import dis
>>> with open('tmp.pyc', 'rb') as f:
        f.read(8)
        dis(marshal.load(f))

  1           0 LOAD_CONST               0 (-1)
              3 LOAD_CONST               1 (None)
              6 IMPORT_NAME              0 (re)
              9 STORE_NAME               0 (re)

  3          12 LOAD_NAME                0 (re)
             15 LOAD_ATTR                1 (compile)
             18 LOAD_CONST               2 ('[aeiou]{2,5}')
             21 CALL_FUNCTION            1
             24 STORE_NAME               2 (lc_vowels)
             27 LOAD_CONST               1 (None)
             30 RETURN_VALUE

उपरोक्त disassembly PYC फ़ाइल से tmp.pyयुक्त होता है:

import re
lc_vowels = re.compile(r'[aeiou]{2,5}')

1
है "में def search(pattern, string, flags=0):"लिखने में कोई त्रुटि?
फुलकव

1
ध्यान दें कि यदि patternपहले से ही एक संकलित पैटर्न है, तो कैशिंग ओवरहेड महत्वपूर्ण हो जाता है: हैशिंग एक SRE_Patternमहंगा है और पैटर्न को कैश करने के लिए कभी नहीं लिखा जाता है, इसलिए प्रत्येक बार लुकअप विफल हो जाता है KeyError
एरिक डुमिनील

5

सामान्य तौर पर, मुझे लगता है कि झंडे का उपयोग करना आसान है (कम से कम याद रखना आसान है), जैसे re.Iकि झंडे इनलाइन का उपयोग करने की तुलना में पैटर्न का संकलन करना।

>>> foo_pat = re.compile('foo',re.I)
>>> foo_pat.findall('some string FoO bar')
['FoO']

बनाम

>>> re.findall('(?i)foo','some string FoO bar')
['FoO']

आप किसी भी तरह से तीसरे तर्क के रूप में झंडे का उपयोग कर सकते हैं re.findall
एड्रॉक्स

5

दिए गए उदाहरणों का उपयोग करना:

h = re.compile('hello')
h.match('hello world')

मैच ऊपर के उदाहरण में विधि नीचे इस्तेमाल किया एक के समान नहीं है:

re.match('hello', 'hello world')

re.compile () एक रेग्युलर एक्सप्रेशन ऑब्जेक्ट लौटाता है , जिसका अर्थ hहै एक रेगेक्स ऑब्जेक्ट।

रेगुलर एक्सप्रेशन से वस्तु का अपना है मैच वैकल्पिक साथ विधि स्थिति और endpos पैरामीटर:

regex.match(string[, pos[, endpos]])

स्थिति

वैकल्पिक दूसरा पैरामीटर पोज़ स्ट्रिंग में एक इंडेक्स देता है जहां खोज शुरू करनी है; यह 0. को डिफॉल्ट करता है। यह पूरी तरह से स्ट्रिंग को समतल करने के बराबर नहीं है; '^'पैटर्न चरित्र स्ट्रिंग की असली शुरुआत में और सिर्फ एक नई पंक्ति के बाद पदों पर मेल खाता है, लेकिन जरूरी नहीं कि सूचकांक जहां खोज शुरू करने के लिए है पर।

endpos

वैकल्पिक पैरामीटर एंडपोज़ सीमा को दर्शाता है कि स्ट्रिंग कितनी दूर तक खोजा जाएगा; के रूप में अगर स्ट्रिंग है यह हो जाएगा endpos वर्ण लंबा, इसलिए केवल से पात्रों स्थिति के लिए endpos - 1एक मैच के लिए खोज की जाएगी। यदि endpos से भी कम है स्थिति , कोई मुकाबला नहीं मिल जाएगा; अन्यथा, यदि आरएक्स एक संकलित नियमित अभिव्यक्ति ऑब्जेक्ट है, तो rx.search(string, 0, 50)इसके बराबर है rx.search(string[:50], 0)

रेगुलर एक्सप्रेशन से वस्तु की खोज , findall , और finditer तरीकों में भी इन मानकों का समर्थन है।

re.match(pattern, string, flags=0)उन्हें समर्थन नहीं के रूप में आप देख सकते हैं करता है,
और न ही इसकी करता है खोज , findall , और finditer समकक्षों।

एक मैच ऑब्जेक्ट में ऐसे गुण होते हैं जो इन मापदंडों को पूरक करते हैं:

match.pos

पॉज़ का मान जो रेगेक्स ऑब्जेक्ट की खोज () या मैच () विधि से पारित किया गया था। यह स्ट्रिंग में सूचकांक है जिस पर आरई इंजन ने एक मैच की तलाश शुरू की।

match.endpos

एंडपोज़ का मूल्य जो रेगेक्स ऑब्जेक्ट की खोज () या मैच () विधि से पारित किया गया था। यह स्ट्रिंग में सूचकांक है जिसके आगे आरई इंजन नहीं जाएगा।


एक रेगेक्स ऑब्जेक्ट में दो अद्वितीय, संभवतः उपयोगी हैं, विशेषताएँ:

regex.groups

पैटर्न में कैप्चरिंग समूहों की संख्या।

regex.groupindex

समूह संख्याओं के लिए (? P) द्वारा परिभाषित किसी भी प्रतीकात्मक समूह के नाम का एक शब्दकोश। यदि कोई प्रतीकात्मक समूह पैटर्न में उपयोग नहीं किया गया था तो शब्दकोश खाली है।


और अंत में, एक मैच ऑब्जेक्ट में यह विशेषता है:

match.re

नियमित अभिव्यक्ति ऑब्जेक्ट जिसका मिलान () या खोज () विधि ने इस मिलान उदाहरण का उत्पादन किया।


4

प्रदर्शन अंतर एक तरफ, re.compile का उपयोग करके और मिलान करने के लिए संकलित नियमित अभिव्यक्ति ऑब्जेक्ट का उपयोग करके (जो भी नियमित अभिव्यक्ति संबंधित ऑपरेशन हैं) अर्थ को पायथन रन-टाइम के लिए स्पष्ट करता है।

मुझे कुछ सरल कोड डीबग करने का कुछ दर्दनाक अनुभव था:

compare = lambda s, p: re.match(p, s)

और बाद में मैं तुलना करूँगा

[x for x in data if compare(patternPhrases, x[columnIndex])]

जहां patternPhrasesएक चर को नियमित अभिव्यक्ति स्ट्रिंग माना जाता है,x[columnIndex] वाला एक चर है।

मुझे परेशानी थी जो patternPhrasesकुछ अपेक्षित स्ट्रिंग से मेल नहीं खाती थी!

लेकिन अगर मैंने re.compile फॉर्म का उपयोग किया है:

compare = lambda s, p: p.match(s)

फिर

[x for x in data if compare(patternPhrases, x[columnIndex])]

पायथन ने शिकायत की होगी कि "स्ट्रिंग में मैच की विशेषता नहीं है", जैसा कि स्थितीय तर्क मानचित्रण में होता है compare,x[columnIndex] नियमित अभिव्यक्ति के रूप में है!, जब मैं वास्तव में मतलब था!

compare = lambda p, s: p.match(s)

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

तो मेरे पाठ का नैतिक यह है कि जब नियमित अभिव्यक्ति सिर्फ शाब्दिक स्ट्रिंग नहीं है, तो मुझे अपनी धारणा को मुखर करने में मदद करने के लिए अजगर का उपयोग करने के लिए re.compile का उपयोग करना चाहिए।


4

Re.compile () का उपयोग करने का एक जोड़ है, re.xBOSE का उपयोग करके मेरे regex पैटर्न में टिप्पणी जोड़ने के रूप में।

pattern = '''
hello[ ]world    # Some info on my pattern logic. [ ] to recognize space
'''

re.search(pattern, 'hello world', re.VERBOSE)

यद्यपि यह आपके कोड को चलाने की गति को प्रभावित नहीं करता है, मैं इसे इस तरह से करना पसंद करता हूं क्योंकि यह मेरी टिप्पणी करने की आदत का हिस्सा है। जब मैं संशोधन करना चाहता हूं, तो मैं उस तर्क को याद करने में समय व्यतीत करने की कोशिश कर रहा हूं जो मेरे कोड के 2 महीने पीछे चला गया था।


1
मैंने आपका उत्तर संपादित कर दिया है। मुझे लगता है कि उल्लेख re.VERBOSEकरना सार्थक है, और यह कुछ ऐसा जोड़ देता है कि अन्य उत्तर बचे हुए लगते हैं। हालाँकि, "मैं यहाँ पोस्ट कर रहा हूँ क्योंकि मैं अभी तक कोई टिप्पणी नहीं कर सकता हूँ" के साथ आपके उत्तर का नेतृत्व करना निश्चित है कि इसे हटा दिया जाए। कृपया उत्तर के अलावा किसी अन्य चीज़ के लिए उत्तर बॉक्स का उपयोग न करें। आप कहीं भी (50 प्रतिनिधि) टिप्पणी करने में सक्षम होने से केवल एक या दो अच्छे उत्तर हैं, इसलिए कृपया धैर्य रखें। जब आप जानते हैं कि आप किसी भी तेज़ी से वहाँ नहीं पहुँचेंगे, तो उत्तर पेटियों में टिप्पणी डालना। यह आपको डाउनवोट और डिलीट किए गए उत्तर देगा।
skrrgwasme

4

पायथन प्रलेखन के अनुसार :

क्रम

prog = re.compile(pattern)
result = prog.match(string)

के बराबर है

result = re.match(pattern, string)

लेकिन उपयोग कर रहा है re.compile() पुन: उपयोग के लिए परिणामी नियमित अभिव्यक्ति ऑब्जेक्ट और सहेजना अधिक कुशल है जब अभिव्यक्ति को एक ही कार्यक्रम में कई बार उपयोग किया जाएगा।

इसलिए मेरा निष्कर्ष है, यदि आप कई अलग-अलग ग्रंथों के लिए एक ही पैटर्न से मेल खाने वाले हैं, तो आप इसे बेहतर तरीके से तैयार करते हैं।


3

दिलचस्प बात यह है कि संकलन मेरे लिए अधिक कुशल साबित होता है (विन XP पर अजगर 2.5.2):

import re
import time

rgx = re.compile('(\w+)\s+[0-9_]?\s+\w*')
str = "average    2 never"
a = 0

t = time.time()

for i in xrange(1000000):
    if re.match('(\w+)\s+[0-9_]?\s+\w*', str):
    #~ if rgx.match(str):
        a += 1

print time.time() - t

ऊपर दिए गए कोड को एक बार चलाने के बाद, और एक बार दोनों ifलाइनों के साथ दूसरे तरीके से टिप्पणी करने के बाद, संकलित रेगेक्स तेजी से दोगुना होता है


2
डीएफ के प्रदर्शन की तुलना के साथ ही मुद्दा। यह वास्तव में उचित नहीं है जब तक कि आप संकलन वक्तव्य के प्रदर्शन की लागत को शामिल न करें।
कार्ल मेयर

6
कार्ल, मैं असहमत हूं। संकलन को केवल एक बार निष्पादित किया जाता है, जबकि मिलान लूप को एक लाख बार निष्पादित किया जाता है
एली बेंडरस्की

@ एलिबेन: मैं कार्ल मेयर से सहमत हूं। संकलन दोनों मामलों में होता है। ट्राइपटिक में उल्लेख किया गया है कि कैशिंग शामिल है, इसलिए एक इष्टतम मामले में (फिर से कैश में रहता है) दोनों दृष्टिकोण ओ (एन + 1) हैं, हालांकि +1 भाग एक तरह का छिपा हुआ है जब आप स्पष्ट रूप से पुन: उपयोग नहीं करते हैं।
पेपरिका

1
अपना स्वयं का बेंचमार्किंग कोड न लिखें। मानक वितरण में शामिल timeit.py का उपयोग करना सीखें।
jemfinch

उस समय का कितना आप लूप के लिए पैटर्न स्ट्रिंग को फिर से बना रहे हैं। यह ओवरहेड तुच्छ नहीं हो सकता है।
IceArdor

3

यहां चर्चा पर ठोकर खाने से पहले मैंने यह परीक्षण किया। हालाँकि, इसे चलाने से मुझे लगा कि मैं कम से कम अपने परिणाम पोस्ट करूँगा।

मैंने जेफ फ्रीडल की "मास्टेरिंग रेगुलर एक्सप्रेशंस" में उदाहरण को चुराया और कमेंट किया। यह एक मैकबुक पर चल रहा है जो OSX 10.6 (2Ghz इंटेल कोर 2 डुओ, 4 जीबी रैम) है। पायथन संस्करण 2.6.1 है।

1 रन - re.compile का उपयोग कर

import re 
import time 
import fpformat
Regex1 = re.compile('^(a|b|c|d|e|f|g)+$') 
Regex2 = re.compile('^[a-g]+$')
TimesToDo = 1000
TestString = "" 
for i in range(1000):
    TestString += "abababdedfg"
StartTime = time.time() 
for i in range(TimesToDo):
    Regex1.search(TestString) 
Seconds = time.time() - StartTime 
print "Alternation takes " + fpformat.fix(Seconds,3) + " seconds"

StartTime = time.time() 
for i in range(TimesToDo):
    Regex2.search(TestString) 
Seconds = time.time() - StartTime 
print "Character Class takes " + fpformat.fix(Seconds,3) + " seconds"

Alternation takes 2.299 seconds
Character Class takes 0.107 seconds

रन 2 - re.compile का उपयोग नहीं कर रहा है

import re 
import time 
import fpformat

TimesToDo = 1000
TestString = "" 
for i in range(1000):
    TestString += "abababdedfg"
StartTime = time.time() 
for i in range(TimesToDo):
    re.search('^(a|b|c|d|e|f|g)+$',TestString) 
Seconds = time.time() - StartTime 
print "Alternation takes " + fpformat.fix(Seconds,3) + " seconds"

StartTime = time.time() 
for i in range(TimesToDo):
    re.search('^[a-g]+$',TestString) 
Seconds = time.time() - StartTime 
print "Character Class takes " + fpformat.fix(Seconds,3) + " seconds"

Alternation takes 2.508 seconds
Character Class takes 0.109 seconds

3

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

>>> ipr = r'\D+((([0-2][0-5]?[0-5]?)\.){3}([0-2][0-5]?[0-5]?))\D+'
>>> average(*timeit.repeat("re.match(ipr, 'abcd100.10.255.255 ')", globals={'ipr': ipr, 're': re}))
1.5077415757028423
>>> ipr = re.compile(ipr)
>>> average(*timeit.repeat("re.match(ipr, 'abcd100.10.255.255 ')", globals={'ipr': ipr, 're': re}))
1.8324008992184038
>>> average(*timeit.repeat("ipr.match('abcd100.10.255.255 ')", globals={'ipr': ipr, 're': re}))
0.9187896518778871

3

प्रदर्शन के अलावा।

उपयोग compileकरने से मुझे
1. मॉड्यूल (रे) की अवधारणाओं को भेद करने में मदद मिलती है ,
2. रेगेक्स ऑब्जेक्ट
3. मैच ऑब्जेक्ट
जब मैंने रेगेक्स सीखना शुरू किया

#regex object
regex_object = re.compile(r'[a-zA-Z]+')
#match object
match_object = regex_object.search('1.Hello')
#matching content
match_object.group()
output:
Out[60]: 'Hello'
V.S.
re.search(r'[a-zA-Z]+','1.Hello').group()
Out[61]: 'Hello'

एक पूरक के रूप में, मैंने reआपके संदर्भ के लिए मॉड्यूल की एक विस्तृत धोखा दे दिया ।

regex = {
'brackets':{'single_character': ['[]', '.', {'negate':'^'}],
            'capturing_group' : ['()','(?:)', '(?!)' '|', '\\', 'backreferences and named group'],
            'repetition'      : ['{}', '*?', '+?', '??', 'greedy v.s. lazy ?']},
'lookaround' :{'lookahead'  : ['(?=...)', '(?!...)'],
            'lookbehind' : ['(?<=...)','(?<!...)'],
            'caputuring' : ['(?P<name>...)', '(?P=name)', '(?:)'],},
'escapes':{'anchor'          : ['^', '\b', '$'],
          'non_printable'   : ['\n', '\t', '\r', '\f', '\v'],
          'shorthand'       : ['\d', '\w', '\s']},
'methods': {['search', 'match', 'findall', 'finditer'],
              ['split', 'sub']},
'match_object': ['group','groups', 'groupdict','start', 'end', 'span',]
}

2

मैं वास्तव में उपरोक्त सभी उत्तरों का सम्मान करता हूं। मेरी राय से हाँ! यह सुनिश्चित करने के लिए कि यह regex संकलन के बजाय re.compile का उपयोग करने के लायक है, बार-बार, हर बार।

Re.compile का उपयोग करने से आपका कोड अधिक गतिशील हो जाता है, क्योंकि आप पहले से संकलित regex को कॉल कर सकते हैं, बजाय फिर से संकलन किए और aagain के। यह बात आपको मामलों में लाभान्वित करती है:

  1. प्रोसेसर के प्रयास
  2. समय जटिलता।
  3. यूनिवर्सल regex बनाता है। (इसे खोज, खोज, मिलान में उपयोग किया जा सकता है)
  4. और आपके कार्यक्रम को शानदार बनाता है।

उदाहरण :

  example_string = "The room number of her room is 26A7B."
  find_alpha_numeric_string = re.compile(r"\b\w+\b")

Findall में उपयोग करना

 find_alpha_numeric_string.findall(example_string)

खोज में उपयोग करना

  find_alpha_numeric_string.search(example_string)

इसी तरह आप इसके लिए उपयोग कर सकते हैं: मिलान और स्थानापन्न


1

यह अच्छा प्रश्न है। आप अक्सर लोगों को बिना कारण re.compile का उपयोग करते देखते हैं। इससे पठनीयता कम होती है। लेकिन यकीन है कि बहुत बार ऐसा होता है जब अभिव्यक्ति को पूर्व-संकलन के लिए कहा जाता है। जैसे जब आप इसे एक लूप में बार-बार इस्तेमाल करते हैं या कुछ ऐसे।

यह प्रोग्रामिंग के बारे में सब कुछ जैसा है (वास्तव में जीवन में सब कुछ)। सामान्य ज्ञान लागू करें।


जहाँ तक मैं अपने संक्षिप्त फ्लिक के माध्यम से बता सकता हूँ, संक्षेप में Python बिना re.compile () के उपयोग का उल्लेख नहीं करता है, जिसने मुझे उत्सुक बना दिया।
Mat

रेगेक्स ऑब्जेक्ट संदर्भ में एक और ऑब्जेक्ट जोड़ता है। जैसा कि मैंने कहा, ऐसी कई स्थितियाँ हैं जहाँ re.compile () का स्थान है। ओपी द्वारा दिया गया उदाहरण उनमें से एक नहीं है।
PEZ

1

(महीनों बाद) अपने स्वयं के कैश को re.match के आसपास जोड़ना आसान है, या उस मामले के लिए कुछ और -

""" Re.py: Re.match = re.match + cache  
    efficiency: re.py does this already (but what's _MAXCACHE ?)
    readability, inline / separate: matter of taste
"""

import re

cache = {}
_re_type = type( re.compile( "" ))

def match( pattern, str, *opt ):
    """ Re.match = re.match + cache re.compile( pattern ) 
    """
    if type(pattern) == _re_type:
        cpat = pattern
    elif pattern in cache:
        cpat = cache[pattern]
    else:
        cpat = cache[pattern] = re.compile( pattern, *opt )
    return cpat.match( str )

# def search ...

एक wibni, यह अच्छा नहीं होगा अगर: cachehint (size =), cacheinfo () -> आकार, हिट, nclear ...


1

मेरे पास बहुत-सा अनुभव है जो संकलित रेग के कई बार बनाम-ऑन-फ्लाई को संकलित करता है, और किसी भी विचारशील अंतर पर ध्यान नहीं दिया है।

स्वीकृत उत्तर पर वोट इस धारणा की ओर जाता है कि @ त्रिपिटिक जो कहते हैं वह सभी मामलों के लिए सही है। आवश्यक रूप से यह सही नहीं है। एक बड़ा अंतर यह है कि आपको यह तय करना है कि किसी फ़ंक्शन के पैरामीटर के रूप में regex स्ट्रिंग या संकलित regex ऑब्जेक्ट स्वीकार करना है या नहीं:

>>> timeit.timeit(setup="""
... import re
... f=lambda x, y: x.match(y)       # accepts compiled regex as parameter
... h=re.compile('hello')
... """, stmt="f(h, 'hello world')")
0.32881879806518555
>>> timeit.timeit(setup="""
... import re
... f=lambda x, y: re.compile(x).match(y)   # compiles when called
... """, stmt="f('hello', 'hello world')")
0.809190034866333

यदि आप उन्हें पुन: उपयोग करने की आवश्यकता है, तो अपने regexs को संकलित करना हमेशा बेहतर होता है।

ऊपर दिए गए समय में उदाहरण पर ध्यान दें एक मैच के लिए आवश्यक समय बनाम "ऑन-द-फ्लाई" आयात समय पर एक संकलित रेगेक्स ऑब्जेक्ट के निर्माण का अनुकरण करता है।


1

एक वैकल्पिक उत्तर के रूप में, जैसा कि मैं देख रहा हूं कि यह पहले उल्लेख नहीं किया गया है, मैं आगे जाऊंगा और पायथन 3 डॉक्स उद्धृत करूंगा :

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


1

यहां एक उदाहरण दिया गया है जहां अनुरोध के अनुसारre.compile 50 गुना अधिक तेजी से उपयोग किया जाता है

बिंदु सिर्फ वही है जो मैंने ऊपर टिप्पणी में बनाया है, अर्थात्, उपयोग करने re.compileसे एक महत्वपूर्ण लाभ हो सकता है जब आपका उपयोग ऐसा हो जैसे कि संकलन कैश से अधिक लाभ न हो। यह कम से कम एक विशेष मामले में होता है (कि मैं अभ्यास में भाग गया), अर्थात् जब निम्नलिखित सभी सत्य हैं:

  • आपके पास बहुत से रेगेक्स पैटर्न हैं (अधिक से अधिक re._MAXCACHE, जिनकी डिफ़ॉल्ट है वर्तमान में 512 है), और
  • आप इन रेगेक्स का बहुत बार उपयोग करते हैं, और
  • आप एक ही पैटर्न के लगातार usages के re._MAXCACHEबीच में अन्य regexes से अधिक द्वारा अलग कर रहे हैं , ताकि प्रत्येक एक लगातार कैश के बीच कैश से फ्लश हो जाता है।
import re
import time

def setup(N=1000):
    # Patterns 'a.*a', 'a.*b', ..., 'z.*z'
    patterns = [chr(i) + '.*' + chr(j)
                    for i in range(ord('a'), ord('z') + 1)
                    for j in range(ord('a'), ord('z') + 1)]
    # If this assertion below fails, just add more (distinct) patterns.
    # assert(re._MAXCACHE < len(patterns))
    # N strings. Increase N for larger effect.
    strings = ['abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'] * N
    return (patterns, strings)

def without_compile():
    print('Without re.compile:')
    patterns, strings = setup()
    print('searching')
    count = 0
    for s in strings:
        for pat in patterns:
            count += bool(re.search(pat, s))
    return count

def without_compile_cache_friendly():
    print('Without re.compile, cache-friendly order:')
    patterns, strings = setup()
    print('searching')
    count = 0
    for pat in patterns:
        for s in strings:
            count += bool(re.search(pat, s))
    return count

def with_compile():
    print('With re.compile:')
    patterns, strings = setup()
    print('compiling')
    compiled = [re.compile(pattern) for pattern in patterns]
    print('searching')
    count = 0
    for s in strings:
        for regex in compiled:
            count += bool(regex.search(s))
    return count

start = time.time()
print(with_compile())
d1 = time.time() - start
print(f'-- That took {d1:.2f} seconds.\n')

start = time.time()
print(without_compile_cache_friendly())
d2 = time.time() - start
print(f'-- That took {d2:.2f} seconds.\n')

start = time.time()
print(without_compile())
d3 = time.time() - start
print(f'-- That took {d3:.2f} seconds.\n')

print(f'Ratio: {d3/d1:.2f}')

उदाहरण उत्पादन मुझे अपने लैपटॉप पर मिलता है (पाइथन 3.7.7):

With re.compile:
compiling
searching
676000
-- That took 0.33 seconds.

Without re.compile, cache-friendly order:
searching
676000
-- That took 0.67 seconds.

Without re.compile:
searching
676000
-- That took 23.54 seconds.

Ratio: 70.89

मैं परेशान नहीं था timeitक्योंकि अंतर इतना निरा है, लेकिन मुझे हर बार गुणात्मक रूप से समान संख्याएं मिलती हैं। ध्यान दें कि बिना भी re.compile, एक ही रेगेक्स का कई बार उपयोग करते हुए और अगले पर चलते हुए इतना बुरा नहीं था (केवल लगभग 2 बार जितना धीमा re.compile), लेकिन दूसरे क्रम में (कई रेगीक्स के माध्यम से लूपिंग), यह काफी खराब है , जैसा सोचा था। इसके अलावा, कैश आकार बढ़ाकर भी काम करता है: बस स्थापित करने re._MAXCACHE = len(patterns)में setup()ऊपर (निश्चित रूप से मैं उत्पादन में ऐसी बातें कर रही है की सलाह नहीं देते अंडरस्कोर से नाम पारंपरिक "निजी" कर रहे हैं के रूप में) चला जाता है ~ 23 सेकंड ~ 0.7 सेकंड, को वापस नीचे जो भी हमारी समझ से मेल खाता है।


पुनश्च: यदि मैं अपने पूरे कोड में केवल 3 रेगेक्स पैटर्न का उपयोग करता हूं, तो उनमें से प्रत्येक ने सैकड़ों बार (बिना किसी विशिष्ट आदेश के) का उपयोग किया, रेगेक्स कैश प्रीजेम्प्ड रेगेक्स को स्वचालित रूप से रखेगा, क्या यह सही है?
बसज

@ बस्ज मुझे लगता है कि आप बस इसे आज़मा सकते हैं और देख सकते हैं :) लेकिन जवाब, मुझे पूरा यकीन है, हाँ: उस मामले में एकमात्र अतिरिक्त लागत AFAICT बस कैश में पैटर्न को देख रही है । यह भी ध्यान दें कि कैश वैश्विक (मॉड्यूल-स्तर) है, इसलिए सिद्धांत रूप में आप कुछ निर्भरता वाले पुस्तकालय को अपने बीच में regex खोज कर सकते हैं, इसलिए यह पूरी तरह से आश्वस्त होना मुश्किल है कि आपका प्रोग्राम केवल 3 (या जो भी) regex का उपयोग करता है पैटर्न, लेकिन यह अन्यथा होने के लिए बहुत अजीब होगा :)
श्रीवत्सआर

0

दूसरे संस्करण का उपयोग करते समय उपयोग किए जाने से पहले नियमित अभिव्यक्तियों को संकलित किया जाता है। यदि आप इसे कई बार निष्पादित करने जा रहे हैं, तो पहले इसे संकलित करना निश्चित रूप से बेहतर है। यदि आप हर बार संकलन नहीं कर रहे हैं तो एक-दूसरे के लिए मैच ठीक है।


0

वैधता / संज्ञानात्मक भार वरीयता

मेरे लिए, मुख्य लाभ यह है कि मुझे केवल याद रखने और पढ़ने की आवश्यकता है, एक - जटिल regex API सिंटैक्स के रूप <compiled_pattern>.method(xxx)बल्कि उससे भी प्रपत्र औरre.func(<pattern>, xxx) प्रपत्र।

re.compile(<pattern>)अतिरिक्त बॉयलरप्लेट का एक सा, सच है।

लेकिन जहां रेगेक्स का संबंध है, अतिरिक्त संकलन कदम संज्ञानात्मक भार का एक बड़ा कारण होने की संभावना नहीं है। और वास्तव में, जटिल पैटर्न पर, आप घोषणा को अलग करने से स्पष्टता प्राप्त कर सकते हैं जो भी regex पद्धति से आप उस पर आह्वान करते हैं।

मैं Regex101 जैसी वेबसाइट में पहले जटिल पैटर्न को ट्यून करता हूं, या एक अलग न्यूनतम टेस्ट स्क्रिप्ट में भी, फिर उन्हें अपने कोड में लाता हूं, इसलिए इसके उपयोग से घोषणा को अलग करना मेरे वर्कफ़्लो को भी फिट करता है।


-1

मैं इस बात को प्रेरित करना चाहूंगा कि पूर्व-संकलित करना, वैचारिक और 'साक्षर' (जैसा कि 'साक्षर प्रोग्रामिंग') लाभप्रद है। इस कोड स्निपेट पर एक नज़र डालें:

from re import compile as _Re

class TYPO:

  def text_has_foobar( self, text ):
    return self._text_has_foobar_re_search( text ) is not None
  _text_has_foobar_re_search = _Re( r"""(?i)foobar""" ).search

TYPO = TYPO()

आपके आवेदन में, आप लिखेंगे:

from TYPO import TYPO
print( TYPO.text_has_foobar( 'FOObar ) )

यह कार्यक्षमता के मामले में उतना ही सरल है जितना इसे प्राप्त किया जा सकता है। क्योंकि यह उदाहरण बहुत छोटा है, इसलिए मैंने _text_has_foobar_re_searchसभी को एक पंक्ति में लाने का तरीका बताया । इस कोड का नुकसान यह है कि यह TYPOलाइब्रेरी ऑब्जेक्ट के जीवनकाल के लिए थोड़ी सी मेमोरी को अधिग्रहित करता है; फायदा यह है कि जब आप फ़ोबार खोज करते हैं, तो आप दो फ़ंक्शन कॉल और दो क्लास शब्दकोश लुकअप के साथ भाग लेंगे। कितने regexes द्वारा कैश किया जाता हैre और उस कैश का ओवरहेड यहाँ अप्रासंगिक है।

नीचे सामान्य शैली के साथ इसकी तुलना करें:

import re

class Typo:

  def text_has_foobar( self, text ):
    return re.compile( r"""(?i)foobar""" ).search( text ) is not None

आवेदन में:

typo = Typo()
print( typo.text_has_foobar( 'FOObar ) )

मैं आसानी से स्वीकार करता हूं कि मेरी शैली अजगर के लिए अत्यधिक असामान्य है, शायद बहस करने योग्य भी। हालाँकि, इस उदाहरण में कि अधिक बारीकी से मेल खाता है कि अजगर का उपयोग कैसे किया जाता है, एक ही मैच करने के लिए, हमें एक ऑब्जेक्ट को तुरंत करना चाहिए, तीन उदाहरण शब्दकोश लुकअप करना चाहिए, और तीन फ़ंक्शन कॉल करना चाहिए; इसके अतिरिक्त, हम इसमें प्रवेश कर सकते हैंre 100 से अधिक रेग्जेस का उपयोग कैशिंग की परेशानी । इसके अलावा, नियमित अभिव्यक्ति विधि शरीर के अंदर छिप जाती है, जो कि ज्यादातर समय ऐसा अच्छा विचार नहीं है।

यह कहा जाता है कि उपायों के हर सबसेट --- लक्षित, aliased आयात बयान; अलियास तरीके जहां लागू हो; फंक्शन कॉल्स और ऑब्जेक्ट डिक्शनरी लुक्स में कमी --- कम्प्यूटेशनल और वैचारिक जटिलता को कम करने में मदद कर सकती है।


2
WTF। इतना ही नहीं आप एक पुराने, उत्तरित प्रश्न को खोद लेते हैं। आपका कोड गैर-मुहावरेदार है और साथ ही कई स्तरों पर गलत है - (ab) नामस्थानों के रूप में कक्षाओं का उपयोग करते हुए जहां एक मॉड्यूल पर्याप्त है, वर्ग के नामों को कैपिटलाइज़ करना, आदि ... बेहतर कार्यान्वयन के लिए pastebin.com/iTAXAWen देखें । आपके द्वारा उपयोग किए जाने वाले रेगेक्स का उल्लेख नहीं है, वह भी टूटा हुआ है। कुल मिलाकर, -1

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

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

"खराब लिखा हुआ" - जैसे आखिर क्यों? "सम्मेलनों और मुहावरों को धता बताता है" - मैंने आपको चेतावनी दी थी। "बिना किसी कारण के" - हां मेरे पास एक कारण है: सरल करें जहां जटिलता कोई उद्देश्य नहीं रखती है; "समय से पहले अनुकूलन के अवतार" - मैं एक प्रोग्रामिंग शैली के लिए बहुत अधिक हूं जो पठनीयता और दक्षता का संतुलन चुनता है; ओपी ने "re.compile का उपयोग करने में लाभ" के बारे में पूछा, जिसे मैं दक्षता के बारे में एक प्रश्न के रूप में समझता हूं। "(ab) वर्गों को नामस्थान के रूप में उपयोग करना" - यह आपके शब्द हैं जो अपमानजनक हैं। क्लास है इसलिए आपके पास एक "सेल्फ" पॉइंट-ऑफ-रेफरेंस है। मैंने इस उद्देश्य के लिए मॉड्यूल का उपयोग करने की कोशिश की, कक्षाएं बेहतर काम करती हैं।
प्रवाह करें

"बड़े वर्ग के नाम", "नहीं, यह PEP8 के बारे में नहीं है" - आप जाहिरा तौर पर इतनी नाराजगी से नाराज हैं कि आप पहले क्या बात करना चाहते हैं, इसके बारे में भी नहीं बता सकते। "डब्ल्यूटीएफ", " गलत " --- देखें कि आप कितने भावुक हैं? अधिक निष्पक्षता और कम झाग कृपया।
प्रवाह करें

-5

मेरी समझ यह है कि वे दो उदाहरण प्रभावी रूप से समतुल्य हैं। एकमात्र अंतर यह है कि पहले में, आप संकलित नियमित अभिव्यक्ति को कहीं और फिर से उपयोग कर सकते हैं, बिना इसे फिर से संकलित किए बिना।

यहाँ आपके लिए एक संदर्भ है: http://diveintopython3.ep.io/refactoring.html

स्ट्रिंग 'एम' के साथ संकलित पैटर्न ऑब्जेक्ट के खोज फ़ंक्शन को कॉल करना एक ही बात को पूरा करता है, जिसमें नियमित अभिव्यक्ति और स्ट्रिंग 'एम' दोनों के साथ पुन: खोज किया जाता है। केवल बहुत, बहुत तेज। (वास्तव में, re.search फ़ंक्शन केवल नियमित अभिव्यक्ति को संकलित करता है और आपके लिए परिणामी पैटर्न ऑब्जेक्ट की खोज विधि कहता है।)


1
मैंने आपको निराश नहीं किया, लेकिन तकनीकी रूप से यह गलत है: पायथन वैसे भी फिर से नहीं बनेगा
Triptych
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.