Apakah layak menggunakan kompilasi ulang Python?

461

Apakah ada manfaat menggunakan kompilasi untuk ekspresi reguler di Python?

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

vs.

re.match('hello', 'hello world')
Tikar
sumber
8
Lain dari kenyataan bahwa dalam 2.6 re.subtidak akan mengambil argumen bendera ...
new123456
58
Saya baru saja menemukan sebuah kasus di mana menggunakan re.compilememberikan peningkatan 10-50x. Moralnya adalah jika Anda memiliki banyak regex (lebih dari MAXCACHE = 100) dan Anda menggunakannya masing-masing berkali-kali (dan dipisahkan oleh lebih dari MAXCACHE regex di antaranya, sehingga masing-masing dihapus dari cache: jadi gunakan yang sama banyak kali dan kemudian pindah ke yang berikutnya tidak masuk hitungan), maka pasti akan membantu untuk mengkompilasi mereka. Kalau tidak, itu tidak membuat perbedaan.
ShreevatsaR
8
Satu hal kecil yang perlu diperhatikan adalah bahwa untuk string yang tidak perlu regex, intes string substring JAUH lebih cepat:>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 Menarik! Bisakah Anda memposting jawaban dengan contoh yang menunjukkan peningkatan 10x-50x? Sebagian besar jawaban yang diberikan di sini sebenarnya menunjukkan peningkatan 3x dalam beberapa kasus yang tepat, dan dalam kasus lain hampir tidak ada peningkatan.
Basj
1
@ Basj Selesai, memposting jawaban . Saya tidak repot-repot menggali untuk apa saya menggunakan Python pada bulan Desember 2013, tetapi hal langsung pertama yang saya coba menunjukkan perilaku yang sama.
ShreevatsaR

Jawaban:

436

Saya sudah memiliki banyak pengalaman menjalankan regex yang dikompilasi 1000 kali dibandingkan dengan kompilasi saat itu juga, dan belum melihat adanya perbedaan yang jelas. Jelas, ini anekdotal, dan tentu saja bukan argumen yang bagus untuk tidak mengkompilasi, tetapi saya menemukan perbedaannya dapat diabaikan.

EDIT: Setelah melihat sekilas kode pustaka Python 2.5 yang sebenarnya, saya melihat bahwa Python secara internal mengkompilasi DAN CACHES regexes setiap kali Anda menggunakannya (termasuk panggilan ke re.match()), jadi Anda benar-benar hanya mengubah KAPAN regex dikompilasi, dan tidak seharusnya t akan menghemat banyak waktu sama sekali - hanya waktu yang diperlukan untuk memeriksa cache (pencarian kunci pada dicttipe internal ).

Dari module re.py (komentar adalah milik saya):

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

Saya masih sering melakukan pra-kompilasi ekspresi reguler, tetapi hanya untuk mengikatnya dengan nama yang bagus dan dapat digunakan kembali, bukan untuk keuntungan kinerja yang diharapkan.

Triptych
sumber
12
Kesimpulan Anda tidak konsisten dengan jawaban Anda. Jika regex dikompilasi dan disimpan secara otomatis, dalam banyak kasus tidak perlu melakukannya dengan tangan.
jfs
84
JF Sebastian, ini berfungsi sebagai sinyal kepada programmer bahwa regexp yang bersangkutan akan banyak digunakan dan tidak dimaksudkan untuk dibuang.
kaleissin
40
Lebih dari itu, saya akan mengatakan bahwa jika Anda tidak ingin menderita hit kompilasi & cache di beberapa bagian penting kinerja aplikasi Anda, sebaiknya Anda mengkompilasinya sebelum menyerahkan bagian yang tidak penting dari aplikasi Anda .
Eddie Parker
20
Saya melihat keuntungan utama untuk menggunakan regex yang dikompilasi jika Anda menggunakan ulang regex yang sama beberapa kali, sehingga mengurangi kemungkinan kesalahan ketik. Jika Anda hanya memanggilnya sekali saja maka kompilasi lebih mudah dibaca.
monkut
18
Jadi, perbedaan utamanya adalah ketika Anda menggunakan banyak regex yang berbeda (lebih dari _MAXCACHE), beberapa dari mereka hanya sekali dan yang lainnya berkali-kali ... maka penting untuk menjaga ekspresi kompilasi Anda untuk mereka yang digunakan lebih banyak sehingga mereka Tidak keluar dari cache saat sudah penuh.
fortran
133

Bagi saya, manfaat terbesar re.compileadalah dapat memisahkan definisi regex dari penggunaannya.

Bahkan ungkapan sederhana seperti 0|[1-9][0-9]*(bilangan bulat di basis 10 tanpa angka nol di depan) bisa cukup rumit sehingga Anda tidak perlu mengetik ulang, memeriksa apakah Anda membuat kesalahan ketik, dan nanti harus memeriksa ulang apakah ada kesalahan ketik ketika Anda mulai debugging . Plus, lebih baik menggunakan nama variabel seperti num atau num_b10 daripada 0|[1-9][0-9]*.

