파이썬에서 한 문자열을 다른 문자열에 어떻게 추가합니까?


593

파이썬에서 다음 문자열 이외의 문자열을 다른 문자열에 추가하는 효율적인 방법을 원합니다.

var1 = "foo"
var2 = "bar"
var3 = var1 + var2

사용하기 좋은 내장 방법이 있습니까?


8
TL; DR : 문자열을 추가하는 간단한 방법 만 찾고 효율성에 관심이없는 경우 :"foo" + "bar" + str(3)
Andrew

답변:


609

문자열에 대한 참조가 하나만 있고 다른 문자열을 끝에 연결하는 경우 CPython은 이제 특수한 경우에 문자열을 제자리로 확장하려고 시도합니다.

결과적으로 연산은 상각 O (n)입니다.

예 :

s = ""
for i in range(n):
    s+=str(i)

예전에는 O (n ^ 2) 였지만 이제는 O (n)입니다.

소스 (bytesobject.c)에서 :

void
PyBytes_ConcatAndDel(register PyObject **pv, register PyObject *w)
{
    PyBytes_Concat(pv, w);
    Py_XDECREF(w);
}


/* The following function breaks the notion that strings are immutable:
   it changes the size of a string.  We get away with this only if there
   is only one module referencing the object.  You can also think of it
   as creating a new string object and destroying the old one, only
   more efficiently.  In any case, don't use this if the string may
   already be known to some other part of the code...
   Note that if there's not enough memory to resize the string, the original
   string object at *pv is deallocated, *pv is set to NULL, an "out of
   memory" exception is set, and -1 is returned.  Else (on success) 0 is
   returned, and the value in *pv may or may not be the same as on input.
   As always, an extra byte is allocated for a trailing \0 byte (newsize
   does *not* include that), and a trailing \0 byte is stored.
*/

int
_PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
{
    register PyObject *v;
    register PyBytesObject *sv;
    v = *pv;
    if (!PyBytes_Check(v) || Py_REFCNT(v) != 1 || newsize < 0) {
        *pv = 0;
        Py_DECREF(v);
        PyErr_BadInternalCall();
        return -1;
    }
    /* XXX UNREF/NEWREF interface should be more symmetrical */
    _Py_DEC_REFTOTAL;
    _Py_ForgetReference(v);
    *pv = (PyObject *)
        PyObject_REALLOC((char *)v, PyBytesObject_SIZE + newsize);
    if (*pv == NULL) {
        PyObject_Del(v);
        PyErr_NoMemory();
        return -1;
    }
    _Py_NewReference(*pv);
    sv = (PyBytesObject *) *pv;
    Py_SIZE(sv) = newsize;
    sv->ob_sval[newsize] = '\0';
    sv->ob_shash = -1;          /* invalidate cached hash value */
    return 0;
}

경험적으로 검증하기가 쉽습니다.

$ python -m timeit -s "s = ''" "xrange (10)의 i의 경우 : s + = 'a'"
1000000 루프, 최고 3 : 1.85 루프 당 루프
$ python -m timeit -s "s = ''" "xrange (100)의 i의 경우 : s + = 'a'"
루프 당 10000 개의 루프 중 3 : 16.8 usec
$ python -m timeit -s "s = ''" "xrange (1000)에서 i의 경우 : s + = 'a'"
루프 당 10000 개의 루프, 최고 3 : 158 usec
$ python -m timeit -s "s = ''" "xrange (10000)의 i의 경우 : s + = 'a'"
1000 루프, 루프 당 3 : 1.71 msec 최고
$ python -m timeit -s "s = ''" "xrange (100000)의 i의 경우 : s + = 'a'"
루프 당 3 개의 루프 : 14.6msec
$ python -m timeit -s "s = ''" "xrange (1000000) : s + = 'a'"의 i
10 개 루프, 루프 당 3 : 173msec 최고

그것은 중요 이 최적화 파이썬 사양의 일부가 아니라는 것을 참고하지만. 내가 아는 한 cPython 구현에만 있습니다. 예를 들어 pypy 또는 jython에 대한 동일한 경험적 테스트는 이전 O (n ** 2) 성능을 보여줄 수 있습니다.

