Domanda

In Python, il dove e quando di utilizzare concatenazione di stringhe contro la sostituzione di stringhe mi sfugge. Come la concatenazione di stringhe ha visto grandi spinte in termini di prestazioni, è questo (sempre più) una decisione stilistica piuttosto che una pratica?

Per un esempio concreto, come si dovrebbe gestire la costruzione di URI flessibili:

DOMAIN = 'http://stackoverflow.com'
QUESTIONS = '/questions'

def so_question_uri_sub(q_num):
    return "%s%s/%d" % (DOMAIN, QUESTIONS, q_num)

def so_question_uri_cat(q_num):
    return DOMAIN + QUESTIONS + '/' + str(q_num)

Edit: Ci sono stati anche i suggerimenti di aderire ad una lista di stringhe e per l'utilizzo di sostituzione di nome. Queste sono le varianti sul tema centrale, che è, da che parte è il modo giusto per farlo in quel momento? Grazie per le risposte!

È stato utile?

Soluzione

La concatenazione è (molto) più veloce in base alla mia macchina. Ma stilisticamente, sono disposto a pagare il prezzo di sostituzione se la prestazione non è critica. Beh, e se ho bisogno di formattazione, non c'è bisogno di chiedere anche la questione ... non c'è altra scelta che usare l'interpolazione / template.

>>> import timeit
>>> def so_q_sub(n):
...  return "%s%s/%d" % (DOMAIN, QUESTIONS, n)
...
>>> so_q_sub(1000)
'http://stackoverflow.com/questions/1000'
>>> def so_q_cat(n):
...  return DOMAIN + QUESTIONS + '/' + str(n)
...
>>> so_q_cat(1000)
'http://stackoverflow.com/questions/1000'
>>> t1 = timeit.Timer('so_q_sub(1000)','from __main__ import so_q_sub')
>>> t2 = timeit.Timer('so_q_cat(1000)','from __main__ import so_q_cat')
>>> t1.timeit(number=10000000)
12.166618871951641
>>> t2.timeit(number=10000000)
5.7813972166853773
>>> t1.timeit(number=1)
1.103492206766532e-05
>>> t2.timeit(number=1)
8.5206360154188587e-06

>>> def so_q_tmp(n):
...  return "{d}{q}/{n}".format(d=DOMAIN,q=QUESTIONS,n=n)
...
>>> so_q_tmp(1000)
'http://stackoverflow.com/questions/1000'
>>> t3= timeit.Timer('so_q_tmp(1000)','from __main__ import so_q_tmp')
>>> t3.timeit(number=10000000)
14.564135316080637

>>> def so_q_join(n):
...  return ''.join([DOMAIN,QUESTIONS,'/',str(n)])
...
>>> so_q_join(1000)
'http://stackoverflow.com/questions/1000'
>>> t4= timeit.Timer('so_q_join(1000)','from __main__ import so_q_join')
>>> t4.timeit(number=10000000)
9.4431309007150048

Altri suggerimenti

Non dimenticare la sostituzione di nome:

def so_question_uri_namedsub(q_num):
    return "%(domain)s%(questions)s/%(q_num)d" % locals()

Diffidare di concatenazione di stringhe in un ciclo! Il costo di concatenazione di stringhe è proporzionale alla lunghezza del risultato. Looping si porta direttamente alla terra di N-squared. Alcune lingue ottimizzeranno la concatenazione alla stringa più recentemente assegnato, ma è rischioso contare sul compilatore per ottimizzare il vostro algoritmo quadratico fino a lineare. Meglio usare il primitivo (join?) Che prende un intero elenco di stringhe, fa una dotazione unica, e li concatena in una volta.

"Come la concatenazione di stringhe ha visto grandi aumenta in termini di prestazioni ..."

Se la performance è, questo è bene sapere.

Tuttavia, i problemi di prestazioni che ho visto sono mai sceso a operazioni sulle stringhe. Ho generalmente messo nei guai con I / O, l'ordinamento e O ( n 2 ) le operazioni di essere i colli di bottiglia.

Fino a quando le operazioni di stringa sono i limitatori di prestazioni, mi bastone con le cose che sono evidenti. Per lo più, che è la sostituzione quando è una linea o meno, la concatenazione quando ha senso, e uno strumento di modello (come Mako), quando si tratta di grandi dimensioni.

