पायथन में एक स्ट्रिंग को घुमाने का पसंदीदा तरीका कौन सा है?


358

चूंकि पायथन को stringबदला नहीं जा सकता है, मैं सोच रहा था कि कैसे एक स्ट्रिंग को और अधिक कुशलता से बदलना है?

मैं इसे लिख सकता हूं:

s += stringfromelsewhere

या इस तरह:

s = []
s.append(somestring)

later

s = ''.join(s)

इस प्रश्न को लिखते समय, मुझे इस विषय पर बात करने वाला एक अच्छा लेख मिला।

http://www.skymind.com/~ocrow/python_string/

लेकिन यह पायथन 2.x में है, इसलिए यह प्रश्न पायथन 3 में कुछ परिवर्तन होगा?


जवाबों:


433

स्ट्रिंग स्ट्रिंग के लिए स्ट्रिंग को जोड़ने का सबसे अच्छा तरीका उपयोग +या है +=। ऐसा इसलिए है क्योंकि यह पठनीय और तेज है। वे भी उतनी ही तेजी से हैं, जो आप चुनते हैं वह स्वाद का मामला है, बाद वाला सबसे आम है। यहाँ timeitमॉड्यूल के साथ समय हैं :

a = a + b:
0.11338996887207031
a += b:
0.11040496826171875

हालांकि, जो लोग सूचियों को रखने और उनके लिए अपील करने और फिर उन सूचियों में शामिल होने की सलाह देते हैं, वे ऐसा इसलिए करते हैं क्योंकि किसी स्ट्रिंग को सूची में शामिल करना संभवतः एक स्ट्रिंग का विस्तार करने की तुलना में बहुत तेज़ है। और यह सच हो सकता है, कुछ मामलों में। यहाँ, उदाहरण के लिए, एक वर्ण स्ट्रिंग के एक मिलियन एपेंड हैं, पहले एक स्ट्रिंग के लिए, फिर एक सूची में:

a += b:
0.10780501365661621
a.append(b):
0.1123361587524414

ठीक है, पता चला है कि जब भी परिणामस्वरूप स्ट्रिंग एक लाख वर्ण लंबा होता है, तब भी संलग्न करना तेज था।

अब एक हज़ार वर्ण लंबे स्ट्रिंग को सौ हज़ार बार जोड़कर देखते हैं:

a += b:
0.41823482513427734
a.append(b):
0.010656118392944336

इसलिए, अंत स्ट्रिंग, लगभग 100 एमबी लंबी हो जाती है। यह बहुत धीमा था, एक सूची में जोड़ना बहुत तेज था। उस समय में अंतिम शामिल नहीं है a.join()। तो कितना समय लगेगा?

a.join(a):
0.43739795684814453

Oups। इस मामले में भी मुड़ता है, परिशिष्ट / शामिल धीमी है।

तो यह सिफारिश कहां से आती है? अजगर 2?

a += b:
0.165287017822
a.append(b):
0.0132720470428
a.join(a):
0.114929914474

ठीक है, संलग्न / join है मामूली तेजी वहाँ अगर आप बहुत देर तक तार का उपयोग कर रहे हैं (जो आप आमतौर पर नहीं कर रहे हैं, क्या आप एक स्ट्रिंग स्मृति में 100 एमबी है कि होता है?)

लेकिन असली क्लिनिक पायथन 2.3 है। जहां मैं आपको समय भी नहीं दिखाऊंगा, क्योंकि यह इतना धीमा है कि यह अभी तक समाप्त नहीं हुआ है। इन परीक्षणों में अचानक मिनट लगते हैं । सिवाय एपेंड / ज्वाइन के, जो बाद के पायथन के तहत उतनी ही तेज है।

हाँ। पाषाण काल ​​में पाषाण युग में स्ट्रिंग संघन बहुत धीमा था। लेकिन 2.4 पर यह अब (या कम से कम अजगर 2.4.7) नहीं है, इसलिए ऐपेंड / जॉइन का उपयोग करने की सिफारिश 2008 में पुरानी हो गई, जब पायथन 2.3 को अपडेट होना बंद हो गया, और आपको इसका उपयोग करना बंद कर देना चाहिए था। :-)