Tentu saja mungkin untuk menyimpan string dan mengirimkannya untuk dikirim kembali; namun, itu kurang bisa dibaca:

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

Versus kompilasi:

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

Meskipun cukup dekat, baris terakhir dari kedua terasa lebih alami dan lebih sederhana bila digunakan berulang kali.

Acumenus
sumber
5
Saya setuju dengan jawaban ini; seringkali menggunakan re.compile menghasilkan lebih banyak, tidak sedikit kode yang dapat dibaca.
Carl Meyer
1
Namun, terkadang yang sebaliknya benar - misalnya jika Anda mendefinisikan regex di satu tempat dan menggunakan grup yang cocok di tempat lain yang jauh.
Ken Williams
1
@ KenWilliams Tidak harus, regex yang bernama baik untuk tujuan tertentu harus jelas bahkan ketika digunakan jauh dari definisi aslinya. Misalnya us_phone_numberatau social_security_numberlain
Brian M. Sheldon
2
@ BrianM. Sheldon memberi nama regex dengan baik tidak benar-benar membantu Anda mengetahui apa yang diwakili oleh berbagai kelompok penangkap.
Ken Williams
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

jadi, jika Anda akan menggunakan regex yang sama banyak, mungkin layak dilakukan re.compile(terutama untuk regex yang lebih kompleks).

Argumen standar terhadap optimasi prematur berlaku, tetapi saya tidak berpikir Anda benar-benar kehilangan banyak kejelasan / keterusterangan dengan menggunakan re.compilejika Anda mencurigai bahwa regexps Anda mungkin menjadi hambatan kinerja.

Memperbarui:

Di bawah Python 3.6 (saya menduga timing di atas dilakukan menggunakan Python 2.x) dan hardware 2018 (MacBook Pro), saya sekarang mendapatkan timing berikut:

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

Saya juga menambahkan case (perhatikan perbedaan tanda kutip antara dua run terakhir) yang menunjukkan itu re.match(x, ...) secara harfiah [kira-kira] setara dengan re.compile(x).match(...), yaitu tidak ada cache di belakang layar dari representasi yang dikompilasi yang tampaknya terjadi.

dF.
sumber
5
Masalah utama dengan metodologi Anda di sini, karena argumen pengaturan TIDAK termasuk dalam pengaturan waktunya. Dengan demikian, Anda telah menghapus waktu kompilasi dari contoh kedua, dan rata-rata saja dalam contoh pertama. Ini tidak berarti contoh pertama dikompilasi setiap kali.
Triptych
1
Ya, saya setuju bahwa ini bukan perbandingan yang adil dari kedua kasus.
Kiv
7
Saya mengerti maksud Anda, tetapi bukankah itu tepatnya yang akan terjadi dalam aplikasi aktual di mana regexp digunakan berkali-kali?
dF.
26
@Triptych, @Kiv: Inti kompilasi regexps yang terpisah dari penggunaan adalah untuk meminimalkan kompilasi; menghapusnya dari timing adalah persis apa yang seharusnya dilakukan dF, karena itu mewakili penggunaan dunia nyata yang paling akurat. Waktu kompilasi sangat tidak relevan dengan cara timeit.py mengerjakan waktunya di sini; itu menjalankan beberapa dan hanya melaporkan yang terpendek, di mana regexp dikompilasi di-cache. Biaya tambahan yang Anda lihat di sini bukan biaya menyusun regexp, tetapi biaya mencarinya di cache regexp yang dikompilasi (kamus).
jemfinch
3
@ Triptych Haruskah import repindah dari pengaturan? Ini semua tentang di mana Anda ingin mengukur. Jika saya menjalankan skrip python berkali-kali, itu akan memiliki import rewaktu. Saat membandingkan keduanya, penting untuk memisahkan kedua garis untuk penentuan waktu. Ya seperti yang Anda katakan itu adalah ketika Anda akan memiliki waktu hit. Perbandingan menunjukkan bahwa Anda mengambil hit waktu satu kali dan mengulangi hit time yang lebih rendah dengan mengkompilasi atau Anda mengambil hit setiap kali dengan asumsi cache dibersihkan antara panggilan, yang seperti telah ditunjukkan bisa terjadi. Menambahkan waktu h=re.compile('hello')akan membantu memperjelas.
Tom Myddeltyn
39

Berikut ini adalah contoh uji sederhana:

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

dengan kompilasi ulang:

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

Jadi, sepertinya kompilasi lebih cepat dengan case sederhana ini, bahkan jika Anda hanya cocok sekali .

raja david
sumber
2
Versi Python manakah ini?
Kyle Strand
2
tidak masalah, intinya adalah mencoba tolok ukur di lingkungan tempat Anda menjalankan kode
david king
1
Bagi saya kinerjanya hampir persis sama untuk 1000 loop atau lebih. Versi yang dikompilasi lebih cepat untuk 1-100 loop. (Pada kedua piton 2.7 dan 3.4).
Zitrax
2
Pada pengaturan Python 2.7.3 saya hampir tidak ada perbedaan. Terkadang kompilasi lebih cepat, terkadang ist lebih lambat. Perbedaannya selalu <5%, jadi saya menghitung perbedaan sebagai mengukur ketidakpastian, karena perangkat hanya memiliki satu CPU.
Dakkaron
1
Dalam Python 3.4.3 terlihat dalam dua menjalankan terpisah: menggunakan dikompilasi bahkan lebih lambat daripada tidak dikompilasi.
Zelphir Kaltstahl
17

