파이톤에서 끈을 연결하기 위해 선호하는 방법은 무엇인가?
파이선스 때부터.string
바꿀 수 없어, 어떻게 하면 줄을 더 효율적으로 연결할 수 있을까?
난 이렇게 쓸 수 있어:
s += stringfromelsewhere
또는 이와 같은 경우:
s = []
s.append(somestring)
# later
s = ''.join(s)
이 질문을 쓰다가 주제에 대해 이야기하는 좋은 기사를 발견했다.
http://www.skymind.com/~ocrow/changes_string/
그런데 파이썬 2.x에 있으니까 문제는 파이썬 3에 뭔가 변화가 있었을까?
문자열 변수에 문자열을 추가하는 가장 좋은 방법은+
또는+=
읽기 쉽고 빠르기 때문이다.또한 속도도 그만큼 빠른데, 어느 쪽을 선택하느냐가 취향의 문제고, 후자가 가장 일반적이다.여기 시간대가 있다.timeit
모듈:
a = a + b:
0.11338996887207031
a += b:
0.11040496826171875
그러나 리스트를 가지고 있고, 리스트에 추가한 다음 그 리스트에 합류할 것을 추천하는 사람들은 리스트에 문자열을 추가하는 것이 아마도 문자열을 확장하는 것에 비해 매우 빠르기 때문에 그렇게 한다.그리고 이것은 어떤 경우에는 사실일 수 있다.예를 들어, 여기서 100만 개의 문자열이 하나의 문자열에 먼저 추가된 다음 리스트에 추가된다.
a += b:
0.10780501365661621
a.append(b):
0.1123361587524414
좋아, 결과 문자열이 백만 자일 때도 덧셈이 더 빨랐다는 것이 밝혀졌다.
이제 천 자 길이의 줄을 십만 번 더 붙이도록 하자.
a += b:
0.41823482513427734
a.append(b):
0.010656118392944336
따라서, 끝 문자열은 약 100MB의 길이가 된다.그것은 꽤 느렸고, 리스트에 추가하는 것이 훨씬 더 빨랐다.그 타이밍에 결승전이 포함되어 있지 않다.a.join()
그럼 얼마나 걸릴까?
a.join(a):
0.43739795684814453
이런 경우에도 추가/조인 속도가 느리다는 것이 밝혀졌다.
그렇다면 이 권고안은 어디에서 나온 것일까?파이썬 2?
a += b:
0.165287017822
a.append(b):
0.0132720470428
a.join(a):
0.114929914474
음, 극도로 긴 문자열을 사용할 경우 추가/조인이 훨씬 더 빠를 수 있다(일반적으로 그렇지 않은 문자열, 메모리에 100MB인 문자열이 있다면 어떤 문자열을 사용하시겠습니까?)
하지만 진짜 해결사는 파이썬 2.3이다.시간표조차 보여주지 않을 곳이요. 너무 느려서 아직 끝나지 않았으니까.이 시험들은 갑자기 몇 분이 걸린다.후기 피톤스 하의 속도만큼 빠른 부록/조인만 제외한다.
응. 석기시대 파이톤에서는 끈 결합이 매우 느렸었어.그러나 2.4에서는 더 이상 (또는 적어도 Python 2.4.7)이 아니기 때문에 2008년에 추가/조인 사용 권고가 구식이 되었고, Python 2.3이 업데이트되는 것을 중단했으므로, 당신은 사용을 중지했어야 했다. :-)
(업데이트:내가 테스트를 좀 더 신중하게 했을 때+
그리고+=
파이썬 2.3의 두 문자열에서도 더 빠르다..''.join()
오해임에 틀림없다)
그러나 이것은 CPython이다.다른 구현에는 다른 문제가 있을 수 있다.그리고 이것은 조기 최적화가 모든 악의 근원이 되는 또 다른 이유일 뿐이다.먼저 측정하지 않는 한 "더 빠르다고 생각되는 기법을 사용하지 마십시오.
따라서 "최상" 버전의 문자열 연결은 + 또는 +=를 사용하는 것이다.그리고 만약 그것이 당신에게 느리다고 밝혀지면, 그것은 거의 가능성이 없는, 다른 것을 해라.
그런데 왜 내 코드에 많은 추가/조인을 사용하는가?왜냐면 가끔은 그게 더 명확할 때도 있으니까특히 당신이 함께 연결해야 할 어떤 것이든 공간, 쉼표 또는 새로운 선으로 분리되어야 할 때 특히 그렇다.
만약 당신이 많은 값을 연결한다면, 둘 다 아니다.리스트를 첨부하는 것은 비싸다.문자열을 사용할 수 있음이를 위한 IO.특히 당신이 많은 수술로 그것을 쌓고 있다면 더욱 그렇다.
from cStringIO import StringIO
# python3: from io import StringIO
buf = StringIO()
buf.write('foo')
buf.write('foo')
buf.write('foo')
buf.getvalue()
# 'foofoofoo'
다른 한 리스트가 , 다다닥다닥을 사용하라.''.join(aList)
파이선 FAQ에서: 많은 문자열을 함께 연결하는 가장 효율적인 방법은 무엇인가?
str과 bytes 객체는 불변하므로, 많은 문자열을 함께 연결하면 새로운 객체가 생성되기 때문에 비효율적이다.일반적인 경우, 총 런타임 비용은 총 문자열 길이에서 2차적이다.
많은 str 객체를 축적하기 위해 권장되는 관용구는 목록으로 배치하고 마지막에 str.join()을 호출하는 것이다.
chunks = [] for s in my_strings: chunks.append(s) result = ''.join(chunks)
(이론적으로 상당히 효율적인 관용어는 io를 사용하는 것이다.스트링IO)
많은 바이트 객체를 누적하려면 내부 연결(+= 연산자)을 사용하여 bytearray 객체를 확장하는 것이 권장된다.
result = bytearray() for b in my_bytes_objects: result += b
편집: 나는 어리석었고 결과를 거꾸로 붙여 목록에 추가하는 것이 cString보다 더 빠른 것처럼 보이게 했다.IO. bytearray/str concat에 대한 테스트와 더 큰 문자열로 더 큰 목록을 사용한 2차 테스트도 추가했다.(제2.7.3절)
큰 문자열 목록에 대한 ipython 테스트 예제
try:
from cStringIO import StringIO
except:
from io import StringIO
source = ['foo']*1000
%%timeit buf = StringIO()
for i in source:
buf.write(i)
final = buf.getvalue()
# 1000 loops, best of 3: 1.27 ms per loop
%%timeit out = []
for i in source:
out.append(i)
final = ''.join(out)
# 1000 loops, best of 3: 9.89 ms per loop
%%timeit out = bytearray()
for i in source:
out += i
# 10000 loops, best of 3: 98.5 µs per loop
%%timeit out = ""
for i in source:
out += i
# 10000 loops, best of 3: 161 µs per loop
## Repeat the tests with a larger list, containing
## strings that are bigger than the small string caching
## done by the Python
source = ['foo']*1000
# cStringIO
# 10 loops, best of 3: 19.2 ms per loop
# list append and join
# 100 loops, best of 3: 144 ms per loop
# bytearray() +=
# 100 loops, best of 3: 3.8 ms per loop
# str() +=
# 100 loops, best of 3: 5.11 ms per loop
파이톤 >= 3.6에서 새로운 f 스트링은 줄을 연결시키는 효율적인 방법이다.
>>> name = 'some_name'
>>> number = 123
>>>
>>> f'Name is {name} and the number is {number}.'
'Name is some_name and the number is 123.'
'+'에 의한 제자리 문자열 결합을 사용하는 것은 모든 값을 지원하지 않기 때문에 안정성과 교차 구현 측면에서 WAST 방식의 결합이다.PEP8 표준은 이를 억제하고 장기 사용을 위해 형식(), 결합() 및 추가() 사용을 권장한다.
연결된 "프로그래밍 권장 사항" 섹션에서 인용한 대로:
예를 들어, += b 또는 a = a + b 형식의 문장에 대해 CPython의 효율적인 내부 문자열 연결 구현에 의존하지 마십시오.이러한 최적화는 CPython에서도 취약하며(일부 유형에만 해당), 리프카운팅을 사용하지 않는 구현에서는 전혀 존재하지 않는다.성능에 민감한 도서관 부분에서는 ''join() 양식을 대신 사용해야 한다.이것은 다양한 구현에 걸쳐 선형 시간에 결합이 이루어지도록 보장할 것이다.
이 함수를 쓰십시오.
def str_join(*args):
return ''.join(map(str, args))
그러면 원하는 곳이면 어디든 간단히 전화할 수 있다.
str_join('Pine') # Returns : Pine
str_join('Pine', 'apple') # Returns : Pineapple
str_join('Pine', 'apple', 3) # Returns : Pineapple3
권장되는 방법은 여전히 덧셈과 결합을 사용하는 것이다.
연결 중인 문자열이 리터럴이면 문자열 리터럴 연결 사용
re.compile(
"[A-Za-z_]" # letter or underscore
"[A-Za-z0-9_]*" # letter, digit or underscore
)
이는 문자열의 일부(위와 같이)에 주석을 달거나 문자열이 아닌 일부에 원시 문자열 또는 세 개의 따옴표를 사용할 경우 유용하다.
이것은 구문 계층에서 발생하기 때문에 제로 결합 연산자를 사용한다.
했듯이, 에서는 @jdi에 가서 Python을 할 한다.str.join
또는io.StringIO
끈 용 는 2차적 을 그리다에게 기대해야 .+=
파이톤 2.4 이후 최적화가 있었음에도 불구하고.이 대답에 따르면 다음과 같다.
하면 파이썬을 '파이썬'이라고
realloc
문자열 크기를 조정하여 사본을 피하려고 시도하십시오.이것은 실행 세부사항이기 때문에, 그리고 만약의 경우라면, 결코 의지해서는 안 된다.realloc
결국 끈을 자주 옮겨야 하고, 성능은 어쨌든 O(n^2)로 저하된다.
순진하게 의존했던 실제 코드의 예를 보여주겠다.+=
이 최적화, 그러나 적용되지 않았다.아래 코드는 수많은 짧은 문자열을 큰 청크로 변환하여 벌크 API에 사용한다.
def test_concat_chunk(seq, split_by):
result = ['']
for item in seq:
if len(result[-1]) + len(item) > split_by:
result.append('')
result[-1] += item
return result
이 코드는 2차 시간 복잡성 때문에 몇 시간 동안 문학을 할 수 있다.다음은 제안된 데이터 구조를 가진 대안이다.
import io
def test_stringio_chunk(seq, split_by):
def chunk():
buf = io.StringIO()
size = 0
for item in seq:
if size + len(item) <= split_by:
size += buf.write(item)
else:
yield buf.getvalue()
buf = io.StringIO()
size = buf.write(item)
if size:
yield buf.getvalue()
return list(chunk())
def test_join_chunk(seq, split_by):
def chunk():
buf = []
size = 0
for item in seq:
if size + len(item) <= split_by:
buf.append(item)
size += len(item)
else:
yield ''.join(buf)
buf.clear()
buf.append(item)
size = len(item)
if size:
yield ''.join(buf)
return list(chunk())
그리고 마이크로 벤치마크:
import timeit
import random
import string
import matplotlib.pyplot as plt
line = ''.join(random.choices(
string.ascii_uppercase + string.digits, k=512)) + '\n'
x = []
y_concat = []
y_stringio = []
y_join = []
n = 5
for i in range(1, 11):
x.append(i)
seq = [line] * (20 * 2 ** 20 // len(line))
chunk_size = i * 2 ** 20
y_concat.append(
timeit.timeit(lambda: test_concat_chunk(seq, chunk_size), number=n) / n)
y_stringio.append(
timeit.timeit(lambda: test_stringio_chunk(seq, chunk_size), number=n) / n)
y_join.append(
timeit.timeit(lambda: test_join_chunk(seq, chunk_size), number=n) / n)
plt.plot(x, y_concat)
plt.plot(x, y_stringio)
plt.plot(x, y_join)
plt.legend(['concat', 'stringio', 'join'], loc='upper left')
plt.show()
너는 다른 방법으로 할 수 있다.
str1 = "Hello"
str2 = "World"
str_list = ['Hello', 'World']
str_dict = {'str1': 'Hello', 'str2': 'World'}
# Concatenating With the + Operator
print(str1 + ' ' + str2) # Hello World
# String Formatting with the % Operator
print("%s %s" % (str1, str2)) # Hello World
# String Formatting with the { } Operators with str.format()
print("{}{}".format(str1, str2)) # Hello World
print("{0}{1}".format(str1, str2)) # Hello World
print("{str1} {str2}".format(str1=str_dict['str1'], str2=str_dict['str2'])) # Hello World
print("{str1} {str2}".format(**str_dict)) # Hello World
# Going From a List to a String in Python With .join()
print(' '.join(str_list)) # Hello World
# Python f'strings --> 3.6 onwards
print(f"{str1} {str2}") # Hello World
나는 다음과 같은 기사를 통해 이 작은 요약을 만들었다.
- Python 3의 f-Strings: 향상된 문자열 형식 지정 구문(Guide)(속도 테스트도 포함)
- 형식 문자열 리터럴
- 문자열 연결 및 형식 지정
- Python에서 문자열 분할, 연결 및 결합
다소 오래된 Code Like a Pythonista: Pythonic Pythonjoin()
에 걸쳐서+
이 절에서PythonSpeedPerformanceTips가 문자열 연결에 대한 섹션에서와 같이 다음과 같은 거부권을 가진다.
이 섹션의 정확성은 Python의 이후 버전과 관련하여 논쟁의 여지가 있다.CPython 2.5에서 문자열 연결은 다른 Python 구현과 마찬가지로 적용되지 않을 수 있지만 상당히 빠르다.연결 참조토론을 위한 테스트 코드.
나의 사용 사례는 약간 달랐다.나는 20개 이상의 필드가 역동적인 곳에 쿼리를 구성해야 했다.형식 방법을 사용하는 이 접근 방식을 따랐다.
query = "insert into {0}({1},{2},{3}) values({4}, {5}, {6})"
query.format('users','name','age','dna','suzan',1010,'nda')
이것은 +나 다른 방법을 사용하는 대신에 나에게 비교적 간단했다.
이것(더 효율적)도 이용할 수 있다. (https://softwareengineering.stackexchange.com/questions/304445/why-is-s-better-than-for-concatenation)
s += "%s" %(stringfromelsewhere)
'programing' 카테고리의 다른 글
Vue에서 상태 변경 시 라우터에 의해 렌더링된 구성 요소를 변경하는 방법 (0) | 2022.03.30 |
---|---|
python 3.x에서 string.replace()를 사용하는 방법 (0) | 2022.03.28 |
모듈에서 동작을 반환하지 않는 지도작업 (0) | 2022.03.28 |
React useEffect에 종속성 배열의 스프레드 요소가 있음 (0) | 2022.03.28 |
vue-cli 프로젝트에서 포트 번호를 변경하는 방법 (0) | 2022.03.28 |