programing

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

lastmoon 2023. 7. 6. 22:32
반응형

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

Python에 효율적인 대량 문자열 연결 방법이 있습니까(C#의 StringBuilder 또는 Java의 StringBuffer)?

여기서 다음 방법을 찾았습니다.

  • 다을사용간연결을 사용한 +
  • 및 "" " " 를 합니다.join방법
  • 용사를 합니다.UserStringMutableString 컨트롤 모듈
  • 및 문배열및용사를 array 컨트롤 모듈
  • 용사를 합니다.cStringIOStringIO 컨트롤 모듈

무엇을 사용해야 하며 그 이유는 무엇입니까?

(관련 질문이 있습니다.)

다음과 같은 이점에 관심이 있을 것입니다.Guido최적화 일화입니다.비록 이것이 오래된 기사이고 같은 것들의 존재보다 앞선다는 것도 기억할 가치가 있습니다.''.join 생각엔)string.joinfields거의 동일함)

그 힘에 힘입어,array당신이 당신의 문제를 그것에 슈혼할 수 있다면 모듈이 가장 빠를 수 있습니다.그렇지만''.join충분히 빠르며 관용적이어서 다른 파이썬 프로그래머들이 이해하기 쉽다는 장점이 있습니다.

마지막으로, 최적화의 황금률입니다. 필요하다는 것을 알지 못하면 최적화하지 말고, 추측하기보다는 측정하십시오.

모듈을 사용하여 다양한 방법을 측정할 수 있습니다.인터넷에서 모르는 사람들이 추측하는 것 대신에, 그것은 당신에게 어떤 것이 가장 빠른지 알려줄 수 있습니다.

모든 구성 요소를 미리 알고 있는 경우 Python 3.6에 도입된 -strings 또는 형식화된 문자열이라고도 하는 리터럴 문자열 보간을 사용합니다.

Mkoistinen의 답변에서 나온 테스트 케이스를 고려할 때, 문자열이 있습니다.

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

리눅스에서 Python 3.6을 사용하는 내 컴퓨터의 경쟁자들과 그들의 실행 시간은 IPython에 의해 시간이 지정되고 그것의 모듈 시간은

  • f'http://{domain}/{lang}/{path}'0.304파운드

  • 'http://%s/%s/%s' % (domain, lang, path).321µs

  • 'http://' + domain + '/' + lang + '/' + path.356µs

  • ''.join(('http://', domain, '/', lang, '/', path))0.249µs(일정한 길이의 튜플을 작성하는 것이 일정한 길이의 목록을 작성하는 것보다 약간 빠름).

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


2의 2와 될 수 .+8비트 문자열의 경우 0.203µs필요하며, 문자열이 모두 유니코드인 경우 0.259µs가 필요합니다.

3에서는 (Python 3.6 버전)의 f''문자열은 가능한 가장 느렸습니다 - 실제로 생성된 바이트 코드는 거의 동일합니다.''.join()필요한호있는사례출에 불필요한 경우str.__format__논쟁 없이 그냥 돌아올 것입니다.self은 3final 되었습니다.)이러한 비효율성은 3.6 기말고사 전에 해결되었습니다.)

''.join(sequence_of_strings)일반적으로 가장 간편하고 빠르게 작동합니다.

당신이 무엇을 하느냐에 따라 다릅니다.

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 시스템에서는 문자열 보간 연산자가 모든 문자열과 문자열을 제어합니다.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), ))

결과:

# Python 2.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에서는 플러스 연산자가 가장 빠릅니다!파이썬 3에서 조인이 승리합니다.참고: 이러한 테스트는 내 시스템에서 매우 반복 가능합니다.따라서 Python 3.x에서는 'plus'가 항상 빠릅니다. 2.6에서는 'intp'가 항상 빠르며, Python 3.x에서는 'join'이 항상 빠릅니다.

# Python 2.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)

# Python 3.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)

# Python 3.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)

# Python 3.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:

  • Python 2.6을 사용하는 경우 '+' 연산자를 사용합니다.
  • Python 2.7을 사용하는 경우 '%' 연산자를 사용합니다.
  • Python 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 3.11에는 % 포맷에 대한 몇 가지 최적화가 있지만 f-string을 고수하는 것이 더 나을 수 있습니다.

Python 3.8.6/3.9의 경우 perfplot에서 오류가 발생했기 때문에 더티 해킹을 해야 했습니다.여기서 을 가정합니다.x[0]입니다.a그리고.x[1]이라b:

Performance

큰 데이터의 경우 그림이 거의 같습니다.작은 데이터의 경우