Saya baru saja mencobanya sendiri. Untuk kasus sederhana dari penguraian angka dari string dan menjumlahkannya, menggunakan objek ekspresi reguler terkompilasi adalah sekitar dua kali lebih cepat daripada menggunakanre metode.

Seperti yang orang lain tunjukkan, remetode (termasuk re.compile) mencari string ekspresi reguler dalam cache dari ekspresi yang dikompilasi sebelumnya. Oleh karena itu, dalam kasus normal, biaya tambahan menggunakanre metode hanyalah biaya pencarian cache.

Namun, pemeriksaan kodenya , menunjukkan cache dibatasi hingga 100 ekspresi. Ini menimbulkan pertanyaan, seberapa menyakitkan meluapnya cache? Kode berisi antarmuka internal ke kompiler ekspresi reguler re.sre_compile.compile,. Jika kita menyebutnya, kita memotong cache. Ternyata sekitar dua urutan besarnya lebih lambat untuk ekspresi reguler dasar, seperti r'\w+\s+([0-9_]+)\s+\w*'.

Inilah tes saya:

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

Metode 'reallyCompiled' menggunakan antarmuka internal, yang mem-bypass cache. Perhatikan satu yang dikompilasi pada setiap perulangan hanya iterasi 10.000 kali, bukan satu juta.

George
sumber
Saya setuju dengan Anda bahwa regex yang dikompilasi berjalan jauh lebih cepat daripada yang tidak dikompilasi. Saya menjalankan lebih dari 10.000 kalimat dan membuat satu lingkaran di dalamnya untuk iterasi untuk regex ketika regex tidak dikompilasi dan dihitung setiap kali prediksi run penuh adalah 8 jam, setelah membuat kamus menurut indeks dengan pola regex terkompilasi yang saya jalankan semuanya selama 2 menit. Saya tidak dapat memahami jawaban di atas ...
Eli Borodach
12

Saya setuju dengan Abe Jujur bahwa match(...)dalam contoh yang diberikan berbeda. Mereka bukan perbandingan satu-ke-satu dan dengan demikian, hasilnya bervariasi. Untuk menyederhanakan balasan saya, saya menggunakan A, B, C, D untuk fungsi-fungsi yang dimaksud. Oh ya, kita berhadapan dengan 4 fungsi dire.py alih-alih 3.

Menjalankan kode ini:

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

sama dengan menjalankan kode ini:

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

Karena, ketika melihat ke sumbernya re.py, (A + B) berarti:

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

dan (C) sebenarnya:

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

Jadi, (C) tidak sama dengan (B). Bahkan, (C) panggilan (B) setelah memanggil (D) yang juga disebut dengan (A). Dengan kata lain (C) = (A) + (B),. Oleh karena itu, membandingkan (A + B) di dalam loop memiliki hasil yang sama dengan (C) di dalam loop.

George regexTest.pymembuktikan ini untuk kita.

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

Minat semua orang adalah, bagaimana mendapatkan hasil 2,323 detik. Untuk memastikan compile(...)hanya dipanggil satu kali, kita perlu menyimpan objek regex yang dikompilasi dalam memori. Jika kita menggunakan kelas, kita bisa menyimpan objek dan menggunakan kembali ketika setiap kali fungsi kita dipanggil.

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

Jika kita tidak menggunakan kelas (yang merupakan permintaan saya hari ini), maka saya tidak punya komentar. Saya masih belajar menggunakan variabel global dalam Python, dan saya tahu variabel global adalah hal yang buruk.

Satu hal lagi, saya percaya bahwa menggunakan (A) + (B)pendekatan memiliki keunggulan. Berikut adalah beberapa fakta yang saya amati (perbaiki saya jika saya salah):

  1. Panggilan A sekali, itu akan melakukan satu pencarian di _cachediikuti oleh satu sre_compile.compile()untuk membuat objek regex. Panggilan A dua kali, ia akan melakukan dua pencarian dan satu kompilasi (karena objek regex di-cache).

  2. Jika _cachemendapatkan memerah di antara, maka objek regex dilepaskan dari memori dan Python perlu dikompilasi lagi. (seseorang menyarankan bahwa Python tidak akan dikompilasi ulang.)

  3. Jika kita menyimpan objek regex dengan menggunakan (A), objek regex masih akan masuk ke _cache dan entah bagaimana memerah. Tetapi kode kami tetap referensi dan objek regex tidak akan dilepaskan dari memori. Itu, Python tidak perlu mengkompilasi lagi.

  4. Perbedaan 2 detik dalam tes George yang dikompilasiInLoop vs yang dikompilasi terutama waktu yang dibutuhkan untuk membuat kunci dan mencari _cache. Itu tidak berarti waktu kompilasi regex.

  5. Tes George benar-benar kompilasi menunjukkan apa yang terjadi jika itu benar-benar melakukan kompilasi ulang setiap kali: itu akan menjadi 100x lebih lambat (ia mengurangi loop dari 1.000.000 menjadi 10.000).

