2010-01-31 8 views

Antwort

20

Je schneller Weg, es zu tun ist, str.translate() zu verwenden Dies ist ~ 50-mal schneller als die Art und Weise ist

# You only need to do this once 
>>> title_trans=''.join(chr(c) if chr(c).isupper() or chr(c).islower() else '_' for c in range(256)) 

>>> "[email protected]%^".translate(title_trans) 
'abcde________' 

# Using map+lambda 
$ python -m timeit '"".join(map(lambda x: x if (x.isupper() or x.islower()) else "_", "[email protected]#$".strip()))' 
10000 loops, best of 3: 21.9 usec per loop 

# Using str.translate 
$ python -m timeit -s 'titletrans="".join(chr(c) if chr(c).isupper() or chr(c).islower() else "_" for c in range(256))' '"[email protected]#$".translate(titletrans)' 
1000000 loops, best of 3: 0.422 usec per loop 

# Here is regex for a comparison 
$ python -m timeit -s 'import re;transre=re.compile("[\W\d]+")' 'transre.sub("_","[email protected]#$")' 
100000 loops, best of 3: 3.17 usec per loop 

Hier eine Version für Unicode

# coding: UTF-8 

def format_title_unicode_translate(title): 
    return title.translate(title_unicode_trans) 

class TitleUnicodeTranslate(dict): 
    def __missing__(self,item): 
     uni = unichr(item) 
     res = u"_" 
     if uni.isupper() or uni.islower(): 
      res = uni 
     self[item] = res 
     return res 
title_unicode_trans=TitleUnicodeTranslate() 

print format_title_unicode_translate(u"Metallica Μεταλλικα") 

Beachten Sie, dass die griechischen Buchstaben zählen als obere und untere, also sind sie nicht substituiert. Wenn sie substituiert sind, einfach die Bedingung

 if item<256 and (uni.isupper() or uni.islower()): 
+0

+1, sehr gute Idee. Der einzige Nachteil, den ich mir vorstellen kann, ist, dass dies bei Unicode-Zeichenfolgen nicht richtig funktioniert, wenn Nicht-ASCII-Zeichen berücksichtigt werden müssen. –

+1

@Tim, Unicode hat auch einen übersetzen - die Semantik ist anders, lass mich sehen, ob ich es zur Arbeit bekommen kann ... –

+0

@Tim, Unicode-Version ist up. Das Übersetzungsmapping wird auf Anfrage erstellt und wird daher immer weniger Fehler haben, wenn mehr Strings übersetzt werden. –

17
import re 
title = re.sub("[\W\d]", "_", title.strip()) 

sollte schneller sein.

Wenn Sie eine Reihe von benachbarten, nicht-Buchstaben mit einem Unterstrich zu ersetzen, verwenden

title = re.sub("[\W\d]+", "_", title.strip()) 

statt, die noch schneller ist.

Ich lief einen Zeitvergleich:.

C:\>python -m timeit -n 100 -s "data=open('test.txt').read().strip()" "''.join(map(lambda x: x if (x.isupper() or x.islower()) else '_', data))" 
100 loops, best of 3: 4.51 msec per loop 

C:\>python -m timeit -n 100 -s "import re; regex=re.compile('[\W\d]+'); data=open('test.txt').read().strip()" "title=regex.sub('_',data)" 
100 loops, best of 3: 2.35 msec per loop 

