2012-04-06 7 views
95

파이썬의 일반적인 반 패턴은 + 루프를 사용하여 일련의 문자열을 연결하는 것입니다. 파이썬 인터프리터가 각 반복에 대해 새로운 문자열 객체를 만들어야하고 이것이 2 차 시간을 끝내기 때문에 이것은 나쁘다. (CPython의 최신 버전은 분명히 어떤 경우에는이를 최적화 할 수 있지만 다른 구현체는 그렇게 할 수 없기 때문에 프로그래머는이를 의지하지 않아야합니다.) ''.join이 올바른 방법입니다.두 개의 문자열을 연결하는 데 '+'를 사용하지 않는 이유는 무엇입니까?

그러나, 나는 들었어요은 (including here on Stack Overflow)이 , 문자열 연결에 대한 지금까지 사용 +는, 대신에 항상 ''.join 또는 형식 문자열을 사용하지 않을해야한다고 말했다. 두 문자열 만 연결하는 경우 왜 이런 경우인지 이해할 수 없습니다. 내 이해가 정확하면 2 차 시간이 걸릴해서는 안되며 a + b''.join((a, b)) 또는 '%s%s' % (a, b)보다 깔끔하고 읽기 쉽습니다.

+을 사용하여 두 개의 문자열을 연결하는 것이 좋습니까? 아니면 내가 모르는 문제가 있습니까?

+0

그 깔끔한 (풋 노트 [6] 참조) 당신은 연결하지 더 제어 할 수 있습니다. 하지만 조금 더 느린 문자열 박살 내기 : P –

+0

'+'가 빠르거나 느린 것을 말하고 있습니까? 그리고 왜? – Taymon

+1

는 +, 빠르다 '[2]에서 %의 timeit을 "A"* 80 + "B"* 80' '1000000 루프 3의 기기 : loop' 당 356 NS '에서 [3] : % '1000000 개의 루프, 3 개당 907 ns/루프 당 ' –

답변

83

두 개의 문자열을 +으로 연결하는 데는 아무런 문제가 없습니다. 사실 ''.join([a, b])보다 읽기가 쉽습니다.

+과 2 개 이상의 문자열을 연결하면 O (n^2) 연산 (join에 비해)이므로 비효율적입니다. 그러나 이것은 루프를 사용하지 않아도됩니다. a + b + c + ...조차도 O (n^2)이기 때문에 각 연결이 새 문자열을 생성합니다.

CPython2.4 이상에서는 CPython2.4 이상을 완화하려고 시도하지만 2 개 이상의 문자열을 연결할 때 join을 사용하는 것이 좋습니다.

+4

당신은 '.join ((a, b)) 맞습니까? – Mutant

+2

@Mutant :'.join'은 반복문을 취하므로'.join ([a, b])'와'.join ((a, b)) '가 유효합니다. – foundling

+0

재미있는 타이밍은 CPython 2.3+에 대해서조차도 http://stackoverflow.com/a/12/12131382/378826 (Lennart Regebro)의 허용 된 답변 (2013 년부터)에서 '+'또는 '+ = 이 "append/join"패턴은 문제 해결에 대한 아이디어를보다 분명하게 드러내는 경우에 유용합니다. – Dilettant

42

플러스 연산자는 두 개의 파이썬 문자열을 연결하는 완벽한 솔루션입니다. 그러나 두 개 이상의 문자열 (n> 25)을 계속 추가하는 경우 다른 것을 생각하고 싶을 수 있습니다.

''.join([a, b, c]) 트릭은 성능 최적화입니다.

+2

튜플이 목록보다 좋지 않습니까? – ThiefMaster

+5

Tuple은 빠를 것입니다. 코드는 단지 예일뿐입니다. 일반적으로 긴 다중 문자열 입력은 동적입니다. –

+0

@Mikko Ohtamaa : 동적 문자열을 튜플에 넣을 수 있습니다. – martineau

5

문자열 연결에 +를 사용해서는 안되며, 대신 항상 ''.join을 사용하는 것은 신화 일 수 있습니다. +을 사용하면 불변 문자열 개체의 불필요한 임시 복사본이 만들어 지지만 실제로 다른 인용되지 않은 사실은 루프에서 join을 호출하면 일반적으로 function call의 오버 헤드가 추가된다는 사실입니다. 당신의 모범을 보자.

