2013-03-12 21 views
19

Comprendere "meglio" come più veloce, elegante e leggibile.Quale modo migliore per concatenare una stringa in python?

Ho due stringhe (a e b) che potrebbero essere nulle o meno. E voglio concatenare loro separati da un trattino solo se entrambi non sono nulli:

a - b

a (se B è nullo)

b (dove a è null)

+8

Cosa vuoi se sono entrambi nulli? – unholysampler

+0

Ok, questo lo so. Ma ci sono molti modi per farlo (come puoi vedere nelle risposte qui sotto). –

+0

What downvotefest (su Q e A) per una domanda piuttosto semplice:/ – Junuxx

risposta

47
# Concatenates a and b with ' - ' or Coalesces them if one is None 
'-'.join([x for x in (a,b) if x]) 

Modifica
Ecco i risultati di questo algoritmo (notare che nessuno funzionerà lo stesso di ''):

>>> '-'.join([x for x in ('foo','bar') if x]) 
'foo-bar' 
>>> '-'.join([x for x in ('foo','') if x]) 
'foo' 
>>> '-'.join([x for x in ('','bar') if x]) 
'bar' 
>>> '-'.join([x for x in ('','') if x]) 
'' 

* Si noti inoltre che la valutazione di Rafael, nel suo post sottostante, mostrava solo una differenza di .0002 secs su un migliaio di iterazioni del metodo di filtro, si può ritenere che una differenza così piccola possa essere dovuta a incongruenze in ava risorse di sistema disponibili al momento dell'esecuzione dello script. Ho eseguito la sua implementazione timeit su diverse iterazioni e ho riscontrato che l'algoritmo sarà più veloce circa il 50% delle volte, né con un ampio margine. Dimostrando quindi che sono fondamentalmente equivalenti.

+0

:-(perché il voto negativo? Solo curioso – Hoopdady

+1

Wow, qualcuno ha alcune opinioni molto forti. – Hoopdady

+5

come ho detto, scusa per le parole forti, ma ho riscontrato un sacco di codice * non commentato * simile in grandi progetti, in cui trascorri molto tempo a capire "che diamine sta andando qui"? –

12

Qui è uno opzione:

("%s - %s" if (a and b) else "%s%s") % (a,b) 

EDIT: Come sottolineato da mgilson, questo codice non riuscirebbe avanti con None 'sa modo migliore (ma meno leggibile) sarebbe:

"%s - %s" % (a,b) if (a and b) else (a or b) 
+0

Non male. Non è affatto male. – mgilson

+0

@mgilson grazie;) – zenpoy

+0

Si noti che si prende il caso 'a = None' se lo si riscrive leggermente:' "% s -% s"% (a, b) se (a e b) altro (a o b) '- Ma è più difficile da leggere direi. – mgilson

35

Come di qualcosa di semplice come:

# if I always need a string even when `a` and `b` are both null, 
# I would set `output` to a default beforehand. 
# Or actually, as Supr points out, simply do `a or b or 'default'` 
if a and b: 
    output = '%s - %s' % (a, b) 
else: 
    output = a or b 

Edit: Un sacco di interessanti soluzioni in questo thread. Ho scelto questa soluzione perché sottolineo la leggibilità e la velocità, almeno in termini di implementazione. Non è la soluzione più scalabile o interessante, ma per questo scopo funziona e mi consente di passare al prossimo problema molto rapidamente.

+9

+1 per semplicità - non necessariamente la semplicità del codice, ma della mappatura tra comprensione umana e codice. Sì, credo che sia all'incirca lo stesso di leggibilità (e manutenibilità, affidabilità, testabilità). – LarsH

+4

+1, preferirei vedere questo nel codice di produzione, come afferma chiaramente l'intento – Matsemann

+1

+1 per semplicità – Carson

0

Prova questo:

def myfunc(a,b): 
    if not b: 
     return a 
    elif not a: 
     return b 
    else: 
     return a+' - '+b 

O

def myfunc(a,b): 
    if a and b: 
     return a+' - '+b 
    else: 
     return a or b 
+1

Non hai davvero bisogno di un 'else' se il precedente' if' ritornerà dalla funzione. Mi sono chiesto spesso se fosse incluso per la leggibilità, ma ho deciso di non farlo. Sarebbe bello se potessimo fare 'return a if b else c'. –

+4

@Carl: Sicuramente you _can_ do 'return a if b else c' – Eric

+0

@Eric: Sweet. Potrei giurare che l'ho provato una volta e ho ricevuto un errore di sintassi, ma l'ho appena provato e funziona perfettamente. Grazie. Questa è una caratteristica così bella. –