(अपडेट: जब मैंने परीक्षण को अधिक सावधानी से किया, तो उपयोग करना बंद कर देता है +और +=पायथन 2.3 पर दो तारों के लिए तेजी से होता है। उपयोग करने की सिफारिश ''.join()गलतफहमी होनी चाहिए । "

हालाँकि, यह CPython है। अन्य कार्यान्वयन में अन्य चिंताएँ हो सकती हैं। और यह सिर्फ एक और कारण है कि समय से पहले अनुकूलन सभी बुराई की जड़ है। जब तक आप पहले इसे माप नहीं लेते हैं, तब तक "तेज़" तकनीक का उपयोग न करें।

इसलिए स्ट्रिंग कॉन्फैनेटेशन करने के लिए "सबसे अच्छा" संस्करण + या + = का उपयोग करना है । और अगर यह आपके लिए धीमा हो जाता है, जो बहुत कम संभावना है, तो कुछ और करें।

तो मैं अपने कोड में बहुत सारे ऐपेंडेंट / जॉइन का उपयोग क्यों करूं? क्योंकि कभी-कभी यह वास्तव में स्पष्ट होता है। विशेष रूप से जब आपको एक साथ समतल करना चाहिए, तो रिक्त स्थान या अल्पविराम या newlines द्वारा अलग किया जाना चाहिए।


10
यदि आपके पास कई तार हैं (n> 10) "" .join (list_of_strings) अभी भी तेज है
मिको ओकटामा

11
क्यों + = तेज है, यह है कि अगर ccththon में कोई प्रदर्शन हैक होता है, यदि रेफक 1 होता है - यह बहुत अधिक अन्य सभी अजगर कार्यान्वयनों पर पड़ता है (बल्कि विशेष रूप से कॉन्फ़िगर किए गए pypy बिल्ड के अपवाद के साथ)
Ronny

17
इसे इतना उखाड़ा क्यों जा रहा है? एक एल्गोरिथ्म का उपयोग करना बेहतर कैसे होता है जो केवल एक विशिष्ट कार्यान्वयन पर कुशल होता है और एक द्विघात समय एल्गोरिथ्म को ठीक करने के लिए एक नाजुक हैक के लिए अनिवार्य रूप से क्या होता है? इसके अलावा आप पूरी तरह से गलत समझते हैं "समय से पहले अनुकूलन सभी बुराई की जड़ है"। वह उद्धरण SMALL अनुकूलन के बारे में बात कर रहा है। यह O (n ^ 2) से O (n) तक जा रहा है जो कि एक छोटा अनुकूलन नहीं है।
वेस

12
यहां वास्तविक उद्धरण है: "हमें छोटी क्षमताओं के बारे में भूलना चाहिए, समय के 97% के बारे में कहना चाहिए: समय से पहले अनुकूलन सभी बुराई की जड़ है। फिर भी हमें उस महत्वपूर्ण 3% में अपने अवसरों को पारित नहीं करना चाहिए। एक अच्छा प्रोग्रामर नहीं होगा। इस तरह के तर्क से शालीनता से लोटपोट हो जाओ, वह महत्वपूर्ण कोड को ध्यान से देखने में समझदार होगा, लेकिन उसके बाद ही उस कोड की पहचान की गई है "
वेस

2
कोई भी यह नहीं कह रहा है कि a + b धीमा है। यह द्विघात है जब आप = a + b को एक से अधिक बार कर रहे हैं। एक + बी + सी धीमी गति से नहीं है, मैं फिर कहता हूँ नहीं धीमी है, जबकि यह पिछले तार (एक = ए + बी दृष्टिकोण के साथ कई बार फिर से पार करने के लिए यह सोचते हैं कि एक पाश में है, क्योंकि यह केवल एक बार प्रत्येक स्ट्रिंग पार करने के लिए है किसी तरह की)। याद रखें तार अपरिवर्तनीय हैं।
वेस

52

यदि आप बहुत सारे मूल्यों को समेट रहे हैं, तो न तो। एक सूची लागू करना महंगा है। आप उसके लिए स्ट्रिंगो का उपयोग कर सकते हैं। खासकर अगर आप इसे बहुत सारे ऑपरेशंस पर बना रहे हैं।

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)

अजगर से अकसर किये गए सवाल: एक साथ कई तारों को समतल करने का सबसे कुशल तरीका क्या है?