는 두 목록, 링크 된 SO 질문에서, 다른 하나는 더 큰 만들기 각 join+ 기능을 사용하는 두 가지 기능 UseJoinUsePlus을 만들 수 있습니다

>>> myl1 = ['A','B','C','D','E','F'] 
>>> myl2=[chr(random.randint(65,90)) for i in range(0,10000)] 

제작.

>>> def UsePlus(): 
    return [myl[i] + myl[i + 1] for i in range(0,len(myl), 2)] 

>>> def UseJoin(): 
    [''.join((myl[i],myl[i + 1])) for i in range(0,len(myl), 2)] 

은 첫 번째 목록 그들은 거의 같은 런타임이

>>> myl=myl1 
>>> t1=timeit.Timer("UsePlus()","from __main__ import UsePlus") 
>>> t2=timeit.Timer("UseJoin()","from __main__ import UseJoin") 
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000) 
2.48 usec/pass 
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000) 
2.61 usec/pass 
>>> 

와 timeit을 실행할 수 있습니다.

는 사용 cprofile 명령을

>>> myl=myl2 
>>> cProfile.run("UsePlus()") 
     5 function calls in 0.001 CPU seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.001 0.001 0.001 0.001 <pyshell#1376>:1(UsePlus) 
     1 0.000 0.000 0.001 0.001 <string>:1(<module>) 
     1 0.000 0.000 0.000 0.000 {len} 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 
     1 0.000 0.000 0.000 0.000 {range} 


>>> cProfile.run("UseJoin()") 
     5005 function calls in 0.029 CPU seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.015 0.015 0.029 0.029 <pyshell#1388>:1(UseJoin) 
     1 0.000 0.000 0.029 0.029 <string>:1(<module>) 
     1 0.000 0.000 0.000 0.000 {len} 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 
    5000 0.014 0.000 0.014 0.000 {method 'join' of 'str' objects} 
     1 0.000 0.000 0.000 0.000 {range} 

을 수 있습니다 그리고 그것은 사용이 오버 헤드에 추가 할 수 있습니다 불필요한 함수 호출의 결과에 가입하는 것이 보인다.

이제 질문으로 돌아옵니다. 모든 경우에 join을 초과하는 +의 사용을 방해해야합니까? 나는 더 믿을