Berikut adalah satu-satunya kasus yang (A + B) lebih baik daripada (C):

  1. Jika kita bisa menembolok referensi objek regex di dalam kelas.
  2. Jika kita perlu memanggil (B) berulang kali (di dalam satu lingkaran atau beberapa kali), kita harus menembolok referensi ke regex objek di luar loop.

Jika (C) cukup baik:

  1. Kami tidak dapat menyimpan referensi.
  2. Kami hanya menggunakannya sesekali.
  3. Secara keseluruhan, kami tidak memiliki terlalu banyak regex (menganggap yang dikompilasi tidak pernah memerah)

Rekap saja, berikut adalah ABC:

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

Terima kasih sudah membaca.

John Pang
sumber
8

Sebagian besar, ada sedikit perbedaan apakah Anda menggunakan re.compile atau tidak. Secara internal, semua fungsi diimplementasikan dalam hal langkah kompilasi:

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)

Selain itu, re.compile () mem-bypass tipuan ekstra dan logika caching:

_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

Selain manfaat kecepatan kecil dari menggunakan re.compile , orang juga menyukai keterbacaan yang berasal dari penamaan spesifikasi pola yang mungkin rumit dan memisahkannya dari logika bisnis tempat diterapkan:

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

Catatan, satu responden lain salah percaya bahwa file pyc disimpan pola dikompilasi secara langsung; namun, pada kenyataannya mereka dibangun kembali setiap kali PYC dimuat:

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

Pembongkaran di atas berasal dari file PYC untuk yang tmp.pyberisi:

import re
lc_vowels = re.compile(r'[aeiou]{2,5}')
Raymond Hettinger
sumber
1
adalah "di def search(pattern, string, flags=0):"salah ketik?
phuclv
1
Perhatikan bahwa jika patternsudah merupakan pola yang dikompilasi, overhead caching menjadi signifikan: hashing a SRE_Patternmahal dan polanya tidak pernah ditulis ke cache, jadi pencarian gagal setiap kali dengan a KeyError.
Eric Duminil
5

Secara umum, saya merasa lebih mudah untuk menggunakan flag (setidaknya lebih mudah untuk mengingat bagaimana), seperti re.Iketika mengkompilasi pola daripada menggunakan flag inline.

>>> 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']
ptone
sumber
Anda bisa menggunakan flags sebagai argumen ketiga re.findalljuga.
aderchox
5

Menggunakan contoh-contoh yang diberikan:

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

The pertandingan metode dalam contoh di atas tidak sama dengan yang digunakan di bawah ini:

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

re.compile () mengembalikan objek ekspresi reguler , yang artinya hadalah objek regex.

Objek regex memiliki metode pencocokan sendiri dengan parameter pos dan endpos opsional :

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

pos

Pos parameter opsional kedua memberikan indeks dalam string tempat pencarian akan dimulai; defaultnya ke 0. Ini tidak sepenuhnya sama dengan mengiris string; yang '^'karakter pola pertandingan di awal yang sebenarnya dari string dan pada posisi setelah baris baru, tetapi belum tentu di indeks di mana pencarian adalah untuk memulai.

endpos

Parameter opsional endpos membatasi seberapa jauh string akan dicari; itu akan menjadi seperti jika string adalah endpos karakter, sehingga hanya karakter dari pos ke endpos - 1akan dicari untuk pertandingan. Jika endpos kurang dari pos , tidak ada kecocokan yang akan ditemukan; jika tidak, jika rx adalah objek ekspresi reguler terkompilasi, rx.search(string, 0, 50)setara dengan rx.search(string[:50], 0).

Metode pencarian , findall , dan finditer objek regex juga mendukung parameter ini.

re.match(pattern, string, flags=0)tidak mendukung mereka seperti yang Anda lihat,
juga tidak mencari , menemukan , dan mencari mitra.

Sebuah objek pertandingan memiliki atribut yang melengkapi parameter ini:

match.pos

Nilai pos yang diteruskan ke metode search () atau match () dari objek regex. Ini adalah indeks ke dalam string di mana mesin RE mulai mencari kecocokan.

match.endpos

Nilai endpos yang diteruskan ke metode search () atau match () dari objek regex. Ini adalah indeks ke dalam string di mana mesin RE tidak akan pergi.


Sebuah objek regex memiliki dua atribut yang unik, mungkin berguna,:

regex.groups

Jumlah kelompok yang menangkap dalam pola.

regex.groupindex

Kamus yang memetakan nama grup simbolik yang ditentukan oleh (? P) ke nomor grup. Kamus kosong jika tidak ada kelompok simbolik yang digunakan dalam pola.


Dan akhirnya, objek pertandingan memiliki atribut ini:

match.re