Dies wird auf Unicode-Strings arbeiten, auch (unter Python 3, \W jedes Zeichen, das keine Unicode Wortzeichen ist unter 2 Python, würden Sie muss hierfür zusätzlich die UNICODE Flag setzen.

+0

Die Art und Weise Sie timeit verwenden ist die ganze Zeit zu zählen, die Datei zu öffnen und zu lesen.Sie sollten dieses Zeug in den '-s' Teil verschieben, um aussagekräftige Ergebnisse zu erhalten. –

+0

Danke, Sie haben natürlich Recht. Da dies in beiden Beispielen gemacht wurde, sollte der Fehler in beiden Fällen gleich sein (und ist, ich habe es gerade versucht). Interessanterweise fand ich, dass das Vorkompilieren der Regex keinen Unterschied machte. Ich habe die Timing-Beispiele trotzdem aktualisiert. –

+0

Beachten Sie, dass Sie Flags mit re nicht setzen können; Sie müssen re.compile() verwenden, um Flags anzugeben und sub() für das Ergebnis aufzurufen. (Dies ist eine merkwürdige API-Auslassung.) Ihre Antwort wäre besser mit der "+" Version entfernt; Es ist nicht das, wonach er gefragt hat, also ist es nur ablenkend. –

2

ändern Statt (x.isupper() or x.islower()) Sie sollten in der Lage sein x.isalpha() zu verwenden. Die isalpha() Methode kann True für '_' zurückgeben (ich erinnere mich nicht, wenn es tut oder nicht), aber dann werden Sie nur '_' mit '_' ersetzen, so dass kein Schaden angerichtet wird. (Danke dafür, dass Sie darauf hingewiesen haben, KennyTM.)

+0

Eigentlich könnte es '_' selbst als '_' zählen alphabetischer Charakter, also vielleicht nicht. Probieren Sie es aus und sehen Sie. – MatrixFrog

+2

Das Ersetzen von '_' durch' _' (oder nicht) ist harmlos. – kennytm

1

Neugierig aus meinen eigenen Gründen schrieb ich ein schnelles Skript, um die verschiedenen hier aufgeführten Ansätze zu testen und nur das Lambda zu entfernen, von dem ich erwartete (fälschlicherweise) würde das Original beschleunigen Lösung.

Die kurze Version ist, dass der str.translate Ansatz die anderen wegbläst. Nebenbei bemerkt ist die Regex-Lösung, obwohl sie nahe beieinander ist, korrekt, wie oben beschrieben.

Hier Programm mein Test ist:

import re 
from time import time 


def format_title(title): 
    return ''.join(map(lambda x: x if (x.isupper() or x.islower()) else "_", 
         title.strip())) 


def format_title_list_comp(title): 
    return ''.join([x if x.isupper() or x.islower() else "_" for x in 
        title.strip()]) 


def format_title_list_comp_is_alpha(title): 
    return ''.join([x if x.isalpha() else "_" for x in title.strip()]) 


def format_title_is_alpha(title): 
    return ''.join(map(lambda x: x if x.isalpha() else '_', title.strip())) 


def format_title_no_lambda(title): 

    def trans(c): 
     if c.isupper() or c.islower(): 
      return c 
     return "_" 

    return ''.join(map(trans, title.strip())) 


def format_title_no_lambda_is_alpha(title): 

    def trans(c): 
     if c.isalpha(): 
      return c 
     return "_" 

    return ''.join(map(trans, title.strip())) 


def format_title_re(title): 
    return re.sub("[\W\d]+", "_", title.strip()) 


def format_title_re_corrected(title): 
    return re.sub("[\W\d]", "_", title.strip()) 


TITLE_TRANS = ''.join(chr(c) if chr(c).isalpha() else '_' for c in range(256)) 


def format_title_with_translate(title): 
    return title.translate(TITLE_TRANS) 


ITERATIONS = 200000 
EXAMPLE_TITLE = "abc123def_$%^!FOO BAR*bazx-bif" 


def timetest(f): 
    start = time() 
    for i in xrange(ITERATIONS): 
     result = f(EXAMPLE_TITLE) 
    diff = time() - start 
    return result, diff 


baseline_result, baseline_time = timetest(format_title) 


def print_result(f, result, time): 
    if result == baseline_result: 
     msg = "CORRECT" 
    else: 
     msg = "INCORRECT" 
    diff = time - baseline_time 
    if diff < 0: 
     indicator = "" 
    else: 
     indicator = "+" 
    pct = (diff/baseline_time) * 100 
    print "%s: %0.3fs %s%0.3fs [%s%0.4f%%] (%s - %s)" % (
     f.__name__, time, indicator, diff, indicator, pct, result, msg) 


print_result(format_title, baseline_result, baseline_time) 

print "----" 

for f in [format_title_is_alpha, 
      format_title_list_comp, 
      format_title_list_comp_is_alpha, 
      format_title_no_lambda, 
      format_title_no_lambda_is_alpha, 
      format_title_re, 
      format_title_re_corrected, 
      format_title_with_translate]: 
    alt_result, alt_time = timetest(f) 
    print_result(f, alt_result, alt_time) 

Und hier sind die Ergebnisse:

format_title: 3.121s +0.000s [+0.0000%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
---- 
format_title_is_alpha: 2.336s -0.785s [-25.1470%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_list_comp: 2.369s -0.751s [-24.0773%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_list_comp_is_alpha: 1.735s -1.386s [-44.4021%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_no_lambda: 2.992s -0.129s [-4.1336%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_no_lambda_is_alpha: 2.377s -0.744s [-23.8314%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_re: 1.290s -1.831s [-58.6628%] (abc_def__FOO_BAR_bazx_bif - INCORRECT) 
format_title_re_corrected: 1.338s -1.782s [-57.1165%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_with_translate: 0.098s -3.022s [-96.8447%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
  • EDITED: Ich habe eine Variation, die Listenkomprehensionen verbessern zeigt deutlich die ursprüngliche Implementierung sowie eine korrekte Regex-Implementierung, die zeigt, dass sie immer noch fast so schnell ist, wenn sie korrekt ist. Natürlich gewinnt str.translate immer noch sehr gut.
+0

Die Regex-Lösung ist falsch, da sie mehrere benachbarte Nicht-Buchstaben als nur einen Unterstrich ersetzt. Lege das '+' nach der Zeichenklasse ab und es wird korrekt sein, wenn auch langsamer. Ich denke, die Frage ist, ob Sie wirklich lange Unterstreichungsstriche in Ihren Ersatzsträngen haben wollen oder nicht ... –

0
import string,sys 
letters=string.letters 
mystring = list("abc134#[email protected]##$%%$*&(()#def") 
for n,c in enumerate(mystring): 
    if not c in letters: 
    mystring[n]="_" 
print ''.join(mystring) 
Verwandte Themen