는 가지 질문

  • 병합 동작 없음에 고려 문자열의

    1. 길이를주의해야한다.

    개발 과정의 사전 성숙 최적화는 악합니다.

  • +7

    물론 루프 내부에서 'join'을 사용하지 않는 것이 좋습니다. 루프는 조인에 전달되는 시퀀스를 생성합니다. – jsbueno

    2

    나는 빠른 테스트했을 :

    import sys 
    
    str = e = "a xxxxxxxxxx very xxxxxxxxxx long xxxxxxxxxx string xxxxxxxxxx\n" 
    
    for i in range(int(sys.argv[1])): 
        str = str + e 
    

    을하고 시간 초과 :

    [email protected]:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py 8000000 
    8000000 times 
    
    real 0m2.165s 
    user 0m1.620s 
    sys  0m0.540s 
    [email protected]:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py 16000000 
    16000000 times 
    
    real 0m4.360s 
    user 0m3.480s 
    sys  0m0.870s 
    

    분명히 a = a + b 경우에 대한 최적화가있다. 그것은 의심스러운 O (n^2) 시간을 나타내지 않습니다.

    적어도 성능면에서 보면 +을 사용하는 것이 좋습니다.

    +2

    "join"케이스와 비교해 볼 수 있습니다. pypy, jython, ironpython 등과 같은 다른 Python 구현의 문제가 있습니다. – jsbueno

    6

    여러 사람과 함께 작업 할 때 어떤 일이 발생하는지 정확히 알기가 어렵습니다. 우리에게 시간의 전체 톤 일어난 하나 개의 특정 두통 피할 수 대신 연결의 형식 문자열을 사용하여 :

    말은, 함수는 인수가 필요합니다, 당신은 문자열을 얻을 것으로 예상 쓰기 :

    In [1]: def foo(zeta): 
        ...:  print 'bar: ' + zeta 
    
    In [2]: foo('bang') 
    bar: bang 
    

    그래서이 함수는 코드 전체에서 꽤 자주 사용될 수 있습니다. 동료는 자신이하는 일을 정확히 알지는 못하지만, 내부에서 완전히 최대 속도를 낼 필요는 없으며, 함수가 문자열을 기대한다는 것을 알지 못할 수도 있습니다. 그래서 그들은 이것으로 끝낼 수 있습니다 : 당신은 단지 형식 문자열을 사용하는 경우

    In [3]: foo(23) 
    --------------------------------------------------------------------------- 
    TypeError         Traceback (most recent call last) 
    
    /home/izkata/<ipython console> in <module>() 
    
    /home/izkata/<ipython console> in foo(zeta) 
    
    TypeError: cannot concatenate 'str' and 'int' objects 
    

    아무 문제가 없을 것입니다 :

    In [1]: def foo(zeta): 
        ...:  print 'bar: %s' % zeta 
        ...:  
        ...:  
    
    In [2]: foo('bang') 
    bar: bang 
    
    In [3]: foo(23) 
    bar: 23 
    

    같은이 __str__을 정의하는 객체의 모든 유형에 대한 사실을있는 뿐만 아니라 전달 될 수있다

    In [1]: from datetime import date 
    
    In [2]: zeta = date(2012, 4, 15) 
    
    In [3]: print 'bar: ' + zeta 
    --------------------------------------------------------------------------- 
    TypeError         Traceback (most recent call last) 
    
    /home/izkata/<ipython console> in <module>() 
    
    TypeError: cannot concatenate 'str' and 'datetime.date' objects 
    
    In [4]: print 'bar: %s' % zeta 
    bar: 2012-04-15 
    

    을 그래서 예 : 당신은 형식 문자열 를 사용할 수있는 경우는에 파이썬이 무엇을 활용하고 할 제공.

    +1

    +1에 대한 정당한 의견이 분분한 의견입니다. 나는 여전히 나는 '+'을 선호한다고 생각한다. – Taymon

    +0

    왜 그냥 foo 메서드를 다음과 같이 정의하지 않습니까? print 'bar :'+ str (zeta)? – EngineerWithJava54321

    +0

    @ EngineerWithJava54321 예를 들어, 'zeta = u "는 \ xac \ u1234 \ u20ac \ U00008000"'이므로'print '표시 줄을 사용해야합니다 :'+ unicode (zeta)' . '% s'은 그것에 대해 생각할 필요없이 바로하고, 훨씬 더 짧습니다. – Izkata

    -3

    ''.join ([a, b])+보다 좋은 해결책입니다.

    코드

    형태 A + = B 또는 = A + B가 파이썬없는 단점 다른 구현 않는 방식으로 기록 (PyPy, 자이, IronPython의, 사이 썬, 사이코을 등)되어야하기 때문에 CPython에서도 깨지기 쉽고 전혀 구현되지 않음 refcounting(참조 계산은 객체, 포인터 또는 핸들과 같은 리소스에 참조, 포인터 또는 핸들 수를 저장하는 기술입니다. 메모리, 디스크 공간 또는 기타 리소스)

    https://www.python.org/dev/peps/pep-0008/#programming-recommendations

    +0

    'a + = b'는 파이썬의 모든 구현에서 작동합니다. 그 중 일부에서는 루프 내에서 완료되었을 때 _이 2 차 시간이 걸립니다. 문제는 루프의 문자열 연결 _outside_에 관한 것입니다. – Taymon

    2

    str.join()을 사용하면 Python의 다양한 구현에서 성능 일관성을 얻을 수 있습니다. CPython은 s = s + t의 2 차 동작을 최적화하지만 다른 Python 구현에서는 그렇지 않을 수도 있습니다.

    CPython의 구현 세부 : s 및 t는 모두 문자열이있는 경우 등의 CPython 일부 파이썬 구현은 일반적으로 폼 S = (S)의 할당 상태를 적절히 최적화를 수행 + t 또는 S + = 티. 이 적용되면이 최적화는 이차 런타임을 훨씬 짧게 만듭니다. 이 최적화는 버전 및 구현 에 따라 다릅니다. 성능에 민감한 코드의 경우 str.join() 메서드를 사용하는 것이 바람직하며 버전과 구현간에 일관된 선형 연결 성능을 보장합니다.

    Sequence Types in Python docs