str और बाइट्स ऑब्जेक्ट अपरिवर्तनीय हैं, इसलिए कई स्ट्रिंग्स को एक साथ जोड़ना अकुशल है क्योंकि प्रत्येक कॉन्फैक्शन एक नई ऑब्जेक्ट बनाता है। सामान्य स्थिति में, कुल स्ट्रिंग लंबाई में कुल रनटाइम लागत द्विघात है।

कई स्ट्रिंग ऑब्जेक्ट्स को संचित करने के लिए, अनुशंसित मुहावरे को एक सूची में रखना और str.join () को अंत में कॉल करना है:

chunks = []
for s in my_strings:
    chunks.append(s)
result = ''.join(chunks)

(io.StringIO का उपयोग करने के लिए एक और उचित रूप से कुशल मुहावरा है)

कई बाइट्स ऑब्जेक्ट्स को संचित करने के लिए, अनुशंसित मुहावरा एक जगह में समवर्ती (+ = ऑपरेटर) का उपयोग करके एक बीटयर्रे ऑब्जेक्ट का विस्तार करना है:

result = bytearray()
for b in my_bytes_objects:
    result += b

संपादित करें: मैं मूर्खतापूर्ण था और परिणाम पीछे की ओर चिपकाए गए थे, जिससे यह प्रतीत होता है कि सूची में जोड़ना cStringIO की तुलना में तेज़ था। मैंने बायटियर / स्ट्रेट कॉनैट के लिए परीक्षण भी जोड़ा है, साथ ही बड़े स्ट्रिंग्स के साथ एक बड़ी सूची का उपयोग करके परीक्षणों का दूसरा दौर भी। (अजगर 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

2
cStringIOPy3 में मौजूद नहीं है। io.StringIOइसके बजाय उपयोग करें ।
पीवीसी

2
के रूप में क्यों एक स्ट्रिंग के लिए बार-बार महंगा महंगा हो सकता है: joelonsoftware.com/articles/fog0000000319.html
वेस

36

पायथन> = 3.6 में, नया एफ-स्ट्रिंग एक स्ट्रिंग को समतल करने का एक कुशल तरीका है।

>>> name = 'some_name'
>>> number = 123
>>>
>>> f'Name is {name} and the number is {number}.'
'Name is some_name and the number is 123.'

8

सिफारिश की विधि अभी भी परिशिष्ट का उपयोग करें और शामिल होने के लिए है।


1
जैसा कि आप मेरे उत्तर से देखते हैं, यह इस बात पर निर्भर करता है कि आप कितने तार हैं। मैंने इस पर कुछ समय दिया है (मेरे उत्तर पर मेरी टिप्पणियों में जुड़ी हुई बात देखें) और आम तौर पर जब तक कि यह दस से अधिक न हो, + का उपयोग करें।
लेन्नर्ट रेगेब्र

1
PEP8 में इसका उल्लेख है ( python.org/dev/peps/pep-0008/#programming-recommendations )। तर्कसंगत यह है कि जबकि CPython के पास + = के साथ स्ट्रिंग संयोजन के लिए विशेष अनुकूलन है, अन्य कार्यान्वयन नहीं हो सकते हैं।
क्वांटम

8

यदि आपके द्वारा समेटे जाने वाले तार शाब्दिक हैं, तो स्ट्रिंग शाब्दिक संयोजन का उपयोग करें

re.compile(
        "[A-Za-z_]"       # letter or underscore
        "[A-Za-z0-9_]*"   # letter, digit or underscore
    )

यह उपयोगी है यदि आप एक स्ट्रिंग के हिस्से (जैसा कि ऊपर) पर टिप्पणी करना चाहते हैं या यदि आप एक शाब्दिक के हिस्से के लिए कच्चे तार या ट्रिपल उद्धरण का उपयोग करना चाहते हैं, लेकिन सभी नहीं।

चूंकि यह सिंटैक्स परत पर होता है इसलिए यह शून्य संघनन ऑपरेटरों का उपयोग करता है।


7

आप इस फ़ंक्शन को लिखें

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

1
str_join = lambda *str_list: ''.join(s for s in str_list)
रिक

7

'+' के द्वारा जगह स्ट्रिंग संयोजन में उपयोग करना स्थिरता और क्रॉस कार्यान्वयन के संदर्भ में सहमति का सबसे अच्छा तरीका है क्योंकि यह सभी मूल्यों का समर्थन नहीं करता है। PEP8 मानक इसे हतोत्साहित करता है और दीर्घकालिक उपयोग के लिए प्रारूप (), ज्वाइन () और परिशिष्ट () के उपयोग को प्रोत्साहित करता है।

लिंक "प्रोग्रामिंग अनुशंसाएँ" खंड से उद्धृत:

उदाहरण के लिए, एक + = b या a = a + b के रूप में बयान के लिए इन-प्लेस स्ट्रिंग कॉन्फ़ेक्शन के CPython के कुशल कार्यान्वयन पर भरोसा न करें। यह अनुकूलन CPython में भी नाजुक है (यह केवल कुछ प्रकारों के लिए काम करता है) और कार्यान्वयन में बिल्कुल भी मौजूद नहीं है जो कि रीफ़ोकाउंटिंग का उपयोग नहीं करते हैं। लाइब्रेरी के प्रदर्शन संवेदनशील भागों में, इसके बजाय '' .join () फ़ॉर्म का उपयोग किया जाना चाहिए। यह सुनिश्चित करेगा कि विभिन्न क्रियान्वयन के दौरान रेखीय समय में संघनन होता है।


5
संदर्भ लिंक अच्छा होगा :)

6

हालांकि कुछ हद तक, कोड लाइक ए पाइथोनिस्टा: आइडोमेटिक पायथन इस खंड में सिफारिश join()करता + है । निम्न अस्वीकरण के साथ, स्ट्रिंग संयोजन पर अपने खंड में PythonSpeedPerformanceTips करता है :

पायथन के बाद के संस्करणों के संबंध में इस खंड की सटीकता विवादित है। CPython 2.5 में, स्ट्रिंग का संघनन काफी तेज है, हालांकि यह अन्य Python कार्यान्वयन के लिए भी लागू नहीं हो सकता है। चर्चा के लिए ConcatenationTestCode देखें।


6

जैसा कि @jdi ने उल्लेख किया है कि पायथन डॉक्यूमेंटेशन स्ट्रिंग स्ट्रिंग के उपयोग str.joinया उपयोग के लिए सुझाव देता है io.StringIO। और कहते हैं कि एक डेवलपर को +=लूप से द्विघात समय की अपेक्षा करनी चाहिए , भले ही पायथन 2.4 के बाद से अनुकूलन हो। जैसा कि यह उत्तर कहता है:

यदि पायथन को पता चलता है कि बाएं तर्क में कोई अन्य संदर्भ नहीं है, तो यह reallocस्ट्रिंग को जगह में बदलकर प्रतिलिपि से बचने का प्रयास करता है। यह ऐसा कुछ नहीं है जिस पर आपको कभी भरोसा करना चाहिए, क्योंकि यह एक कार्यान्वयन विवरण है और क्योंकि यदि reallocस्ट्रिंग को अक्सर स्थानांतरित करने की आवश्यकता होती है, तो प्रदर्शन ओ (एन ^ 2) के लिए वैसे भी खराब हो जाता है।

मैं वास्तविक दुनिया कोड का एक उदाहरण दिखाऊंगा जो +=इस अनुकूलन पर भोलेपन से निर्भर था, लेकिन यह लागू नहीं हुआ। नीचे दिया गया कोड बल्क एपीआई में इस्तेमाल किए जाने वाले छोटे खंडों के चलने योग्य को बड़ी मात्रा में परिवर्तित करता है।

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

यह कोड द्विघात समय जटिलता के कारण घंटों तक साहित्यिक रूप से चल सकता है। नीचे सुझाए गए डेटा संरचनाओं के साथ विकल्प हैं:

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()

सूक्ष्म बेंचमार्क


5

आप विभिन्न तरीकों से कर सकते हैं।

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

मैंने निम्नलिखित लेखों के माध्यम से इस छोटे से सारांश का निर्माण किया।


3

मेरा उपयोग का मामला थोड़ा अलग था। मुझे एक क्वेरी का निर्माण करना था जहाँ 20 से अधिक क्षेत्र गतिशील थे। मैंने प्रारूप विधि का उपयोग करने के इस दृष्टिकोण का पालन किया

query = "insert into {0}({1},{2},{3}) values({4}, {5}, {6})"
query.format('users','name','age','dna','suzan',1010,'nda')

यह + या अन्य तरीकों का उपयोग करने के बजाय मेरे लिए तुलनात्मक रूप से सरल था


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.