Python'un yeniden derlenmesini kullanmaya değer mi?


462

Python'da düzenli ifadeler için derleme kullanmanın herhangi bir faydası var mı?

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

vs

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

8
Diğer ardından 2.6'da olması re.sub... Bir bayrak argüman almayacaktır
new123456

58
Sadece kullanmanın re.compile10-50x iyileşme sağladığı bir durumla karşılaştım. Ahlaki olduğunu eğer sen (Maxcache = 100 'den fazla) regexes çok şey var ve sen Maxcache daha bu kişilere kez her biri için (ve ayrılmış bir sürü kullanmak böylece her biri önbellekten kızarmış alır ki aradaki Regexes: böylece kullanarak aynı şeyi birçok kez ve sonra bir sonrakine geçmek sayılmaz), o zaman kesinlikle onları derlemeye yardımcı olacaktır. Aksi takdirde, bir fark yaratmaz.
ShreevatsaR

8
Dikkat edilmesi gereken küçük bir şey, regex gerektirmeyen dizeler için indize alt dize testinin çok daha hızlı olmasıdır:>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
Gamrix

@ShreevatsaR İlginç! 10x-50x gelişme gösteren bir örnekle cevap gönderebilir misiniz? Burada verilen cevapların çoğu, bazı kesin durumlarda 3 kat iyileşme gösterirken, diğer durumlarda neredeyse hiç iyileşme göstermez.
Basj

1
@Basj Done, bir cevap gönderdi . Aralık 2013'te Python'u kullandığımı kazmak için uğraşmadım, ancak ilk denediğim şey aynı davranışı gösteriyor.
ShreevatsaR

Yanıtlar:


436

Anında derleme ile 1000 kez derlenmiş normal regex çalıştırma deneyimim oldu ve herhangi bir algılanabilir fark fark etmedim. Açıkçası, bu anekdot ve kesinlikle derlemeye karşı büyük bir argüman değil , ama farkı ihmal edilebilir buldum.

DÜZENLEME: Gerçek Python 2.5 kitaplık koduna hızlı bir bakıştan sonra, Python'un bunları zaten kullandığınızda (çağrılar dahil re.match()) içsel olarak derlediğini ve önbelleğe aldığını görüyorum , bu yüzden gerçekten sadece normal ifade derlendiğinde değişmeli ve t Çok fazla zaman tasarrufu sağlamak - sadece önbelleği kontrol etmek için gereken süre (dahili dicttipte anahtar arama ).

Re.py modülünden (yorumlar benimdir):

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

Sık sık düzenli ifadeleri önceden derliyorum, ancak bunları beklenen performans artışı için değil, güzel, yeniden kullanılabilir bir isme bağlamak için.


12
Sonucunuz cevabınızla tutarsız. Normal ifadeler otomatik olarak derlenir ve saklanırsa, çoğu durumda bunu elle yapmaya gerek yoktur.
jfs

84
JF Sebastian, programcıya söz konusu regexp'in çok fazla kullanılacağına dair bir işaret görevi görüyor ve bir ıskarta olma amaçlı değil.
kaleissin

40
Dahası, uygulamanızın performans açısından kritik bazı bölümlerinde derleme ve önbellek isabetine maruz kalmak istemiyorsanız, uygulamanızın kritik olmayan bir bölümünde elden önce bunları derlemek için en iyisi olduğunu söyleyebilirim. .
Eddie Parker

20
Aynı regex'i birden çok kez kullanırsanız, yazım hatası olasılığını azaltırsanız, derlenmiş normal ifadeyi kullanmanın ana avantajını görüyorum. Eğer sadece bir kez çağırırsanız derlenmemiş daha okunabilir.
monkut

18
Bu nedenle, temel fark, bazıları sadece bir kez ve diğerleri birçok kez farklı regex (_MAXCACHE'den daha fazla) kullandığınızda olacaktır ... o zaman derlenmiş ifadelerinizi daha çok kullanılanlar için tutmak önemlidir. dolduğunda önbellekten temizlenmez.
fortran

133

Benim için en büyük fayda re.compile, normal ifadenin tanımını kullanımından ayırabilmektir.

0|[1-9][0-9]*(Baştaki sıfırlarda sıfır olmadan tamsayı) gibi basit bir ifade bile , yeniden yazmayı, yazım hatası yapıp yapmadığınızı kontrol etmeyi ve daha sonra hata ayıklamaya başladığınızda yazım hatası olup olmadığını tekrar kontrol etmeniz gerekmeyecek kadar karmaşık olabilir. . Ayrıca, num veya num_b10 gibi bir değişken adı kullanmak daha iyidir 0|[1-9][0-9]*.

Dizeleri saklamak ve bunları yeniden eşleştirmek için kesinlikle mümkündür; ancak, bu daha az okunabilir:

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

Derlemeye karşı:

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

Oldukça yakın olmasına rağmen, ikincisinin son satırı tekrar tekrar kullanıldığında daha doğal ve daha basit hissediyor.


5
Bu yanıta katılıyorum; re.compile komutunun kullanılması çoğu zaman daha az okunabilir kodla sonuçlanmaz.
Carl Meyer

1
Bazen bunun tersi de geçerlidir - örneğin normal ifadeyi bir yerde tanımlar ve eşleşen gruplarını başka bir yerde kullanırsanız.
Ken Williams

1
@KenWilliams Belirli bir amaç için iyi adlandırılmış bir normal ifade, orijinal tanımdan uzakta kullanıldığında bile net olmalıdır. Örneğin us_phone_numberveya social_security_numberbenzeri
Brian M. Sheldon

2
Regex'i iyi adlandırmak, çeşitli yakalama gruplarının neyi temsil ettiğini bilmenize yardımcı olmaz.
Ken Williams

69

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

Bu nedenle, aynı normal ifadeyi çok fazla kullanacaksanız, yapmaya re.compiledeğebilir (özellikle daha karmaşık normal ifadeler için).

Erken optimizasyona karşı standart argümanlar geçerlidir, ancak normal re.compileifadelerinizin performans darboğazı olabileceğinden şüpheleniyorsanız, kullanarak netliği / açıklığı gerçekten kaybettiğinizi düşünmüyorum .

Güncelleme:

Python 3.6 (yukarıdaki zamanlamaların Python 2.x kullanılarak yapıldığından şüpheleniyorum) ve 2018 donanımı (MacBook Pro) altında, şimdi aşağıdaki zamanlamaları alıyorum:

% 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.

Ayrıca re.match(x, ...), kelimenin tam anlamıyla [kabaca] eşdeğer olduğunu re.compile(x).match(...), yani derlenmiş gösterimin perde arkası önbelleğe alınmadığını gösteren bir durum (son iki çalışma arasındaki tırnak işareti farklarına dikkat edin) ekledim .


5
Buradaki metodolojinizle ilgili büyük sorunlar, çünkü kurulum argümanı zamanlamaya dahil DEĞİLDİR. Böylece, derleme süresini ikinci örnekten kaldırdınız ve sadece ilk örnekte ortaladınız. Bu, ilk örneğin her seferinde derlendiği anlamına gelmez.
Triptych

1
Evet, bunun iki vakanın adil bir karşılaştırması olmadığını kabul ediyorum.
Kiv

7
Ne demek istediğini anlıyorum, ancak normal ifadenin birçok kez kullanıldığı gerçek bir uygulamada tam olarak ne olur?
dF.

26
@Triptych, @Kiv: Normal ifadeleri kullanımdan ayrı olarak derlemenin amacı derlemeyi en aza indirmektir; zamanlamadan çıkarmak tam olarak dF'nin yapması gereken şeydi, çünkü gerçek dünya kullanımını en doğru şekilde temsil ediyor. Derleme zamanı özellikle timeit.py'nin zamanlamalarını burada yapma şekliyle ilgisizdir; birkaç çalışma yapar ve yalnızca en kısa olanı bildirir, bu noktada derlenmiş normal ifade önbelleğe alınır. Burada gördüğünüz ekstra maliyet normal ifadeyi derleme maliyeti değil, derlenmiş normal ifade önbelleğinde (sözlük) arama maliyeti.
jemfinch

3
@Triptych Kurulumdan çıkarılmalı mı import re? Her şey ölçmek istediğiniz yerle ilgili. Birkaç kez bir python betiği çalıştırırsam, import rezaman vurur olurdu . İkisini karşılaştırırken, zamanlama için iki çizgiyi ayırmak önemlidir. Evet, dediğin gibi zaman vurulacak. Karşılaştırma, ya isabet edilen zamanı bir kez alıp derleyerek daha az isabet süresini tekrarladığınızı ya da her seferinde önbelleğin aramalar arasında temizlendiğini varsayarak isabet aldığınızı gösterir. Zamanlaması eklemek h=re.compile('hello')açıklığa kavuşmanıza yardımcı olur.
Tom Myddeltyn

39

İşte basit bir test örneği:

~$ 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 ile:

~$ 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

Yani, sadece bir kez eşleşseniz bile , bu basit durumla derlemek daha hızlı görünüyor .


2
Bu hangi Python sürümü?
Kyle Strand

2
önemli değil, asıl önemli olan kodu çalıştıracağınız ortamdaki ölçütü denemek
david king

1
Benim için performans 1000 döngü veya daha fazlası için neredeyse aynı. Derlenen sürüm 1-100 döngü için daha hızlıdır. (Her iki piton 2.7 ve 3.4'te).
Zitrax

2
Python 2.7.3 kurulumumda neredeyse hiç fark yok. Bazen derleme daha hızlı, bazen de daha yavaştır. Fark her zaman <% 5'tir, bu yüzden cihazın sadece bir CPU'su olduğundan farkı ölçüm belirsizliği olarak sayıyorum.
Dakkaron

1
Python 3.4.3'te iki ayrı çalışmada görülür: derlenmiş kullanımı derlenenden daha yavaştı.
Zelphir Kaltstahl

17

Bunu kendim denedim. Bir dizeden bir sayının ayrıştırılması ve toplanması için, derlenmiş bir normal ifade nesnesi kullanmak reyöntemleri kullanmaktan yaklaşık iki kat daha hızlıdır .

Diğerlerinin belirttiği gibi, reyöntemler (dahil re.compile), daha önce derlenmiş ifadelerin önbelleğinde normal ifade dizesini arar. Bu nedenle, normal durumda, reyöntemleri kullanmanın ek maliyeti basitçe önbellek aramanın maliyetidir.

Ancak, kodun incelenmesi , önbelleğin 100 ifadeyle sınırlı olduğunu gösterir. Bu şu soruyu akla getiriyor, önbellek taşması ne kadar acı verici? Kod, normal ifade derleyicisine bir iç arabirim içerir re.sre_compile.compile. Eğer ararsak, önbelleği atlarız. Temel bir düzenli ifade için yaklaşık iki büyüklük sırası daha yavaş olduğu ortaya çıkıyor r'\w+\s+([0-9_]+)\s+\w*'.

İşte benim testim:

#!/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

'ReallyCompiled' yöntemleri, önbelleği atlayan dahili arabirimi kullanır. Her döngü yinelemesinde derlenenin bir milyon değil, yalnızca 10.000 kez yinelendiğine dikkat edin.


Derlenmiş regex'lerin derlenmemiş olandan çok daha hızlı çalıştığını kabul ediyorum. 10.000'den fazla cümle çalıştırdım ve regexler derlenmediğinde regexes için yineleme yapmak için bir döngü yaptım ve derlediğim regex desenleri ile dizine göre bir sözlük oluşturduktan sonra, tam bir çalışmanın tahmini 8 saat olduğunda hesaplandı her şeyi 2 dakika boyunca. Yukarıdaki cevapları anlayamıyorum ...
Eli Borodach

12

Dürüst Abe ile match(...)verilen örneklerin farklı olduğunu kabul ediyorum . Bunlar bire bir karşılaştırmalar değildir ve bu nedenle sonuçlar değişkendir. Cevabımı basitleştirmek için, söz konusu işlevler için A, B, C, D kullanıyorum. Ah evet, re.py3 yerine 4 fonksiyonla uğraşıyoruz .

Bu kod parçasını çalıştırma:

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

bu kodu çalıştırmakla aynı:

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

Çünkü kaynağa bakıldığında re.py(A + B) şu anlama gelir:

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

ve (C) aslında:

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

Yani, (C) (B) ile aynı değildir. Aslında, (C), (D) yi çağırdıktan sonra (A) ile de (B) yi çağırır. Başka bir deyişle (C) = (A) + (B),. Bu nedenle, bir döngü içindeki (A + B) karşılaştırması bir döngü içindeki (C) ile aynı sonucu verir.

George regexTest.pybunu bizim için kanıtladı.

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

Herkesin ilgisi 2.323 saniyenin sonucunu nasıl elde edeceğidir. Sadece bir compile(...)kez çağrıldığından emin olmak için, derlenmiş normal ifade nesnesini hafızada saklamamız gerekir. Bir sınıf kullanırsak, nesneyi saklayabilir ve fonksiyonumuz her çağrıldığında yeniden kullanabiliriz.

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

Sınıfı kullanmıyorsak (bugünkü isteğim budur), o zaman yorumum yok. Hala Python'da global değişkeni kullanmayı öğreniyorum ve global değişkenin kötü bir şey olduğunu biliyorum.

Bir nokta daha, (A) + (B)yaklaşım kullanmanın bir üst eli olduğuna inanıyorum . Gözlemlediğim bazı gerçekler (lütfen yanılıyorsam beni düzeltin):

  1. Bir kez çağırdığında, bir regex nesnesi oluşturmak için bir arama ve _cacheardından bir arama yapacaktır sre_compile.compile(). Çağrılar İki kez, iki arama ve bir derleme yapacaktır (regex nesnesi önbelleğe alındığı için).

  2. _cacheGet arasında geçiş yapılırsa, regex nesnesi bellekten serbest bırakılır ve Python'un tekrar derlenmesi gerekir. (Birisi Python'un yeniden derlenmeyeceğini önerir.)

  3. Regex nesnesini (A) kullanarak tutarsak, regex nesnesi _cache içine girer ve bir şekilde temizlenir. Ancak kodumuz üzerinde bir referans tutar ve regex nesnesi bellekten serbest bırakılmaz. Bunlar, Python'un tekrar derlemesine gerek yok.

  4. George'un compiled testindeki 2 saniyelik farklar, derlenmiş vsLoop vs derlemede anahtarın oluşturulması ve _cache'in aranması için gereken zamandır. Regex'in derlenme zamanı anlamına gelmez.

  5. George'un gerçekten derleme testi, derlemeyi her seferinde gerçekten yeniden yaparsa ne olacağını gösterir: 100 kat daha yavaş olacaktır (döngüyü 1.000.000'dan 10.000'e düşürdü).

(A + B) 'nin (C)' den daha iyi olduğu tek durumlar şunlardır:

  1. Regex nesnesinin bir başvurusunu bir sınıf içinde önbelleğe alabilirsek.
  2. (B) 'yi tekrar tekrar (bir döngü içinde veya birden çok kez) çağırmamız gerekiyorsa, nesneyi döngü dışında regex nesnesi için önbelleğe almalıyız.

(C) 'nin yeterince iyi olduğu durumda:

  1. Bir referansı önbelleğe alamıyoruz.
  2. Sadece arada bir kullanıyoruz.
  3. Genel olarak, çok fazla regex'imiz yok (derlenmiş olanın asla kızarmadığını varsayın)

Sadece bir özet, işte ABC:

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

Okuduğunuz için teşekkürler.


8

Çoğunlukla, re.compile kullanıp kullanmadığınız arasında çok az fark vardır . Dahili olarak, tüm işlevler bir derleme adımı açısından uygulanır:

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)

Ek olarak, re.compile () işlevi, fazladan dolaylı aktarma ve önbelleğe alma mantığını atlar:

_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

Yeniden derlemenin kullanılmasının küçük hız avantajına ek olarak , insanlar potansiyel olarak karmaşık desen özelliklerini adlandırmaktan ve bunları uygulandığı iş mantığından ayırmaktan gelen okunabilirliği de severler:

#### 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()

Not: Bir diğer katılımcı yanlış derlenmiş pyc dosyalarının derlenmiş kalıpları doğrudan sakladığına inanıyordu ; ancak, gerçekte PYC her yüklendiğinde yeniden inşa edilirler:

>>> 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

Yukarıdaki sökme aşağıdakileri tmp.pyiçeren PYC dosyasından gelir :

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

1
olduğu "içinde def search(pattern, string, flags=0):"bir yazım hatası?
phuclv

1
Eğer Not patternzaten derlenmiş kalıptır, önbelleğe alma havai anlamlı hale gelir: bir karma SRE_Patternpahalıdır ve arama bir ile her zaman başarısız böylece desen, önbelleğe yazılır asla KeyError.
Eric Duminil

5

Genel olarak, re.Idesenleri derlerken bayrakları satır içi kullanmaktan ziyade , bayrak kullanmanın (en azından nasıl hatırlandığını daha kolay) buluyorum .

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

vs

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

Bayrakları re.findallyine de üçüncü argüman olarak kullanabilirsiniz .
aderchox

5

Verilen örnekleri kullanarak:

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

Maç yukarıdaki örnekte yöntem aşağıda kullanılanla aynı değildir:

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

re.compile () bir döner normal ifade nesnesi aracı, hbir normal ifade amacıdır.

Regex nesnesinin isteğe bağlı pos ve endpos parametreleriyle kendi eşleşme yöntemi vardır :

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

pos

İsteğe bağlı ikinci parametre pos , dizede aramanın başlayacağı bir dizin verir; varsayılan olarak 0'dır. Bu, dizeyi dilimlemeye tamamen eşdeğer değildir; '^'Desen karakter ama arama başlamaktır endeksine, dizenin gerçek başında ve sadece bir satır sonra pozisyonlarda eşleşir.

endPos

İsteğe bağlı parametre noktaları dizenin ne kadar aranacağını sınırlar; dizedir sanki olacak endPos karakter uzunluğunda, yani sadece karakterler pos için endpos - 1bir maç için aranır. Eğer endPos azdır pos , eşleşme bulunacaktır; aksi takdirde, rx derlenmiş bir normal ifade nesnesiyse, rx.search(string, 0, 50)eşdeğeridir rx.search(string[:50], 0).

Normal ifade nesnesinin arama , findall ve finditer yöntemleri de bu parametreleri destekler.

re.match(pattern, string, flags=0)gördüğünüz gibi desteklemez
, arama , findall ve bulucu meslektaşları da desteklemez.

Bir eşleme nesnesinin bu parametreleri tamamlayan nitelikleri vardır:

match.pos

Normal ifade nesnesinin search () veya match () yöntemine iletilen pos değeri. Bu, RE motorunun bir eşleşme aramaya başladığı dizenin dizinidir.

match.endpos

Normal ifade nesnesinin search () veya match () yöntemine iletilen endpos değerleri. Bu, RE motorunun ötesine geçmeyeceği dizenin dizinidir.


Bir düzenli ifade nesnesi iki, muhtemelen kullanışlı, benzersiz özelliklere sahiptir:

regex.groups

Modeldeki yakalama gruplarının sayısı.

regex.groupindex

(? P) ile tanımlanan sembolik grup adlarını grup numaralarıyla eşleyen sözlük. Kalıpta hiçbir sembolik grup kullanılmamışsa sözlük boştur.


Ve son olarak, bir eşleşme nesnesi şu özelliğe sahiptir:

match.re

Match () veya search () yöntemi bu eşleşme örneğini üreten normal ifade nesnesi.


4

Performans farkı bir yana, re.compile kullanarak ve eşleme yapmak için derlenmiş normal ifade nesnesini kullanarak (normal ifade ile ilgili işlemler ne olursa olsun) semantikleri Python çalışma zamanı için daha net hale getirir.

Bazı basit kod hata ayıklama bazı acı deneyim oldu:

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

ve sonra karşılaştırmayı kullanırdım

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

burada patternPhrasesnormal ifade dizesi içeren bir değişken olması gerekiyordu, x[columnIndex]değişken içeren bir dize.

patternPhrasesBeklenen dize eşleşmedi sorun vardı !

Ancak re.compile formunu kullanırsam:

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

daha sonra

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

Python, gerçekte ifade ettiğimde compare, konumsal argüman eşlemesinde olduğu gibi, "dizginin eşleşme niteliğine sahip olmadığından" x[columnIndex]normal ifade olarak kullanıldığından şikayet ederdi !

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

Benim durumumda, re.compile kullanmak düzenli ifadenin amacına daha açıktır, değeri çıplak gözlere gizlendiğinde, Python çalışma zamanı kontrolünden daha fazla yardım alabilirim.

Bu yüzden dersimin ahlaki, normal ifade sadece değişmez bir dize olmadığında, Python'un varsayımımı iddia etmeme yardımcı olması için re.compile kullanmam gerektiğidir.


4

Re.VERBOSE kullanarak regex kalıplarıma yorum ekleme şeklinde re.compile () kullanmanın bir ek özelliği daha var

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

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

Bu, kodunuzu çalıştırma hızını etkilemese de, yorum yapma alışkanlığımın bir parçası olduğu için bu şekilde yapmayı seviyorum. İnandýrmaya ben deđiţiklikleri yapmak istediđimde 2 ay boyunca kodumun arkasýndaki mantığı hatýrlamaya çalýţtýđým zamaný sevmiyorum.


1
Cevabınızı düzenledim. Bahsetmeye re.VERBOSEdeğer olduğunu düşünüyorum ve diğer cevapların dışında bıraktığı bir şey ekliyor. Ancak, cevabınızı "Henüz yorum yapamadığım için buraya gönderiyorum" şeklinde yönlendirerek sorunun silineceğini unutmayın. Lütfen cevap kutusunu başka cevaplar için kullanmayın. Siz herhangi bir yere yorum yapabilmekten sadece bir veya iki iyi yanıt uzaktasınız (50 rep), bu yüzden lütfen sabırlı olun. Bilmemeniz gerektiğini düşündüğünüzde cevap kutularına yorum koymak sizi oraya daha hızlı götürmez. Size oy ve silinen cevapları alacak.
skrrgwasme

4

Python belgelerine göre :

Sekans

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

eşittir

result = re.match(pattern, string)

ancak re.compile(), ifade tek bir programda birkaç kez kullanılacaksa, elde edilen normal ifade nesnesinin yeniden kullanılmak üzere kullanılması ve kaydedilmesi daha etkilidir.

Sonuç olarak, birçok farklı metin için aynı kalıbı eşleştirecekseniz, önceden derlemeniz daha iyi olur.


3

İlginçtir ki, derleme benim için daha verimli oluyor (Win XP'de Python 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

Yukarıdaki kodu bir kez olduğu gibi çalıştırmak ve iki ifsatır bir kez başka bir şekilde yorumlandığında, derlenmiş normal ifade iki kat daha hızlıdır


2
DF'nin performans karşılaştırmasıyla aynı sorun. Derleme ifadesinin performans maliyetini dahil etmedikçe bu gerçekten adil değildir.
Carl Meyer

6
Carl, katılmıyorum. Derleme yalnızca bir kez yürütülürken, eşleşen döngü milyon kez yürütülür
Eli Bendersky

@eliben: Carl Meyer ile aynı fikirdeyim. Derleme her iki durumda da gerçekleşir. Triptych, önbelleğe alma işleminden söz eder, bu nedenle en uygun durumda (önbellekte kalır) her iki yaklaşım da O (n + 1) 'dir, ancak re.compile açıkça kullanmadığınızda +1 kısmı bir tür gizlidir.
paprika

1
Kendi karşılaştırma kodunuzu yazmayın. Standart dağıtımda bulunan timeit.py dosyasını kullanmayı öğrenin.
jemfinch

For döngüsünde desen dizesini ne kadar sürede yeniden oluşturuyorsunuz. Bu yük önemsiz olamaz.
IceArdor

3

Bu tartışmayı burada tartışmadan önce yaptım. Ancak, çalıştırdıktan sonra en azından sonuçlarımı göndereceğini düşündüm.

Örneği Jeff Friedl'in "Düzenli İfadelerde Ustalaşmak" da çaldım ve piç ettim. Bu, OSX 10.6 (2Ghz intel core 2 duo, 4GB ram) çalıştıran bir macbook'ta. Python sürümü 2.6.1'dir.

Run 1 - re.compile kullanarak

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

Run 2 - re.compile kullanılmıyor

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

Bu cevap geç geliyor olabilir ama ilginç bir bulgu. Normal ifadeyi birden çok kez kullanmayı planlıyorsanız, derleme kullanmak size gerçekten zaman kazandırabilir (bu, dokümanlarda da belirtilir). Aşağıda, eşleme yöntemi doğrudan çağrıldığında derlenmiş bir normal ifadeyi kullanmanın en hızlı olduğunu görebilirsiniz. derlenmiş bir regex'i re.match'a geçirmek daha yavaş hale getirir ve pıtırtı dizesiyle re.match'u ortada bir yerde geçirir.

>>> 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

Performansın yanı sıra.

Kullanarak 1. modül (re) , 2. regex nesnesi 3. eşlemecompile kavramlarını ayırt etmeme yardımcı olur regex öğrenmeye başladığımda



#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'

Bir tamamlayıcı olarak, rereferans için kapsamlı bir modül hile sayfası yaptım .

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

Yukarıdaki tüm cevaplara gerçekten saygı duyuyorum. Bence Evet! Her zaman regex'i tekrar tekrar derlemek yerine re.compile kullanmaya değer.

Re.compile komutunu kullanmak , kodunuzu daha dinamik hale getirir, çünkü yeniden derlemek ve yeniden kazanmak yerine zaten derlenmiş regex'i çağırabilirsiniz. Bu şey durumlarda size fayda sağlar:

  1. İşlemci Çabaları
  2. Zaman Karmaşıklığı.
  3. Regex Universal yapar. (Findall, arama, maçta kullanılabilir)
  4. Ve programınızın havalı görünmesini sağlar.

Misal :

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

Findall'da kullanma

 find_alpha_numeric_string.findall(example_string)

Aramada kullanma

  find_alpha_numeric_string.search(example_string)

Benzer şekilde aşağıdakiler için de kullanabilirsiniz: Eşleştirme ve Değiştirme


1

Bu iyi bir soru. Çoğu zaman insanların sebepsiz yeniden derleme kullandığını görürsünüz. Okunabilirliği azaltır. Ancak, ifadeyi önceden derlerken birçok kez çağrıldığından emin olun. Bir döngüde tekrar tekrar kullandığınızda olduğu gibi.

Programlama ile ilgili her şey gibi (aslında hayattaki her şey). Sağduyu uygulayın.


Kısaca yaptığım hareketten anlayabildiğim kadarıyla, Özetle Python, re.compile () olmadan kullanımdan bahsetmiyor, bu da beni meraklandırıyor.
Mat

Normal ifade nesnesi, bağlama bir nesne daha ekler. Dediğim gibi, re.compile () 'in yerini aldığı birçok durum vardır. OP tarafından verilen örnek bunlardan biri değildir.
PEZ

1

(aylar sonra) re.match'e kendi önbelleğinizi veya bu konudaki herhangi bir şeyi eklemek kolaydır -

""" 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 ...

Bir wibni, şu durumlarda iyi olmaz: cachehint (size =), cacheinfo () -> size, hit, nclear ...


1

Anında derleme ile 1000 kez derlenmiş normal ifadeyi çalıştırma konusunda çok fazla deneyimim oldu ve algılanabilir bir fark görmedim

Kabul edilen cevaptaki oylar @Triptych'in söylediklerinin tüm durumlar için doğru olduğu varsayımına yol açar. Bu mutlaka doğru değildir. Büyük bir fark, bir regex dizesini veya derlenmiş bir regex nesnesini bir işleve parametre olarak kabul edip etmeyeceğinize karar vermeniz gerektiğidir:

>>> 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

Tekrar kullanmanız gerektiğinde normal ifadelerinizi derlemek her zaman daha iyidir.

Yukarıdaki timeit'teki örneğe, bir eşleşme için gerektiğinde "anında" veya derleme sırasında bir derlenmiş normal ifade nesnesinin oluşturulmasını simüle ettiğini unutmayın.


1

Alternatif bir cevap olarak, daha önce bahsedilmediğini gördüğüm gibi, devam edip Python 3 belgelerini alıntılayacağım :

Bu modül düzeyinde işlevleri mi kullanmalısınız yoksa kalıbı alıp yöntemlerini kendiniz mi çağırmalısınız? Bir döngü içindeki normal ifadeye erişiyorsanız, bunu önceden derlemek birkaç işlev çağrısını kaydeder. Döngülerin dışında, dahili önbellek sayesinde fazla bir fark yok.


1

Aşağıda, kullanımın istendiğire.compile gibi 50 kattan daha hızlı olduğu bir örnek verilmiştir .

Mesele, yukarıdaki yorumda yaptığımla aynıdır, yani re.compilekullanımınız derleme önbelleğinden çok fazla faydalanmayacak şekilde kullanmak önemli bir avantaj olabilir. Bu, en azından belirli bir durumda (pratikte karşılaştığım), yani aşağıdakilerin tümü doğru olduğunda gerçekleşir:

  • Sen regex desen çok şey var (birden fazla re._MAXCACHEolan, varsayılan şu anda 512 olan) ve
  • bu normal ifadeleri birçok kez kullanırsınız ve
  • aynı desenin ardışık kullanımları, re._MAXCACHEaralarındaki diğer normal ifadelerden daha fazla ile ayrılır , böylece her biri ardışık kullanımlar arasındaki önbellekten temizlenir.
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}')

Dizüstü bilgisayarımda elde ettiğim örnek çıktı (Python 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

timeitFark çok keskin olduğu için uğraşmadım , ancak her seferinde niteliksel olarak benzer sayılar alıyorum. re.compileAynı regex'i birden çok kez kullanmadan ve bir sonrakine geçmeden bile , o kadar da kötü olmadığını (olduğu kadar sadece 2 kat daha yavaş re.compile), ancak diğer sırada (birçok regexte döngü) önemli ölçüde daha kötü olduğunu unutmayın. , beklenildiği gibi. Ayrıca, önbellek boyutunu artırarak çok çalışır: basitçe ayarlayarak re._MAXCACHE = len(patterns)içinde setup()~ 23 saniye ~ 0.7 saniye için geri adım düşer (alt çizgilerle isimleri geleneksel olarak “özel” olarak tabii ki üretimde böyle şeyler yapıyor önermiyoruz ait) Yukarıdaki da hangi anlayışımızla eşleşir.


Not: Kodumda yalnızca 3 normal ifade deseni kullanırsam , bunların her biri (belirli bir sipariş olmadan) yüzlerce kez kullanılırsa, normal ifade önbelleği önceden derlenmiş normal ifadeyi otomatik olarak tutacaktır, doğru mu?
Basj

@Basj Ben sadece denemek ve görmek olabilir :) Ama cevap, eminim, evet: bu durumda tek ek maliyet AFAICT sadece önbellek desen bakmak olduğunu . Önbelleğin genel (modül düzeyinde) olduğunu unutmayın, bu nedenle prensipte aralarınızda regex aramaları yapan bazı bağımlılık kütüphaneniz olabilir, bu nedenle programınızın yalnızca 3 (veya herhangi bir sayıda) regex kullandığından tamamen emin olmak zor desen, ama aksi halde olması çok garip olurdu :)
ShreevatsaR

0

İkinci sürüm kullanılırken Normal İfadeler kullanılmadan önce derlenir. Eğer birçok kez uygulayacaksanız, önce derlemek kesinlikle daha iyidir. Eğer bir kez için maç her zaman derleme iyi.


0

Okunabilirlik / bilişsel yük tercihi

Bana göre, ana kazanç sadece unutmayın, ve okunması gerektiğidir biri - komplike regex API sözdizimi formu <compiled_pattern>.method(xxx)yerine daha formu vere.func(<pattern>, xxx) formda.

re.compile(<pattern>)Ekstra Demirbaş biraz, doğrudur.

Ancak regex söz konusu olduğunda, bu ekstra derleme adımının bilişsel yükün büyük bir nedeni olması muhtemel değildir. Aslında, karmaşık kalıplarda, beyanı daha sonra çağırdığınız herhangi bir normal ifade yönteminden ayırmaktan bile netlik kazanabilirsiniz.

İlk önce Regex101 gibi bir web sitesinde veya hatta ayrı bir minimal test komut dosyasında karmaşık desenleri ayarlamaya, sonra bunları koduma getirmeye çalışıyorum, bu yüzden bildirimi kullanımından ayırmak iş akışımla da uyuyor.


-1

Ön derlemenin hem kavramsal hem de 'edebi olarak' ('okuryazar programlamada olduğu gibi) avantajlı olduğunu motive etmek istiyorum. bu kod snippet'ine bir göz atın:

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()

başvurunuzda şunları yazacaksınız:

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

bu işlevsellik açısından olabildiğince basit. çünkü bu örnek çok kısa, _text_has_foobar_re_searchhepsini tek satırda almanın yolunu kaptım . bu kodun dezavantajı, TYPOkütüphane nesnesinin ömrü ne olursa olsun, hafızayı az kullanmasıdır; avantajı, bir foobar araması yaparken, iki işlev çağrısı ve iki sınıf sözlük aramasıyla kaçmanızdır. kaç regex tarafından önbelleğe alınırre ve bu önbelleğin yükü burada önemsizdir.

bunu aşağıdaki daha genel stille karşılaştırın:

import re

class Typo:

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

Uygulamada:

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

Stilimin python için oldukça sıra dışı olduğunu, hatta belki de tartışmalı olduğunu kolayca itiraf ediyorum. bununla birlikte, tek bir eşleme yapmak için bir nesneyi başlatmalı, üç örnek sözlük araması yapmalı ve üç işlev çağrısı yapmalıyız; buna ek olarak,re 100'den fazla regex kullanırken önbellek sorunları . ayrıca, düzenli ifade, çoğu zaman böyle iyi bir fikir olmayan yöntem gövdesi içinde gizlenir.

önlemlerin her alt kümesinin --- hedeflenmiş, diğer adlara ayrılmış ithalat beyanlarının; uygulanabilir olduğunda takma yöntemler; fonksiyon çağrılarının ve nesne sözlüğü aramalarının azaltılması --- hesaplama ve kavramsal karmaşıklığın azaltılmasına yardımcı olabilir.


2
O NE LAN. Sadece eski, cevaplanmış bir soruyu sildin. Kodunuz pek çok düzeyde idiomatik değildir ve yanlıştır - (ab) sınıfları modülün yeterli olduğu ad alanları olarak kullanmak, sınıf adlarını büyük harfle yazmak vb. ... Daha iyi uygulamalar için pastebin.com/iTAXAWen adresine bakın . Kullandığınız normal ifadeden de bahsetmiyoruz bile. Genel olarak, -1

2
suçlu. Bu eski bir soru, ama yavaşlamış bir konuşmada # 100 olmak umurumda değil. soru kapatılmadı. benim kod bazı tatlar için düşman olabilir uyardı. Bence bunu python'da nelerin yapılabileceğinin sadece bir göstergesi olarak görebilseydiniz: her şeyi alırsak, inandığımız her şeyi, isteğe bağlı olarak ve sonra herhangi bir şekilde bir araya getirip, neye benzeyebileceğimiz gibi görünüyor almak? eminim bu çözümün avantajlarını ve dezavantajlarını fark edebiliyor ve daha açık bir şekilde şikayet edebiliyorsunuz. Aksi takdirde yanlışlık iddianızı PEP008'den biraz daha fazla kullanır
akış

2
Hayır, bu PEP8 ile ilgili değil. Bu sadece konvansiyonları isimlendiriyor ve bunları izlemediğim için asla aşağı inmem. Gösterdiğin kod sadece kötü yazılmış çünkü seni aşağıladım. Hiçbir nedenden dolayı konvansiyonlara ve deyimlere meydan okuyor ve izin optimizasyonunun enkarnasyonudur: Bunun bir darboğaz olması için canlı gün ışığını diğer tüm kodlardan optimize etmeniz gerekir ve hatta üçüncü üçüncü yeniden yazma daha kısa, daha fazla deyimsel ve mantıksal olarak hızlı (aynı sayıda özellik erişimi).

"kötü yazılmış" - neden tam olarak? "konvansiyonlara ve deyimlere meydan okur" - sizi uyardım. "sebepsiz" - evet bir nedenim var: karmaşıklığın hiçbir amaca hizmet etmediği yerlerde basitleştirmek; "erken optimizasyon enkarnasyonu" - Ben çok okunabilirlik ve verimlilik dengesi seçen bir programlama tarzı için; OP, verimlilikle ilgili bir soru olarak anladığım "yeniden derleme kullanımında fayda" nın ortaya çıkmasını istedi. "(ab) sınıfları ad alanı olarak kullanmak" - bu, sizin küfürlü sözlerinizdir. sınıf oradadır, böylece bir "benlik" referans noktanız vardır. Bu amaçla modülleri kullanmayı denedim, sınıflar daha iyi çalışıyor.
akış

"sınıf adlarını büyük harfe çevirme", "Hayır, bu PEP8 ile ilgili değil" - görünüşe göre öylesine aşırı derecede öfkelisiniz ki, ilk önce neyin bağlanacağını bile söyleyemezsiniz. "WTF", " yanlış " --- ne kadar duygusal olduğunuzu görüyor musunuz? daha fazla tarafsızlık ve daha az köpük lütfen.
akış

-5

Anladığım kadarıyla, bu iki örnek etkili bir şekilde eşdeğerdir. Tek fark, ilkinde, derlenmiş normal ifadeyi başka bir yerde tekrar derlenmesine neden olmadan yeniden kullanabilmenizdir.

İşte size bir referans: http://diveintopython3.ep.io/refactoring.html

Derlenmiş desen nesnesinin arama işlevini 'M' dizesiyle çağırmak, hem normal ifade hem de 'M' dizesiyle re.search çağrılmasıyla aynı şeyi gerçekleştirir. Sadece çok, çok daha hızlı. (Aslında, re.search işlevi normal ifadeyi derler ve elde edilen desen nesnesinin arama yöntemini sizin için çağırır.)


1
seni aldatmadım, ama teknik olarak bu yanlış: Python yine de derlemek olmaz
Triptych
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.