$ pypy -m timeit -s "s = ''" "xrange (10)의 i의 경우 : s + = 'a'"
루프 당 10000 개의 루프, 최고 3 : 90.8 usec
$ pypy -m timeit -s "s = ''" "xrange (100)의 i의 경우 : s + = 'a'"
루프 당 1000 루프, 3 : 3 : 896 usec 이상
$ pypy -m timeit -s "s = ''" "xrange (1000)의 i의 경우 : s + = 'a'"
루프 당 100 개, 3 개 최고 : 9.03msec
$ pypy -m timeit -s "s = ''" "xrange (10000)의 i의 경우 : s + = 'a'"
10 개 루프, 3 개 중 최고 : 89.5msec (루프 당)

지금까지는 좋지만

$ pypy -m timeit -s "s = ''" "xrange (100000)의 i의 경우 : s + = 'a'"
루프 당 10 개, 최고 3 : 12.8 초

이차보다 더 나쁘다. 따라서 pypy는 짧은 문자열에서는 잘 작동하지만 더 큰 문자열에서는 성능이 좋지 않습니다.


14
흥미 롭군 "지금"은 파이썬 3.x를 의미합니까?
Steve Tjoa

10
@Steve, 아니오. 적어도 2.6 일 수도 있고 2.5 일 수도 있습니다
John La Rooy

8
PyString_ConcatAndDel함수를 인용 했지만에 대한 주석을 포함했습니다 _PyString_Resize. 또한,이 의견은 Big-O
Winston Ewert의 31:12의

3
다른 구현에서 코드를 크롤링하는 CPython 기능을 사용한 것을 축하합니다. 나쁜 조언.
Jean-François Fabre

4
이것을 사용하지 마십시오. Pep8은 다음과 같이 명시 적으로 설명합니다. 코드는 다른 Python 구현 (PyPy, Jython, IronPython, Cython, Psyco 등)에 불리하지 않은 방식으로 작성되어야합니다 ."".join(str_a, str_b)
Eraw

287

조기 최적화하지 마십시오. 문자열 연결로 인한 속도 병목 현상이 있다고 생각할 이유가 없다면 +and 만 사용하십시오 +=.

s  = 'foo'
s += 'bar'
s += 'baz'

즉, Java의 StringBuilder와 같은 것을 목표로하는 경우 표준 파이썬 관용구는 항목을 목록에 추가 한 다음 str.join끝에 모두 연결하는 것입니다.

l = []
l.append('foo')
l.append('bar')
l.append('baz')

s = ''.join(l)

문자열을 목록으로 작성하고 .join ()하는 속도가 어떤 영향을 미치는지 모르겠지만 일반적으로 가장 깨끗한 방법입니다. 필자가 작성한 SQL 템플릿 엔진에 대해 문자열 내에서 % s 표기법을 사용하여 큰 성공을 거두었습니다.
richo

25
@ Richo.join을 사용하는 것이 더 효율적입니다. 그 이유는 파이썬 문자열은 변경할 수 없기 때문에 s + = more를 반복적으로 사용하면 연속적으로 더 큰 문자열이 많이 할당됩니다. .join은 구성 부분에서 최종 문자열을 한 번에 생성합니다.
Ben

5
@Ben,이 분야에서 크게 개선되었습니다-내 답변보기
John La Rooy

41
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))

이는 str1과 str2를 공백으로 분리 자로 분리합니다. 당신은 또한 할 수 있습니다 "".join(str1, str2, ...). str.join()반복 가능하므로 문자열을 목록이나 튜플에 넣어야합니다.

내장 메소드보다 효율적입니다.


str1이 empy이면 어떻게됩니까? 공백이 설정됩니까?
Jürgen K.

38

하지마

즉, 대부분의 경우 기존 문자열에 추가하는 대신 전체 문자열을 한 번에 생성하는 것이 좋습니다.

예를 들어,하지 마십시오 : obj1.name + ":" + str(obj1.count)