Cosa si desidera concatenare / interpolare e come si desidera formattare il risultato dovrebbe guidare la vostra decisione.

  • String interpolazione permette di aggiungere facilmente la formattazione. In realtà, la versione di interpolazione di stringhe non fa la stessa cosa che la vostra versione di concatenazione; in realtà aggiunge una barra in più in avanti prima del parametro q_num. Per fare la stessa cosa, si dovrebbe scrivere return DOMAIN + QUESTIONS + "/" + str(q_num) in questo esempio.

  • L'interpolazione rende più facile per formattare numerici; "%d of %d (%2.2f%%)" % (current, total, total/current) sarebbe molto meno leggibile in forma concatenazione.

  • La concatenazione è utile quando non si dispone di un numero fisso di elementi a stringa-izzare.

Inoltre, sappiamo che Python 2.6 introduce una nuova versione di interpolazione stringa, chiamato stringa di template :

def so_question_uri_template(q_num):
    return "{domain}/{questions}/{num}".format(domain=DOMAIN,
                                               questions=QUESTIONS,
                                               num=q_num)

String template è previsto per eventualmente sostituire% -interpolation, ma questo non accadrà per un bel po ', credo.

Stavo testando la velocità di diversi metodi di stringa concatenazione / sostituzione per curiosità. Una ricerca su Google su questo argomento mi ha portato qui. Ho pensato di inserire il risultato del test, nella speranza che possa aiutare qualcuno a decidere.

    import timeit
    def percent_():
            return "test %s, with number %s" % (1,2)

    def format_():
            return "test {}, with number {}".format(1,2)

    def format2_():
            return "test {1}, with number {0}".format(2,1)

    def concat_():
            return "test " + str(1) + ", with number " + str(2)

    def dotimers(func_list):
            # runs a single test for all functions in the list
            for func in func_list:
                    tmr = timeit.Timer(func)
                    res = tmr.timeit()
                    print "test " + func.func_name + ": " + str(res)

    def runtests(func_list, runs=5):
            # runs multiple tests for all functions in the list
            for i in range(runs):
                    print "----------- TEST #" + str(i + 1)
                    dotimers(func_list)

... Dopo l'esecuzione runtests((percent_, format_, format2_, concat_), runs=5), ho trovato che il metodo% era di circa due volte più veloce come gli altri su queste piccole stringhe. Il metodo concat era sempre il più lento (a malapena). Ci sono state molto piccole differenze quando si passa le posizioni nel metodo format(), ma le posizioni di commutazione era sempre almeno .01 più lento del metodo format regolare.

Esempio dei risultati del test:

    test concat_()  : 0.62  (0.61 to 0.63)
    test format_()  : 0.56  (consistently 0.56)
    test format2_() : 0.58  (0.57 to 0.59)
    test percent_() : 0.34  (0.33 to 0.35)

ho eseguito questi perché io uso la concatenazione di stringhe nei miei scritti, e mi chiedevo che cosa il costo era. Li ho imbattuto in diversi ordini per assicurarsi che nulla è stato interferire, o di ottenere migliori prestazioni di essere prima o l'ultima. Su un lato nota, ho buttato in alcuni generatori di stringhe più lunghe in quelle funzioni come "%s" + ("a" * 1024) e concat regolare era quasi 3 volte più veloce (1.1 vs 2.8) come utilizzando i metodi format e %. Immagino che dipende sulle corde, e ciò che si sta cercando di ottenere. Se le prestazioni è importante, potrebbe essere meglio per provare cose diverse e testarli. Io tendo a scegliere la leggibilità sopra la velocità, a meno che la velocità diventa un problema, ma questo è solo a me. SO non piaceva la mia copia / incolla, ho dovuto mettere 8 spazi su tutto per farlo sembrare giusto. Io di solito uso 4.

Ricordate, le decisioni stilistiche sono decisioni pratiche, se mai intenzione di mantenere o il debug del codice :-) C'è una citazione famosa da Knuth (possibilmente citando Hoare?): "Dobbiamo dimenticare piccola efficienze, dicono circa il 97% del tempo: l'ottimizzazione prematura è la radice di tutti i mali "

.

Fino a quando si è attenti a non (diciamo) trasformare un compito O (n) in un O (n 2 ) compito, vorrei andare con qualsiasi a trovare più facile da capire ..

Io uso la sostituzione ovunque posso. Io uso solo la concatenazione se sto costruendo una stringa in dire un ciclo for.

In realtà la cosa giusta da fare, in questo caso (percorsi di costruzione) è quello di utilizzare os.path.join. Non concatenazione di stringhe o interpolazione

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top