32

Wow, sembra una domanda caldo: p La mia proposta:

' - '.join(filter(bool, (a, b))) 

che dà:

>>> ' - '.join(filter(bool, ('', ''))) 
'' 
>>> ' - '.join(filter(bool, ('1', ''))) 
'1' 
>>> ' - '.join(filter(bool, ('1', '2'))) 
'1 - 2' 
>>> ' - '.join(filter(bool, ('', '2'))) 
'2' 

Ovviamente, None si comporta come '' con questo codice.

+4

Ottima soluzione! Il più elegante per ora. –

+4

'filter (None, (a, b))' funzionerebbe anche, ma 'bool' è probabilmente un po 'più esplicito. Per la cronaca, questo in realtà non è diverso rispetto alla versione precedente che utilizza invece una lista-comp. – mgilson

+1

Usando 'bool' ottengo un * leggero * calo delle prestazioni.Probabilmente perché passare 'None' permette di chiamare i metodi di verità più direttamente, mentre passando' bool' il 'filter' deve fare una normale chiamata di funzione. – Bakuriu

3

C'è un sacco di risposte qui :)

Le due migliori risposte (prestazioni e codice pulito in una sola riga) sono le risposte di @icecrime e @Hoopdady

Entrambi asnwers risultati altrettanto, l'unica differenza è la prestazione.

cases = [ 
(None, 'testB'), 
('', 'testB'), 
('testA', 'testB'), 
('testA', ''), 
('testA', None), 
(None, None) 
] 

for case in cases: print '-'.join(filter(bool, case)) 
'testB' 
'testB' 
'testA-testB' 
'testA' 
'testA' 

for case in cases: print '-'.join([x for x in case if x]) 
'testB' 
'testB' 
'testA-testB' 
'testA' 
'testA' 

Allora, facciamo un punto di riferimento :)

import timeit 

setup = ''' 
cases = [ 
    (None, "testB"), 
    ("", "testB"), 
    ("testA","testB"), 
    ("testA", ""), 
    ("testA", None), 
    (None, None) 
] 
''' 

print min(timeit.Timer(
    "for case in cases: '-'.join([x for x in case if x])", setup=setup 
).repeat(5, 1000)) 
0.00171494483948 

print min(timeit.Timer(
    "for case in cases: '-'.join(filter(bool, case))", setup=setup 
).repeat(5, 1000)) 
0.00283288955688 

Ma, come ha detto @mgilson, utilizzando None anziché bool come funzione nella filter produce lo stesso risultato e hanno una performance molto migliore:

print min(timeit.Timer(
    "for case in cases: '-'.join(filter(None, case))", setup=setup 
).repeat(5, 1000)) 
0.00154685974121 

Quindi, il miglior risultato è la risposta dato dal @icecrime con il suggerimento di @mgilson:

012.351.
'-'.join(filter(None, (a,b))) 

La differenza di prestazioni è in millisecondi per 1000 iterazioni (microsecondi per iterazione). Quindi questi due metodi hanno prestazioni alquanto simili e, per quasi tutti i progetti, è possibile sceglierne uno qualsiasi; Nel caso in cui il progetto deve avere una migliore performance, considerando microsecondi, si potrebbe seguire questo benchmark :)

+1

C'è qualcosa che mi manca. Ho provato a copiare e incollare il tuo codice e ogni volta errore – Hoopdady

+0

Anche quando eseguo il mio test del tempo, la mia strada è leggermente più veloce. – Hoopdady

+0

@Hoopdady, grazie! Qualcosa non funziona con le identificazioni nella dichiarazione di 'setup' e mi manca un') 'nel tuo codice, risolto ora! –

0

Qualcosa pythonian, leggibile ed elegante:

strings = string1, string2 

'{0}{1}{2}'.format(
    # output first string if it's not empty 
    strings[0] if strings[0] else '', 

    # join with hyphen if both strings are not empty  
    '-' if all(strings) else '', 

    # output second string if it's not empty 
    strings[1] if strings[1] else '' 
    ) 

e troppo veloce;)

0

lo farei in questo modo:

def show_together(item1=None, item2=None, seperator='-'): 
    return '%s%s%s' % (item1,seperator,item2) if item1 and item2 else item1 or item2 



>>> show_together(1,1) 
'1-1' 

>>> show_together(1) 
1 

>>> show_together() 
>>> show_together(4,4,'$') 
'4$4' 
4

volevo solo offrire una soluzione Toxotes' riscritto come uno di linea utilizzando format.

output = "{0} - {1}".format(a, b) if (a and b) else (a or b) 
Problemi correlati