파이썬의 일반적인 반 패턴 +
은 루프에서 사용하여 문자열 시퀀스를 연결하는 것 입니다. 파이썬 인터프리터가 각 반복마다 새로운 문자열 객체를 만들어야하고 결국 2 차 시간이 걸리기 때문에 이것은 나쁘다. (최신 버전의 CPython은 어떤 경우에는 분명히이를 최적화 할 수 있지만 다른 구현에서는 그렇게 할 수 없으므로 프로그래머는 이에 의존하지 않는 것이 좋습니다.) ''.join
이 작업을 수행하는 올바른 방법입니다.
그러나 문자열 연결에는 절대 사용 하지 말고 대신 항상 또는 형식 문자열을 사용 해서는 안된다고 ( 여기 Stack Overflow 포함) 말했다고 들었습니다 . 두 개의 문자열 만 연결하는 경우 왜 이런 경우인지 이해할 수 없습니다. 내 이해가 정확하다면 2 차 시간이 걸리지 않아야 하며 나 보다 더 깨끗하고 읽기 쉽다고 생각 합니다 .+
''.join
a + b
''.join((a, b))
'%s%s' % (a, b)
+
두 문자열을 연결 하는 데 사용 하는 것이 좋은 습관 입니까? 아니면 내가 모르는 문제가 있습니까?
답변
합치에 아무것도 잘못이 두 가지 와 문자열 +
. 실제로 ''.join([a, b])
.
두 개 이상의 문자열을 연결 +
하는 것은 O (n ^ 2) 연산 (O (n)과 비교하여 join
)이므로 비효율적입니다. 그러나 이것은 루프 사용과 관련이 없습니다. 짝수 a + b + c + ...
는 O (n ^ 2)이므로 각 연결이 새 문자열을 생성하기 때문입니다.
CPython2.4 이상은이를 완화하려고 시도하지만 join
2 개 이상의 문자열을 연결할 때 사용하는 것이 좋습니다 .
답변
Plus 연산자는 두 개의 Python 문자열 을 연결하는 완벽한 솔루션 입니다. 그러나 두 개 이상의 문자열을 계속 추가하는 경우 (n> 25) 다른 것을 생각할 수 있습니다.
''.join([a, b, c])
트릭은 성능 최적화입니다.
답변
문자열 연결에 +를 사용해서는 안되며 대신 항상 ”.join을 사용하면 안된다는 가정은 신화 일 수 있습니다. 를 사용 +
하면 불변의 문자열 객체의 불필요한 임시 복사본이 생성 되는 것은 사실 이지만 자주 인용되지 않는 다른 사실은 join
루프에서 호출 하면 일반적으로 function call
. 예를 들어 보겠습니다.
연결된 SO 질문에서 하나와 더 큰 조작으로 두 개의 목록을 만듭니다.
>>> myl1 = ['A','B','C','D','E','F']
>>> myl2=[chr(random.randint(65,90)) for i in range(0,10000)]
두 가지 기능을 만들 수 있습니다 UseJoin
및 UsePlus
각각의 사용 join
과 +
기능을.
>>> 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)]
첫 번째 목록으로 timeit을 실행할 수 있습니다.
>>> 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
>>>
런타임은 거의 동일합니다.
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을 사용하면 오버 헤드를 증가시킬 수있는 불필요한 함수 호출이 발생하는 것으로 보입니다.
이제 질문으로 돌아갑니다. 모든 경우 에 +
over join
를 사용하지 말아야합니까 ?
나는 생각하지 않는다.
- 문제의 문자열 길이
- 연결 작업이 없습니다.
그리고 개발 초기 최적화 과정에서 벗어나는 것은 악합니다.
답변
여러 사람과 함께 작업 할 때 무슨 일이 일어나고 있는지 정확히 알기가 어려울 때가 있습니다. 연결 대신 형식 문자열을 사용하면 우리에게 여러 번 발생하는 특정 성가심을 피할 수 있습니다.
함수에 인수가 필요하고 문자열을받을 것으로 예상하여 작성합니다.
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
그렇습니다 : 형식 문자열을 사용할 수 있다면 그것을 수행하고 Python이 제공하는 것을 활용하십시오.
답변
빠른 테스트를했습니다.
import sys
str = e = "a xxxxxxxxxx very xxxxxxxxxx long xxxxxxxxxx string xxxxxxxxxx\n"
for i in range(int(sys.argv[1])):
str = str + e
시간을 정했습니다.
mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py 8000000
8000000 times
real 0m2.165s
user 0m1.620s
sys 0m0.540s
mslade@mickpc:/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) 시간을 나타내지 않습니다.
따라서 적어도 성능면에서 사용하는 +
것이 좋습니다.
답변
Python 문서에 따르면 str.join ()을 사용하면 다양한 Python 구현에서 성능 일관성을 얻을 수 있습니다. CPython은 s = s + t의 2 차 동작을 최적화하지만 다른 Python 구현에서는 그렇지 않을 수 있습니다.
CPython 구현 세부 정보 : s와 t가 모두 문자열 인 경우 CPython과 같은 일부 Python 구현은 일반적으로 s = s + t 또는 s + = t 형식의 할당에 대해 내부 최적화를 수행 할 수 있습니다. 적용 가능한 경우이 최적화는 2 차 런타임을 훨씬 적게 만듭니다. 이 최적화는 버전 및 구현에 따라 다릅니다. 성능에 민감한 코드의 경우 버전 및 구현에서 일관된 선형 연결 성능을 보장하는 str.join () 메서드를 사용하는 것이 좋습니다.
Python 문서의 시퀀스 유형 (각주 [6] 참조)
답변
파이썬 3.8에서 다음을 사용합니다.
string4 = f'{string1}{string2}{string3}'