Objek ekspresi reguler yang metode kecocokan () atau pencarian () menghasilkan instance kecocokan ini.

Abe Jujur
sumber
4

Mengesampingkan perbedaan kinerja, menggunakan kompilasi ulang dan menggunakan objek ekspresi reguler terkompilasi untuk melakukan kecocokan (operasi terkait ekspresi reguler apa pun) membuat semantik lebih jelas untuk run-time Python.

Saya memiliki beberapa pengalaman menyakitkan dalam debug beberapa kode sederhana:

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

dan kemudian saya akan gunakan bandingkan di

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

di mana patternPhrasesseharusnya menjadi variabel yang berisi string ekspresi reguler,x[columnIndex] adalah variabel yang berisi string.

Saya mengalami masalah yang patternPhrasestidak cocok dengan string yang diharapkan!

Tetapi jika saya menggunakan formulir re.compile:

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

lalu masuk

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

Python akan mengeluh bahwa "string tidak memiliki atribut kecocokan", seperti pemetaan argumen posisi compare, x[columnIndex]digunakan sebagai ekspresi reguler !, ketika saya benar-benar bermaksud

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

Dalam kasus saya, menggunakan re.compile lebih eksplisit dari tujuan ekspresi reguler, ketika nilainya tersembunyi di mata telanjang, jadi saya bisa mendapatkan lebih banyak bantuan dari pengecekan run-time Python.

Jadi moral pelajaran saya adalah bahwa ketika ekspresi reguler bukan hanya string literal, maka saya harus menggunakan kompilasi ulang untuk membiarkan Python membantu saya menegaskan asumsi saya.

Yu Shen
sumber
4

Ada satu tambahan penambahan menggunakan re.compile (), dalam bentuk menambahkan komentar ke pola regex saya menggunakan re.VERBOSE

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

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

Walaupun ini tidak mempengaruhi kecepatan menjalankan kode Anda, saya suka melakukannya dengan cara ini karena ini adalah bagian dari kebiasaan saya berkomentar. Saya benar-benar tidak suka menghabiskan waktu mencoba mengingat logika yang ada di balik kode saya 2 bulan kemudian ketika saya ingin membuat modifikasi.

cyneo
sumber
1
Saya sudah mengedit jawaban Anda. Saya pikir menyebutkan re.VERBOSEbermanfaat, dan itu menambahkan sesuatu yang jawaban lain tampaknya telah ditinggalkan. Namun, mengarahkan jawaban Anda dengan "Saya memposting di sini karena saya belum bisa berkomentar" pasti akan menghapusnya. Tolong jangan gunakan kotak jawaban untuk apa pun selain jawaban. Anda hanya memiliki satu atau dua jawaban yang bagus untuk tidak dapat berkomentar di mana saja (50 rep), jadi harap bersabar. Menempatkan komentar di kotak jawaban ketika Anda tahu Anda seharusnya tidak akan membuat Anda di sana lebih cepat. Ini akan membuat Anda menurunkan suara dan menghapus jawaban.
skrrgwasme
4

Menurut dokumentasi Python :

Urutannya

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

setara dengan

result = re.match(pattern, string)

tetapi menggunakan re.compile() dan menyimpan objek ekspresi reguler yang dihasilkan untuk digunakan kembali lebih efisien ketika ekspresi akan digunakan beberapa kali dalam satu program.

Jadi kesimpulan saya adalah, jika Anda akan mencocokkan pola yang sama untuk banyak teks yang berbeda, Anda lebih baik mengkompilasinya.

Chris Wu
sumber
3

Menariknya, kompilasi terbukti lebih efisien bagi saya (Python 2.5.2 di Win XP):

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

Menjalankan kode di atas sekali sebagaimana adanya, dan sekali dengan dua ifbaris berkomentar sebaliknya, regex yang dikompilasi dua kali lebih cepat

Eli Bendersky
sumber
2
Masalah yang sama dengan perbandingan kinerja dF. Ini tidak benar-benar adil kecuali Anda memasukkan biaya kinerja dari pernyataan kompilasi itu sendiri.
Carl Meyer
6
Carl, saya tidak setuju. Kompilasi hanya dieksekusi satu kali, sedangkan loop yang cocok dijalankan sejuta kali
Eli Bendersky
@eliben: Saya setuju dengan Carl Meyer. Kompilasi berlangsung dalam kedua kasus. Triptych menyebutkan bahwa caching terlibat, jadi dalam kasus optimal (tetap dalam cache) kedua pendekatan adalah O (n +1), meskipun bagian +1 agak tersembunyi ketika Anda tidak menggunakan re.compile secara eksplisit.
paprika
1
Jangan menulis kode tolok ukur Anda sendiri. Pelajari cara menggunakan timeit.py, yang termasuk dalam distribusi standar.
jemfinch
Berapa banyak waktu yang Anda buat ulang string pola dalam for loop. Overhead ini tidak bisa sepele.
IceArdor
3

Saya menjalankan tes ini sebelum menemukan diskusi di sini. Namun, setelah menjalankannya saya pikir saya setidaknya akan memposting hasil saya.

