프로그래밍 언어/Python

파이썬에서 한 문자열을 다른 문자열에 추가하는 방법

Rateye 2021. 6. 9. 10:39
728x90
반응형
질문 : 파이썬에서 한 문자열을 다른 문자열에 어떻게 추가합니까?

다음과는 달리 Python에서 한 문자열을 다른 문자열에 추가하는 효율적인 방법을 원합니다.

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

사용할 좋은 기본 제공 방법이 있습니까?

답변

문자열에 대한 참조가 하나만 있고 다른 문자열을 끝에 연결하는 경우 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=''" "for i in xrange(10):s+='a'"
1000000 loops, best of 3: 1.85 usec per loop
$ python -m timeit -s"s=''" "for i in xrange(100):s+='a'"
10000 loops, best of 3: 16.8 usec per loop
$ python -m timeit -s"s=''" "for i in xrange(1000):s+='a'"
10000 loops, best of 3: 158 usec per loop
$ python -m timeit -s"s=''" "for i in xrange(10000):s+='a'"
1000 loops, best of 3: 1.71 msec per loop
$ python -m timeit -s"s=''" "for i in xrange(100000):s+='a'"
10 loops, best of 3: 14.6 msec per loop
$ python -m timeit -s"s=''" "for i in xrange(1000000):s+='a'"
10 loops, best of 3: 173 msec per loop

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

$ pypy -m timeit -s"s=''" "for i in xrange(10):s+='a'"
10000 loops, best of 3: 90.8 usec per loop
$ pypy -m timeit -s"s=''" "for i in xrange(100):s+='a'"
1000 loops, best of 3: 896 usec per loop
$ pypy -m timeit -s"s=''" "for i in xrange(1000):s+='a'"
100 loops, best of 3: 9.03 msec per loop
$ pypy -m timeit -s"s=''" "for i in xrange(10000):s+='a'"
10 loops, best of 3: 89.5 msec per loop

지금까지는 훌륭했지만

$ pypy -m timeit -s"s=''" "for i in xrange(100000):s+='a'"
10 loops, best of 3: 12.8 sec per loop

아야 2 차보다 더 나빠. 따라서 pypy는 짧은 문자열에서는 잘 작동하지만 큰 문자열에서는 제대로 수행되지 않습니다.

출처 : https://stackoverflow.com/questions/4435169/how-do-i-append-one-string-to-another-in-python
728x90
반응형