मैं पायथन में एक स्ट्रिंग को दूसरे से कैसे जोड़ सकता हूं?


593

मैं पायथन में एक स्ट्रिंग को दूसरे से जोड़ने के लिए एक कुशल तरीका चाहता हूं, निम्नलिखित के अलावा।

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

उपयोग करने के लिए कोई अच्छी अंतर्निहित विधि है?


8
TL; DR: यदि आप स्ट्रिंग्स को जोड़ने का आसान तरीका खोज रहे हैं, और आप दक्षता की परवाह नहीं करते हैं:"foo" + "bar" + str(3)
एंड्रयू

जवाबों:


609

यदि आपके पास केवल एक स्ट्रिंग का संदर्भ है और आप अंत तक किसी अन्य स्ट्रिंग को संक्षिप्त करते हैं, तो CPython अब इस पर विशेष केस करता है और स्ट्रिंग को जगह में विस्तारित करने का प्रयास करता है।

अंतिम परिणाम यह है कि ऑपरेशन को परिशोधित ओ (एन) है।

जैसे

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 = '" "i for xrange (10): s + =' a '"
1000000 लूप्स, सर्वश्रेष्ठ 3: 1.85 usec प्रति लूप
$ python -m timeit -s "s = '" "i for xrange (100): s + =' a '"
10000 लूप, सर्वश्रेष्ठ 3: 16.8 usec प्रति लूप
$ python -m timeit -s "s = '" "i for xrange (1000): s + =' a '"
10000 लूप, 3 का सर्वश्रेष्ठ: 158 प्रति लूप
$ python -m timeit -s "s = '" "i for xrange (10000): s + =' a '"
1000 लूप, सर्वश्रेष्ठ 3: 1.71 मिसे प्रति लूप
$ python -m timeit -s "s = '" "i for xrange (100000): s + =' a '"
10 लूप, सर्वश्रेष्ठ 3: 14.6 मिसेक प्रति लूप
$ python -m timeit -s "s = '" "i for xrange (1000000): s + =' a '"
10 लूप, सर्वश्रेष्ठ 3: 173 मिसेक प्रति लूप

हालांकि यह ध्यान रखना महत्वपूर्ण है कि यह अनुकूलन पायथन युक्ति का हिस्सा नहीं है। यह केवल cPython कार्यान्वयन में है जहाँ तक मुझे पता है। उदाहरण के लिए pypy या jython पर समान अनुभवजन्य परीक्षण पुराने O (n ** 2) प्रदर्शन दिखा सकता है।

$ pypy -m timeit -s "s = '" "i for xrange (10): s + = a'" "
10000 लूप, सर्वश्रेष्ठ 3: 90.8 usec प्रति लूप
$ pypy -m timeit -s "s = '" "i for xrange (100): s + = a'" "
1000 लूप, 3 का सर्वश्रेष्ठ: 896 प्रति लूप usec
$ pypy -m timeit -s "s = '" "i for xrange (1000): s + = a'" "
100 लूप, सर्वश्रेष्ठ 3: 9.03 मिसे प्रति लूप
$ pypy -m timeit -s "s = '" "i for xrange (10000): s + =' a" "
10 लूप, सर्वश्रेष्ठ 3: 89.5 मिसेक प्रति लूप

अब तक तो अच्छा है, लेकिन फिर,

$ pypy -m timeit -s "s = '" "i for xrange (100000): s + =' a '"
10 लूप, सर्वश्रेष्ठ 3: 12.8 प्रति लूप सेकेंड

चतुष्कोणीय से भी बदतर ouch। तो pypy कुछ ऐसा कर रहा है जो छोटे तार के साथ अच्छा काम करता है, लेकिन बड़े तार के लिए खराब प्रदर्शन करता है।


14
दिलचस्प। "अब" से, क्या आपका मतलब 3. पायथन से है?
स्टीव तूज़ा

10
@Steve, नहीं। यह कम से कम 2.6 में शायद 2.5 भी है
जॉन ला रोय डे

8
आपने PyString_ConcatAndDelफ़ंक्शन को उद्धृत किया है, लेकिन इसके लिए टिप्पणी शामिल की है _PyString_Resize। इसके अलावा, टिप्पणी वास्तव में बिग-ओ
विंस्टन एवर्ट

3
एक CPython सुविधा का शोषण करने पर बधाई जो अन्य कार्यान्वयन पर कोड को क्रॉल कर देगा। बुरी सलाह।
जीन फ़्राँस्वा Fabre

4
इसका उपयोग न करें। Pep8 में स्पष्ट रूप से कहा गया है: कोड को इस तरह से लिखा जाना चाहिए कि पायथन (PyPy, Jython, IronPython, Cython, Psyco और अन्य) के अन्य कार्यान्वयन को नुकसान न पहुंचे, और फिर यह इस विशिष्ट उदाहरण के रूप में यह बहुत नाजुक है क्योंकि इससे बचने के लिए कुछ बेहतर है।"".join(str_a, str_b)
इरा