Saya mencuri dan mem-bastard contoh di "Menguasai Ekspresi Reguler" karya Jeff Friedl. Ini ada di macbook yang menjalankan OSX 10.6 (2GHz intel core 2 duo, ram 4GB). Versi Python adalah 2.6.1.

Jalankan 1 - menggunakan kompilasi ulang

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

Jalankan 2 - Tidak menggunakan kompilasi ulang

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
netricate
sumber
3

Jawaban ini mungkin datang terlambat tetapi merupakan penemuan yang menarik. Menggunakan kompilasi benar-benar dapat menghemat waktu Anda jika Anda berencana menggunakan regex beberapa kali (ini juga disebutkan dalam dokumen). Di bawah ini Anda dapat melihat bahwa menggunakan regex yang dikompilasi adalah yang tercepat ketika metode kecocokan dipanggil secara langsung. melewati regex yang dikompilasi untuk re.match membuatnya lebih lambat dan melewati re.match dengan string derai ada di suatu tempat di tengah.

>>> 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
Akilesh
sumber
3

Selain kinerja.

Menggunakan compilemembantu saya untuk membedakan konsep
1. modul (re) ,
2. objek regex
3. objek yang cocok
Ketika saya mulai belajar regex

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

Sebagai pelengkap, saya membuat modul lembar contekan yang lengkap reuntuk referensi Anda.

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',]
}
Kalkulus
sumber
2

Saya sangat menghargai semua jawaban di atas. Dari pendapat saya Ya! Yang pasti layak menggunakan kompilasi ulang alih-alih mengkompilasi regex, lagi dan lagi, setiap kali.

Menggunakan re.compile membuat kode Anda lebih dinamis, karena Anda dapat memanggil regex yang sudah dikompilasi, daripada mengkompilasi lagi dan lagi. Hal ini bermanfaat bagi Anda dalam hal:

  1. Upaya Prosesor
  2. Kompleksitas Waktu.
  3. Membuat regex Universal. (Dapat digunakan di findall, cari, cocokkan)
  4. Dan membuat program Anda terlihat keren.

Contoh:

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

Menggunakan di Findall

 find_alpha_numeric_string.findall(example_string)

Digunakan dalam pencarian

  find_alpha_numeric_string.search(example_string)

Demikian pula Anda dapat menggunakannya untuk: Match and Substitute

Adakron Gr8
sumber
1

Ini pertanyaan yang bagus. Anda sering melihat orang menggunakan kompilasi ulang tanpa alasan. Ini mengurangi keterbacaan. Tapi tentu saja ada banyak waktu di mana pra-kompilasi ekspresi diperlukan. Seperti ketika Anda menggunakannya berulang kali dalam satu lingkaran atau semacamnya.

Ini seperti segala sesuatu tentang pemrograman (sebenarnya segala sesuatu dalam hidup). Terapkan akal sehat.

PEZ
sumber
Sejauh yang saya tahu dari film singkat saya, Python in a Nutshell tidak menyebutkan penggunaan tanpa re.compile (), yang membuat saya penasaran.
Mat
Objek regex menambahkan satu objek lagi ke konteks. Seperti yang saya katakan, ada banyak situasi di mana re.compile () memiliki tempatnya. Contoh yang diberikan oleh OP bukan salah satunya.
PEZ
1

(berbulan-bulan kemudian) mudah untuk menambahkan cache Anda sendiri di sekitar re.match, atau hal lainnya -