대신 : 사용 "%s:%d" % (obj1.name, obj1.count)

보다 읽기 쉽고 효율적입니다.


54
첫 번째 예제와 같이 (string + string)보다 읽기 쉬운 것이 없기 때문에 죄송합니다. 두 번째 예제는 더 효율적이지만 읽기
쉽지

23
@ExceptionSlayer, 문자열 + 문자열은 따르기가 매우 쉽습니다. 그러나 "<div class='" + className + "' id='" + generateUniqueId() + "'>" + message_text + "</div>", 나는 읽기 쉽고 오류가 덜 발생합니다."<div class='{classname}' id='{id}'>{message_text}</div>".format(classname=class_name, message_text=message_text, id=generateUniqueId())
Winston Ewert

PHP / perl의 "string. = verifydata ()"또는 이와 비슷한 것과 비슷한 경우에는 도움이되지 않습니다.
Shadur

@Shadur, 내 요점은 다시 생각해야한다는 것입니다. 실제로 동등한 것을하고 싶습니까, 아니면 완전히 다른 접근법이 더 낫습니까?
Winston Ewert

1
그리고이 경우 그 질문에 대한 대답은 "아니요. 그 접근 방식이 나의 사용 사례를 다루지 않기 때문입니다"
Shadur

11

파이썬 3.6은 f-strings을 제공합니다 .

var1 = "foo"
var2 = "bar"
var3 = f"{var1}{var2}"
print(var3)                       # prints foobar

중괄호 안에서 무엇이든 할 수 있습니다

print(f"1 + 1 == {1 + 1}")        # prints 1 + 1 == 2

10

큰 문자열을 작성하기 위해 많은 추가 작업을 수행해야하는 경우 StringIO 또는 cStringIO를 사용할 수 있습니다 . 인터페이스는 파일과 같습니다. 즉 : write텍스트를 추가해야합니다.

두 개의 문자열을 추가하는 경우을 사용하십시오 +.


9

실제로 응용 프로그램에 따라 다릅니다. 수백 개의 단어를 반복하면서 모든 단어를 목록에 추가하려면 .join()더 좋습니다. 그러나 긴 문장을 작성하는 경우을 사용하는 것이 좋습니다 +=.


5

기본적으로 차이는 없습니다. 유일한 일관된 추세는 파이썬이 모든 버전에서 느려지고있는 것 같습니다 ... :(


명부

%%timeit
x = []
for i in range(100000000):  # xrange on Python 2.7
    x.append('a')
x = ''.join(x)

파이썬 2.7

1 루프, 루프 당 3:34

파이썬 3.4

1 개 루프, 3 최고 : 7.99 루프 당의

파이썬 3.5

1 루프, 루프 당 최고 3 : 8.48

파이썬 3.6

1 루프, 루프 당 3 : 9.93


%%timeit
x = ''
for i in range(100000000):  # xrange on Python 2.7
    x += 'a'

파이썬 2.7 :

1 루프, 루프 당 최고 3 : 3.1 초

파이썬 3.4

1 루프, 루프 당 최고 3 : 9.08

파이썬 3.5

1 개 루프, 3 최고 : 8.82 루프 당의

파이썬 3.6

1 개 루프, 3 최고 : 9.24 루프 당의


2
나는 그것이 달려 있다고 생각합니다. 내가 도착 1.19 s하고 992 msPython2.7에 각각
존 라 Rooy

4

__add__ 함수로 문자열 추가

str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)

산출

Hello World

4
str + str2여전히 짧습니다.
Nik O'Lai

2
a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'

1
코드는 훌륭하지만 함께 설명하는 것이 도움이 될 것입니다. 이 페이지의 다른 답변보다이 방법을 사용하는 이유는 무엇입니까?
cgmb

11
사용 a.__add__(b)은 쓰기와 동일합니다 a+b. +연산자를 사용하여 문자열을 연결하면 Python은 __add__왼쪽 문자열에서 매개 변수로 오른쪽 문자열을 전달하는 메서드를 호출합니다 .
Addie
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.