2013-03-12 2 views
19

Поймите «лучше», как более быструю, изящную и удобочитаемую.Какой лучший способ конкатенировать строку в python?

У меня есть две строки (a и b), которые могут быть нулевыми или нет. И я хочу объединить их разделенных дефисом, только если оба не равно нулю:

a - b

a (если б равна нулю)

b (где нулевая)

+8

Что вам нужно, если они оба являются нулевыми? – unholysampler

+0

Хорошо, это я знаю. Но есть много способов сделать это (как вы можете видеть в ответах ниже). –

+0

Что такое downvotefest (как для Q, так и для A) для довольно простого вопроса:/ – Junuxx

ответ

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

Редактировать
Вот результаты этого алгоритма (Заметим, что ни будет работать так же, как «»):

>>> '-'.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]) 
'' 

* Также обратите внимание, что оценка Рафаэля в его сообщении ниже показала разницу в 0,0002 секунды над 1000 итерациями метода фильтра, можно предположить, что такая небольшая разница может быть вызвана несогласованностью в ava во время запуска скрипта. Я выполнил его timeit-реализацию на нескольких итерациях и обнаружил, что любой алгоритм будет быстрее примерно в 50% случаев, ни с большим отрывом. Таким образом, они в основном эквивалентны.

+0

:-(Почему голосую? Просто интересно. – Hoopdady

+1

Ничего себе, у кого-то есть очень сильные мнения. – Hoopdady

+5

как я уже сказал, извините за сильные слова, но я столкнулся с большим количеством подобных * uncommented * кода в крупных проектах, где вы тратите много времени на то, чтобы выяснить, «что здесь происходит»? –

12

Вот один опция:

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

EDIT: Как указано на mgilson, этот код будет неудачной с None «са лучший способ (но менее читаемым один) будет:

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

Неплохо. Совсем неплохо. – mgilson

+0

@mgilson thanks;) – zenpoy

+0

Обратите внимание, что вы забираете случай 'a = None', если вы его немного переписываете:' '% s -% s"% (a, b) if (a и b) else (a или б) '- Но это труднее читать, я бы сказал. – mgilson

35

Как о чем-то простым, как:

# 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: Много интересных решений в этой теме. Я выбрал это решение, потому что я подчеркивал удобочитаемость и быстроту, по крайней мере, с точки зрения реализации. Это не самое масштабируемое или интересное решение, но для этой области действия оно работает и позволяет быстро перейти к следующей проблеме.

+9

+1 для простоты - не простота кода обязательно, а отображение между пониманием человеком и кодом. Да, я думаю, это примерно так же, как читаемость (и ремонтопригодность, надежность, тестируемость). – LarsH

+4

+1, я бы предпочел увидеть это в производственном коде, так как он четко указывает на намерение – Matsemann

+1

+1 для простоты – Carson

0

Попробуйте это:

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

Или

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

Вам действительно не нужно 'else', если предыдущий' if' вернется из функции. Я часто задавался вопросом, следует ли включить его в удобочитаемость, но решила против него. Было бы неплохо, если бы мы могли сделать «return a if b else c». –

+4

@ Карл: Конечно, вы _can_ do 'return a if b else c' – Eric

+0

@ Эрик: Сладкий. Я мог бы поклясться, что однажды попробовал, и получил синтаксическую ошибку, но я просто попробовал, и все работает отлично. Благодарю. Это такая приятная особенность. –

32

Ого, кажется, как горячий вопрос: р Мое предложение:

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

Что дает:

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

Очевидно, None ведет себя как '' с помощью этого кода.

+4

Отличное решение! Самый элегантный на данный момент. –

+4

'filter (None, (a, b))' также будет работать, но 'bool', вероятно, немного более явный. Для записи это действительно не отличается от версии выше, которая вместо этого использует list-comp. – mgilson

+1

Использование 'bool' Я получаю * небольшое * снижение производительности.Вероятно, потому что передача «Нет» позволяет более прямо обращаться к методам истины, а при передаче 'bool'' filter' должен выполнять обычный вызов функции. – Bakuriu

3

Там много ответов здесь :)

Две лучшие ответы (производительность и чистый код в одной строке) являются ответы @icecrime и @Hoopdady

Оба asnwers результаты одинаково, с той лишь разницей это производительность.

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' 

Так что давайте делать тест :)

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 

Но, как сказал @mgilson, используя None вместо bool как функция filter производит тот же результат и имеют достаточно большую производительность:

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

Таким образом, лучший результат ответ дал на @icecrime с предложением от @mgilson:

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

Разница рабочих характеристик составляет миллисекунды на 1000 итераций (микросекунды на итерацию). Таким образом, эти два метода имеют совершенно равную производительность, и для практически любого проекта вы можете выбрать любой; В случае, если ваш проект должен иметь более высокую производительность, учитывая микросекунд, вы могли бы следовать этому бенчмарк :)

+1

Есть что-то, что мне не хватает. Я пытался копировать и вставлять ваш код, и он каждый раз ошибки – Hoopdady

+0

Также, когда я запускаю свой собственный тест времени, мой способ немного быстрее. – Hoopdady

+0

@Hoopdady, спасибо! Что-то пошло не так с идентификациями в декларации 'setup' и я пропустил') 'в вашем коде, исправленном сейчас! –

0

Что-то pythonian, читаемый и элегантную:

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

И слишком быстро;)

0

Я хотел бы сделать это нравится:

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

Я просто хотел предложить решение toxotes' переписан как один лайнер с использованием format.

output = "{0} - {1}".format(a, b) if (a and b) else (a or b) 
Смежные вопросы