""" 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, bukankah lebih baik jika: cachehint (size =), cacheinfo () -> size, hits, nclear ...

denis
sumber
1

Saya sudah memiliki banyak pengalaman menjalankan regex yang dikompilasi 1000 kali dibandingkan dengan kompilasi saat itu juga, dan belum melihat adanya perbedaan yang jelas.

Suara pada jawaban yang diterima mengarah pada asumsi bahwa apa yang dikatakan @Triptych berlaku untuk semua kasus. Ini belum tentu benar. Satu perbedaan besar adalah ketika Anda harus memutuskan apakah akan menerima string regex atau objek regex yang dikompilasi sebagai parameter ke fungsi:

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

Itu selalu lebih baik untuk mengkompilasi regex Anda jika Anda perlu menggunakannya kembali.

Perhatikan contoh pada timeit di atas yang mensimulasikan pembuatan objek regex yang dikompilasi satu kali pada waktu impor versus "on-the-fly" saat diperlukan untuk pertandingan.

Lonetwin
sumber
1

Sebagai jawaban alternatif, seperti yang saya lihat bahwa itu belum disebutkan sebelumnya, saya akan melanjutkan dan mengutip dokumen Python 3 :

Haruskah Anda menggunakan fungsi-fungsi tingkat modul ini, atau Anda harus mendapatkan pola dan memanggil metode sendiri? Jika Anda mengakses regex dalam satu lingkaran, pra-kompilasi itu akan menghemat beberapa panggilan fungsi. Di luar loop, tidak ada banyak perbedaan berkat cache internal.

Michael Kiros
sumber
1

Berikut adalah contoh penggunaan re.compilelebih dari 50 kali lebih cepat, seperti yang diminta .

Intinya sama dengan apa yang saya buat di komentar di atas, yaitu, menggunakan re.compilebisa menjadi keuntungan yang signifikan ketika penggunaan Anda seperti tidak mendapat banyak manfaat dari cache kompilasi. Ini terjadi setidaknya dalam satu kasus tertentu (yang saya jumpai dalam praktik), yaitu ketika semua hal berikut ini benar:

  • Anda memiliki banyak pola regex (lebih dari re._MAXCACHE, yang standarnya saat ini 512), dan
  • Anda sering menggunakan regex ini, dan
  • Anda berturut-turut penggunaan dengan pola yang sama dipisahkan oleh lebih dari re._MAXCACHEregex lain di antara keduanya, sehingga masing-masing dihapus dari cache antara penggunaan berturut-turut.
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}')

Contoh output yang saya dapatkan di laptop saya (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

Saya tidak peduli timeitkarena perbedaannya sangat mencolok, tetapi saya mendapatkan angka yang hampir sama secara kualitatif setiap kali. Perhatikan bahwa bahkan tanpa re.compile, menggunakan regex yang sama beberapa kali dan beralih ke yang berikutnya tidak begitu buruk (hanya sekitar 2 kali lebih lambat dari dengan re.compile), tetapi di urutan lain (perulangan melalui banyak regex), secara signifikan lebih buruk , seperti yang diharapkan. Juga, meningkatkan ukuran cache juga berfungsi: cukup mengatur re._MAXCACHE = len(patterns)di setup()atas (tentu saja saya tidak merekomendasikan melakukan hal-hal seperti dalam produksi karena nama dengan garis bawah secara konvensional "pribadi") menjatuhkan ~ 23 detik kembali ke ~ 0,7 detik, yang juga cocok dengan pemahaman kita.

ShreevatsaR
sumber
PS: jika saya hanya menggunakan 3 pola regex di seluruh kode saya, masing-masing dari mereka menggunakan (tanpa urutan tertentu) ratusan kali, cache regex akan menyimpan regex yang dikompilasi secara otomatis, benarkah itu?
Basj
@ Basj Saya pikir Anda bisa mencobanya dan melihat :) Tapi jawabannya, saya cukup yakin, adalah ya: satu-satunya biaya tambahan dalam hal ini AFAICT adalah hanya dengan hanya mencari pola dalam cache . Perhatikan juga bahwa cache bersifat global (level modul), jadi pada prinsipnya Anda bisa meminta pustaka dependensi melakukan pencarian regex di antara Anda, sehingga sulit untuk sepenuhnya yakin bahwa program Anda hanya menggunakan 3 (atau berapa pun) regex pola, tetapi akan sangat aneh untuk menjadi yang lain :)
ShreevatsaR
0

Ekspresi Reguler dikompilasi sebelum digunakan saat menggunakan versi kedua. Jika Anda akan mengeksekusi berkali-kali itu pasti lebih baik untuk mengkompilasinya terlebih dahulu. Jika tidak mengkompilasi setiap kali Anda cocok untuk satu kali saja tidak masalah.

Adam Peck
sumber
0

Preferensi keterbacaan / muatan kognitif

Bagi saya, keuntungan utama adalah bahwa saya hanya perlu mengingat, dan membaca, salah satu bentuk sintaks API rumit regex - yang <compiled_pattern>.method(xxx)bentuk daripada yang dan yangre.func(<pattern>, xxx) bentuk.

Itu re.compile(<pattern>) adalah sedikit boilerplate ekstra, benar.

Tetapi di mana regex prihatin, bahwa langkah kompilasi ekstra tidak mungkin menjadi penyebab besar beban kognitif. Dan pada kenyataannya, pada pola yang rumit, Anda bahkan mungkin mendapatkan kejelasan dari memisahkan deklarasi dari metode regex apa pun yang Anda gunakan.

Saya cenderung untuk menyelaraskan pola yang rumit di situs web seperti Regex101, atau bahkan dalam skrip uji minimal yang terpisah, kemudian membawanya ke dalam kode saya, jadi memisahkan deklarasi dari penggunaannya sesuai dengan alur kerja saya juga.

JL Peyret
sumber
-1

Saya ingin memotivasi bahwa pra-kompilasi baik secara konseptual dan 'harfiah' (seperti dalam 'pemrograman terpelajar') menguntungkan. lihat cuplikan kode ini:

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

di aplikasi Anda, Anda akan menulis:

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

ini sesederhana dalam hal fungsi yang bisa didapat. karena ini adalah contoh yang sangat singkat, saya menggabungkan cara untuk mendapatkan _text_has_foobar_re_searchsemuanya dalam satu baris. Kerugian dari kode ini adalah bahwa ia menempati sedikit memori untuk apa pun umur TYPOobjek perpustakaan; keuntungannya adalah ketika melakukan pencarian foobar, Anda akan lolos dengan dua panggilan fungsi dan dua pencarian kamus kelas. berapa banyak regex yang di-cache olehre dan overhead dari cache itu tidak relevan di sini.

bandingkan dengan gaya yang lebih umum, di bawah:

import re

class Typo:

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

Dalam aplikasi:

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

Saya siap mengakui bahwa gaya saya sangat tidak biasa untuk python, bahkan mungkin bisa diperdebatkan. Namun, dalam contoh yang lebih cocok dengan bagaimana python banyak digunakan, untuk melakukan satu pertandingan, kita harus membuat instance objek, melakukan tiga contoh pencarian kamus, dan melakukan tiga panggilan fungsi; Selain itu, kita mungkin masukre masalah caching saat menggunakan lebih dari 100 regex. juga, ekspresi reguler disembunyikan di dalam tubuh metode, yang sebagian besar waktu bukanlah ide yang bagus.

baik itu dikatakan bahwa setiap himpunan langkah --- ditargetkan, pernyataan impor alias; metode lain jika ada; pengurangan panggilan fungsi dan pencarian kamus objek --- dapat membantu mengurangi kompleksitas komputasi dan konseptual.

mengalir
sumber
2
WTF. Anda tidak hanya menggali pertanyaan lama yang sudah dijawab. Kode Anda juga non-idiomatis dan salah pada banyak tingkatan - (ab) menggunakan kelas sebagai ruang nama di mana modul cukup, menggunakan nama kelas, dll. Lihat pastebin.com/iTAXAWen untuk implementasi yang lebih baik. Belum lagi regex yang Anda gunakan rusak juga. Secara keseluruhan, -1
2
bersalah. ini adalah pertanyaan lama, tetapi saya tidak keberatan menjadi # 100 dalam percakapan yang melambat. pertanyaannya belum ditutup. saya memang memperingatkan kode saya bisa menjadi musuh selera. saya pikir jika Anda bisa melihatnya sebagai demonstrasi belaka dari apa yang bisa dilakukan dengan python, seperti: jika kita mengambil semuanya, semua yang kita percayai, sebagai opsional, dan mengotak-atik bersama dengan cara apa pun, seperti apa hal-hal yang kita bisa lakukan Dapatkan? saya yakin Anda bisa membedakan manfaat dan ketidakmampuan dari solusi ini dan dapat mengeluh lebih jelas. kalau tidak saya harus menyimpulkan klaim Anda tentang kesalahan hanya bergantung pada sedikit lebih dari PEP008
mengalir
2
Tidak, ini bukan tentang PEP8. Itu hanya konvensi penamaan, dan saya tidak akan pernah memilih untuk tidak mengikuti mereka. Saya menurunkan Anda karena kode yang Anda tampilkan ditulis dengan buruk. Itu menentang konvensi dan idiom tanpa alasan, dan merupakan inkarnasi dari optimasi permatur: Anda harus mengoptimalkan daylight hidup dari semua kode lain untuk menjadi hambatan, dan bahkan kemudian penulisan ulang ketiga yang saya tawarkan lebih pendek, lebih idiomatik dan sama cepatnya dengan alasan Anda (jumlah akses atribut yang sama).
"ditulis dengan buruk" - seperti mengapa tepatnya? "menentang konvensi dan idiom" - aku memperingatkanmu. "tanpa alasan" - ya saya punya alasan: sederhanakan di mana kompleksitas tidak memiliki tujuan; "inkarnasi optimasi prematur" - saya sangat menyukai gaya pemrograman yang memilih keseimbangan keterbacaan dan efisiensi; OP meminta elisitasi "manfaat dalam menggunakan re.compile", yang saya pahami sebagai pertanyaan tentang efisiensi. "(ab) menggunakan kelas sebagai ruang nama" - kata-kata Anda yang kasar. kelas ada sehingga Anda memiliki titik referensi "mandiri". Saya mencoba menggunakan modul untuk tujuan ini, kelas bekerja lebih baik.
mengalir
"Mengapitalisasi nama kelas", "Tidak, ini bukan tentang PEP8" - Anda benar-benar sangat marah sehingga Anda bahkan tidak tahu apa yang harus diperdebatkan lebih dulu. "WTF", " salah " --- lihat seberapa emosional Anda? lebih banyak objektivitas dan lebih sedikit buih.
mengalir
-5

Pemahaman saya adalah bahwa kedua contoh itu setara secara efektif. Satu-satunya perbedaan adalah bahwa pada yang pertama, Anda dapat menggunakan kembali ekspresi reguler yang dikompilasi di tempat lain tanpa membuatnya dikompilasi lagi.

Berikut referensi untuk Anda: http://diveintopython3.ep.io/refactoring.html

Memanggil fungsi pencarian objek pola dikompilasi dengan string 'M' melakukan hal yang sama dengan memanggil re.search dengan ekspresi reguler dan string 'M'. Hanya jauh, lebih cepat. (Pada kenyataannya, fungsi penelitian ulang hanya mengkompilasi ekspresi reguler dan memanggil metode pencarian objek pola yang dihasilkan untuk Anda.)

Matthew Maravillas
sumber
1
saya tidak downvote Anda, tetapi secara teknis ini salah: Python tidak akan mengkompilasi ulang pula
Triptych