Performance 2

이것은 코드, 큰 데이터 == 범위(8), 작은 데이터 == 범위(4)입니다.

import perfplot

from random import choice
from string import ascii_lowercase as letters

def generate_random(x):
    data = ''.join(choice(letters) for i in range(x))
    sata = ''.join(choice(letters) for i in range(x))
    return [data,sata]

def fstring_func(x):
    return [ord(i) for i in f'{x[0]}{x[1]}']

def format_func(x):
    return [ord(i) for i in "{}{}".format(x[0], x[1])]

def replace_func(x):
    return [ord(i) for i in "|~".replace('|', x[0]).replace('~', x[1])]

def join_func(x):
    return [ord(i) for i in "".join([x[0], x[1]])]

perfplot.show(
    setup=lambda n: generate_random(n),
    kernels=[
        fstring_func,
        format_func,
        replace_func,
        join_func,
    ],
    n_range=[int(k ** 2.5) for k in range(4)],
)

중간 데이터가 있고 네 개의 문자열이 있는 경우x[0],x[1],x[2],x[3]: 문자열대신두":

def generate_random(x):
    a =  ''.join(choice(letters) for i in range(x))
    b =  ''.join(choice(letters) for i in range(x))
    c =  ''.join(choice(letters) for i in range(x))
    d =  ''.join(choice(letters) for i in range(x))
    return [a,b,c,d]

Performance 3

f-string을 고수하는 것이 좋습니다.또한 %s의 속도는 .format()과 비슷합니다.

크기를 알 수 없는 추가 가능한 문자열이 필요한 상황에 부딪혔습니다.벤치마크 결과는 다음과 같습니다(파이썬 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

이것은 '+='이 가장 빠르다는 것을 보여주는 것 같습니다.스카이마인드 링크의 결과는 약간 구식입니다.

(두 번째 예는 완전하지 않다는 것을 알고 있습니다.최종 목록에 참여해야 합니다.그러나 이것은 단순히 목록을 준비하는 것이 문자열 연결보다 더 오래 걸린다는 것을 보여줍니다.)

1년 후, Mkoistinen의 답변을 Python 3.4.3으로 테스트해 보겠습니다.

  • + 0.963564149000(95.83% 빠른 속도)
  • 0.923408469000(100.00% 빠른 속도)에 가입합니다.
  • 폼 1.501130934000(61.51% 빠른 속도)
  • intp 1.019677452000(90.56% 빠른 속도)

변한 게 없습니다.조인은 여전히 가장 빠른 방법입니다.가독성 측면에서 문자열 보간(intp)이 가장 좋은 선택인 경우에도 문자열 보간을 사용할 수 있습니다.

아마도 "Python 3.6의 새로운 f-strings"가 문자열을 연결하는 가장 효율적인 방법일 것입니다.

%s 사용

>>> timeit.timeit("""name = "Some"
... age = 100
... '%s is %s.' % (name, age)""", number = 10000)
0.0029734770068898797

.format 사용

>>> timeit.timeit("""name = "Some"
... age = 100
... '{} is {}.'.format(name, age)""", number = 10000)
0.004015227983472869

f-string 사용

>>> timeit.timeit("""name = "Some"
... age = 100
... f'{name} is {age}.'""", number = 10000)
0.0019175919878762215

짧은 문자열의 작은 집합(예: 몇 자 이하의 문자열 2개 또는 3개)의 경우 더하기가 여전히 훨씬 빠릅니다.파이썬 2와 3에서 mkoistinen의 멋진 스크립트 사용하기:

plus 2.679107467004 (100.00% as fast)
join 3.653773699996 (73.32% as fast)
form 6.594011374000 (40.63% as fast)
intp 4.568015249999 (58.65% as fast)

따라서 코드가 많은 수의 개별적인 작은 연결을 수행할 때 속도가 중요한 경우 선호되는 방법입니다.

Jason Baker의 벤치마크에서 영감을 받아 10가지를 비교한 간단한 벤치마크를 소개합니다."abcdefghijklmnopqrstuvxyz"문자열, 표시.join()더 빠릅니다. 이렇게 작은 변수가 증가하더라도 다음과 같습니다.

연결

>>> x = timeit.Timer(stmt='"abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz"')
>>> x.timeit()
0.9828147209324385

합류하다

>>> x = timeit.Timer(stmt='"".join(["abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz"])')
>>> x.timeit()
0.6114138159765048

언급URL : https://stackoverflow.com/questions/1316887/what-is-the-most-efficient-string-concatenation-method-in-python

반응형