287

समय से पहले अनुकूलन न करें। यदि आपके पास यह मानने का कोई कारण नहीं है कि स्ट्रिंग के संघनन के कारण गति की अड़चन है, तो बस साथ रहें +और +=:

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

कहा कि, यदि आप जावा के स्ट्रिंगब्यूलर जैसी किसी चीज के लिए लक्ष्य कर रहे हैं, तो विहित पायथन मुहावरे में किसी सूची में आइटम जोड़ना है और फिर str.joinअंत में उन सभी का उपयोग करना है:

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

s = ''.join(l)

मुझे नहीं पता कि आपके तार को सूचियों के रूप में बनाने की गति के निहितार्थ क्या हैं और फिर .join () उन्हें हैं, लेकिन मुझे लगता है कि यह आम तौर पर सबसे साफ तरीका है। मैंने एसक्यूएल टेम्प्लेटिंग इंजन के लिए एक स्ट्रिंग के भीतर% s संकेतन का उपयोग करने के साथ बड़ी सफलताएं भी लिखी हैं।
रिचो

25
@Richo का उपयोग करना .join अधिक कुशल है। इसका कारण यह है कि पायथन स्ट्रिंग्स अपरिवर्तनीय हैं, इसलिए बार-बार s + = का उपयोग करके क्रमिक रूप से बड़े स्ट्रिंग्स को आवंटित किया जाएगा। .join अपने घटक भागों से एक बार में अंतिम तार उत्पन्न करेगा।
बेन

5
@ फिर, इस क्षेत्र में एक महत्वपूर्ण सुधार हुआ है - मेरा जवाब देखें
जॉन ला रोय डे

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

यह विभाजक के रूप में str1 और str2 को एक स्थान से जोड़ता है। आप यह भी कर सकते हैं "".join(str1, str2, ...)str.join()एक पुनरावृत्ति लेता है, इसलिए आपको स्ट्रिंग्स को एक सूची या टुपल में रखना होगा।

यह एक निर्माण विधि के लिए जितना कुशल है उतना ही इसके बारे में है।


क्या होता है, अगर str1 empy है? क्या व्हॉट्सएप सेट हो जाएगा?
जुर्गन के।

38

मत करो।

यही है, ज्यादातर मामलों के लिए आप एक बार में पूरे स्ट्रिंग को उत्पन्न करने से बेहतर होते हैं, फिर मौजूदा स्ट्रिंग के लिए संलग्न होते हैं।

उदाहरण के लिए, ऐसा न करें: obj1.name + ":" + str(obj1.count)

इसके बजाय: उपयोग करें "%s:%d" % (obj1.name, obj1.count)

यह पढ़ने में आसान और अधिक कुशल होगा।


54
मुझे खेद है कि पहले उदाहरण की तरह (स्ट्रिंग + स्ट्रिंग) पढ़ने में ज्यादा आसान नहीं है, दूसरा उदाहरण अधिक कुशल हो सकता है, लेकिन अधिक पठनीय नहीं है
JqueryToAddNumbers

23
@ExceptionSlayer, string + string का पालन करना बहुत आसान है। लेकिन "<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())
विंस्टन एवर्ट

जब मैं करने की कोशिश कर रहा हूं तो यह बिल्कुल भी मदद नहीं करता है, जैसे PHP / पर्ल के "स्ट्रिंग = = वेरडेटा ()" या इसके समान है।
शादुर

@ बहादुर, मेरी बात यह है कि आपको फिर से सोचना चाहिए, क्या आप वास्तव में कुछ समकक्ष करना चाहते हैं, या एक पूरी तरह से अलग दृष्टिकोण बेहतर है?
विंस्टन एवर्ट

1
और इस मामले में उस प्रश्न का उत्तर है "नहीं, क्योंकि यह दृष्टिकोण मेरे उपयोग के मामले को कवर नहीं करता है"
शादुर

11

अजगर 3.6 हमें एफ-स्ट्रिंग्स देता है , जो एक खुशी है:

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: 7.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: 7.41 प्रति लूप

अजगर 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


2
a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'

1
कोड अच्छा है, लेकिन यह एक साथ स्पष्टीकरण देने में मदद करेगा। इस पृष्ठ पर अन्य उत्तरों के बजाय इस पद्धति का उपयोग क्यों करें?
cgmb

11
उपयोग करना a.__add__(b)लेखन के समान है a+b। जब आप +ऑपरेटर का उपयोग करते हुए तारों को समतल करते हैं , तो पायथन __add__एक पैरामीटर के रूप में दाईं ओर स्ट्रिंग को पार करते हुए बाईं ओर स्ट्रिंग पर विधि को कॉल करेगा ।
Addie
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.