[python] 파이썬에서 가장 효율적인 문자열 연결 방법은 무엇입니까?

(같은 파이썬에서 어떤 효율적인 대량 문자열 연결 방법이 모두 StringBuilder C # 또는 StringBuffer를 자바는)? 다음과 같은 방법을 찾았 습니다 .

  • 사용하여 간단한 연결 +
  • 문자열리스트와 join메소드 사용
  • 모듈 UserString에서 사용MutableString
  • 문자 배열과 array모듈 사용
  • 모듈 cStringIO에서 사용StringIO

그러나 전문가가 무엇을 사용하거나 제안합니까? 그 이유는 무엇입니까?

[ 관련 질문은 여기 ]



답변

당신은 이것에 관심이있을 것입니다 : Guido 의 최적화 일화 . 이 기사는 오래된 기사이며 다음과 같은 것들의 존재를 앞두고 있다는 것을 기억할 가치가 있지만 (거의 같지는 ''.join않지만 string.joinfields)

그것의 장점으로, 문제를 해결할 수 있다면 array모듈 가장 빠를 있습니다. 그러나 ''.join아마도 충분히 빠르며 관용적이기 때문에 다른 파이썬 프로그래머가 이해하기 쉽습니다.

마지막으로, 최적화의 황금률 : 당신이 알아야 할 것이 아니라면 추측하지 말고 측정하지 않으면 최적화하지 마십시오.

timeit모듈을 사용하여 다른 방법을 측정 할 수 있습니다 . 그것은 인터넷에서 추측을하는 임의의 낯선 사람 대신에 가장 빠른 것을 알려줄 수 있습니다 .


답변

''.join(sequenceofstrings) 가장 단순하고 빠른 방식으로 가장 잘 작동합니다.


답변

Python 3.6은 Literal String Interpolation 으로 알려진 구성 요소의 문자열 연결을위한 게임을 변경했습니다 .

mkoistinen의 답변 에서 테스트 사례가 주어지면 문자열이 있습니다.

domain = 'some_really_long_example.com'
lang = 'en'
path = 'some/really/long/path/'

경쟁자는

  • f'http://{domain}/{lang}/{path}'0.151 μS

  • 'http://%s/%s/%s' % (domain, lang, path) -0.321µs

  • 'http://' + domain + '/' + lang + '/' + path -0.356µs

  • ''.join(('http://', domain, '/', lang, '/', path))0.249 μS (일정한 길이의 튜플을 구축하는 것은 약간 빠른 일정한 길이의 목록을 구축하는 것보다 것을 통지).

따라서 현재 가장 짧고 가장 아름다운 코드도 가장 빠릅니다.

Python 3.6의 알파 버전에서는 f''문자열 구현이 가장 느 렸습니다. 실제로 생성 된 바이트 코드는 인수없이 불변으로 반환되는 ''.join()불필요한 호출이 str.__format__있는 경우와 거의 같습니다 self. 이러한 비 효율성은 3.6 최종 전에 해결되었습니다.

속도는 +내 컴퓨터에서 연결된 Python 2의 가장 빠른 방법과 대조 될 수 있습니다 . 그리고 그 소요 0.203 8 비트 문자열 μS 및 0.259 문자열이 모든 경우 유니 μS한다.


답변

그것은 당신이하는 일에 달려 있습니다.

Python 2.5 이후에는 + 연산자를 사용한 문자열 연결이 매우 빠릅니다. 두 개의 값을 연결하는 경우 + 연산자를 사용하는 것이 가장 좋습니다.

>>> x = timeit.Timer(stmt="'a' + 'b'")
>>> x.timeit()
0.039999961853027344

>>> x = timeit.Timer(stmt="''.join(['a', 'b'])")
>>> x.timeit()
0.76200008392333984

그러나 루프에서 문자열을 구성하는 경우 목록 결합 방법을 사용하는 것이 좋습니다.

>>> join_stmt = """
... joined_str = ''
... for i in xrange(100000):
...   joined_str += str(i)
... """
>>> x = timeit.Timer(join_stmt)
>>> x.timeit(100)
13.278000116348267

>>> list_stmt = """
... str_list = []
... for i in xrange(100000):
...   str_list.append(str(i))
... ''.join(str_list)
... """
>>> x = timeit.Timer(list_stmt)
>>> x.timeit(100)
12.401000022888184

…하지만 차이가 눈에 띄기 전에 상대적으로 많은 수의 문자열을 모아야합니다.


답변

John Fouhy의 답변에 따르면, 필요한 경우가 아니라면 최적화하지 마십시오. 그러나 여기에 있고이 질문을하는 경우 반드시 해야하기 때문일 수 있습니다 . 필자의 경우 문자열 변수에서 일부 URL을 빨리 조립해야했습니다. 나는 (지금까지) 문자열 형식 방법을 고려하고있는 사람이 없다는 것을 알았습니다. 그래서 나는 그것을 시도 할 것이라고 생각했으며, 주로 약간의 관심을 끌기 위해 좋은 보간을 위해 거기에 문자열 보간 연산자를 던질 것이라고 생각했습니다. 솔직히 말해서, 나는 이들 중 어느 것이 직접적인 ‘+’연산이나 ”.join ()까지 쌓을 것이라고 생각하지 않았습니다. 하지만 그거 알아? Python 2.7.5 시스템 에서 문자열 보간 연산자는 모두 규칙을 지정 하고 string.format ()이 가장 나쁜 성능을 나타냅니다.

# concatenate_test.py

from __future__ import print_function
import timeit

domain = 'some_really_long_example.com'
lang = 'en'
path = 'some/really/long/path/'
iterations = 1000000

def meth_plus():
    '''Using + operator'''
    return 'http://' + domain + '/' + lang + '/' + path

def meth_join():
    '''Using ''.join()'''
    return ''.join(['http://', domain, '/', lang, '/', path])

def meth_form():
    '''Using string.format'''
    return 'http://{0}/{1}/{2}'.format(domain, lang, path)

def meth_intp():
    '''Using string interpolation'''
    return 'http://%s/%s/%s' % (domain, lang, path)

plus = timeit.Timer(stmt="meth_plus()", setup="from __main__ import meth_plus")
join = timeit.Timer(stmt="meth_join()", setup="from __main__ import meth_join")
form = timeit.Timer(stmt="meth_form()", setup="from __main__ import meth_form")
intp = timeit.Timer(stmt="meth_intp()", setup="from __main__ import meth_intp")

plus.val = plus.timeit(iterations)
join.val = join.timeit(iterations)
form.val = form.timeit(iterations)
intp.val = intp.timeit(iterations)

min_val = min([plus.val, join.val, form.val, intp.val])

print('plus %0.12f (%0.2f%% as fast)' % (plus.val, (100 * min_val / plus.val), ))
print('join %0.12f (%0.2f%% as fast)' % (join.val, (100 * min_val / join.val), ))
print('form %0.12f (%0.2f%% as fast)' % (form.val, (100 * min_val / form.val), ))
print('intp %0.12f (%0.2f%% as fast)' % (intp.val, (100 * min_val / intp.val), ))

결과 :

# python2.7 concatenate_test.py
plus 0.360787868500 (90.81% as fast)
join 0.452811956406 (72.36% as fast)
form 0.502608060837 (65.19% as fast)
intp 0.327636957169 (100.00% as fast)

더 짧은 도메인과 더 짧은 경로를 사용하면 여전히 보간이 성공합니다. 그러나 그 차이는 줄이 길수록 더 두드러집니다.

좋은 테스트 스크립트를 얻었으므로 Python 2.6, 3.3 및 3.4에서도 테스트했습니다. 결과는 다음과 같습니다. 파이썬 2.6에서는 더하기 연산자가 가장 빠릅니다! Python 3에서는 join이 승리합니다. 참고 :이 테스트는 시스템에서 매우 반복 가능합니다. 따라서 ‘plus’는 2.6에서 항상 빠르며 ‘intp’는 2.7에서 항상 빠르며 ‘join’은 Python 3.x에서 항상 빠릅니다.

# python2.6 concatenate_test.py
plus 0.338213920593 (100.00% as fast)
join 0.427221059799 (79.17% as fast)
form 0.515371084213 (65.63% as fast)
intp 0.378169059753 (89.43% as fast)

# python3.3 concatenate_test.py
plus 0.409130576998 (89.20% as fast)
join 0.364938726001 (100.00% as fast)
form 0.621366866995 (58.73% as fast)
intp 0.419064424001 (87.08% as fast)

# python3.4 concatenate_test.py
plus 0.481188605998 (85.14% as fast)
join 0.409673971997 (100.00% as fast)
form 0.652010936996 (62.83% as fast)
intp 0.460400978001 (88.98% as fast)

# python3.5 concatenate_test.py
plus 0.417167026084 (93.47% as fast)
join 0.389929617057 (100.00% as fast)
form 0.595661019906 (65.46% as fast)
intp 0.404455224983 (96.41% as fast)

교훈 :

  • 때로는 내 가정이 잘못되었습니다.
  • 시스템 환경에 대해 테스트하십시오. 프로덕션 환경에서 실행됩니다.
  • 문자열 보간은 아직 죽지 않았습니다!

tl; dr :

  • 2.6을 사용하는 경우 + 연산자를 사용하십시오.
  • 2.7을 사용하는 경우 ‘%’연산자를 사용하십시오.
  • 3.x를 사용하는 경우 ”.join ()을 사용하십시오.

답변

매번 새로운 연결 후 새 문자열의 상대적 크기에 따라 다릅니다. +연산자를 사용하면 모든 연결에 대해 새 문자열이 작성됩니다. 중개 문자열이 상대적으로 길면 +새 중개 문자열이 저장되기 때문에 속도가 점점 느려집니다.

이 경우를 고려하십시오.

from time import time
stri=''
a='aagsdfghfhdyjddtyjdhmfghmfgsdgsdfgsdfsdfsdfsdfsdfsdfddsksarigqeirnvgsdfsdgfsdfgfg'
l=[]
#case 1
t=time()
for i in range(1000):
    stri=stri+a+repr(i)
print time()-t

#case 2
t=time()
for i in xrange(1000):
    l.append(a+repr(i))
z=''.join(l)
print time()-t

#case 3
t=time()
for i in range(1000):
    stri=stri+repr(i)
print time()-t

#case 4
t=time()
for i in xrange(1000):
    l.append(repr(i))
z=''.join(l)
print time()-t

결과

1 0.00493192672729

2 0.000509023666382

3 0.00042200088501

4 0.000482797622681

1 & 2의 경우 큰 문자열을 추가하고 join ()은 약 10 배 더 빠릅니다. 3 & 4의 경우 작은 문자열을 추가하고 ‘+’가 약간 더 빠릅니다.


답변

알 수없는 크기의 추가 가능한 문자열이 필요한 상황이 발생했습니다. 벤치 마크 결과는 다음과 같습니다 (python 2.7.3).

$ python -m timeit -s 's=""' 's+="a"'
10000000 loops, best of 3: 0.176 usec per loop
$ python -m timeit -s 's=[]' 's.append("a")'
10000000 loops, best of 3: 0.196 usec per loop
$ python -m timeit -s 's=""' 's="".join((s,"a"))'
100000 loops, best of 3: 16.9 usec per loop
$ python -m timeit -s 's=""' 's="%s%s"%(s,"a")'
100000 loops, best of 3: 19.4 usec per loop

이것은 ‘+ =’가 가장 빠름을 보여줍니다. skymind 링크의 결과가 약간 오래된 것입니다.

(두 번째 예제가 완료되지 않았으므로 최종 목록을 결합해야 함을 알 수 있습니다. 그러나 단순히 목록을 준비하는 것이 문자열 연결보다 시간이 오래 걸린다는 것을 보여줍니다.)