अजगर में सबसे कुशल स्ट्रिंग संघनन विधि क्या है?


148

वहाँ (जैसे अजगर में किसी भी कुशल जन स्ट्रिंग संयोजन विधि है StringBuilder C # या में StringBuffer जावा में)? मुझे निम्नलिखित विधियाँ यहाँ मिलीं :

  • का उपयोग करते हुए सरल सम्मेलन +
  • स्ट्रिंग सूची और joinविधि का उपयोग करना
  • मॉड्यूल UserStringसे उपयोग करनाMutableString
  • चरित्र सरणी और arrayमॉड्यूल का उपयोग करना
  • मॉड्यूल cStringIOसे उपयोग करनाStringIO

लेकिन आप विशेषज्ञ क्या उपयोग करते हैं या सुझाव देते हैं, और क्यों?

[ एक संबंधित प्रश्न यहां ]



ज्ञात अंशों को एक में समेटने के लिए, पायथन 3.6 में f''प्रारूप के तार होंगे जो पिछले पायथन संस्करणों में किसी भी विकल्प से अधिक तेज़ होंगे।
अंती हापाला

जवाबों:


127

इसमें आपकी रुचि हो सकती है: गुइडो द्वारा एक अनुकूलन किस्सा । यद्यपि यह भी याद रखने योग्य है कि यह एक पुराना लेख है और यह चीजों के अस्तित्व से पहले की तरह है ''.join(हालांकि मुझे लगता string.joinfieldsहै कि यह कमोबेश यही है)

उस के बल पर, arrayमॉड्यूल सबसे तेज़ हो सकता है यदि आप अपनी समस्या को इसमें शामिल कर सकते हैं। लेकिन ''.joinसंभवत: तेजी से पर्याप्त है और मुहावरेदार होने का लाभ है और इस प्रकार अन्य अजगर प्रोग्रामर को समझने में आसान है।

अंत में, अनुकूलन का सुनहरा नियम: तब तक अनुकूलन न करें जब तक कि आपको पता न हो कि आपको अनुमान लगाने की आवश्यकता है, और मापें।

आप timeitमॉड्यूल का उपयोग करके विभिन्न तरीकों को माप सकते हैं । यह आपको बता सकता है कि इंटरनेट बनाने वाले अनुमानों पर यादृच्छिक अजनबियों के बजाय, जो सबसे तेज़ है।


1
अनुकूलन कब करना है, इस बिंदु पर जोड़ना चाहते हैं: सबसे खराब मामलों के खिलाफ परीक्षण करना सुनिश्चित करें। उदाहरण के लिए, मैं अपना नमूना बढ़ा सकता हूं ताकि मेरा वर्तमान कोड 0.17 सेकंड से 170 सेकंड तक चला जाए। वैसे मैं बड़े नमूना आकारों में परीक्षण करना चाहता हूं क्योंकि वहां कम भिन्नता है।
फ्लिपर

2
"जब तक आप को पता है कि आप की जरूरत है अनुकूलन मत करो।" जब तक आप सिर्फ एक अलग तरह के मुहावरे का उपयोग नहीं कर रहे हैं और थोड़े अतिरिक्त प्रयास के साथ अपने कोड को फिर से तैयार करने से बच सकते हैं।
jeremyjjbrown 11

1
एक जगह जिसे आप जानते हैं कि आपको साक्षात्कार करने की आवश्यकता है (जो हमेशा आपकी गहरी समझ को ब्रश करने का एक शानदार समय है)। दुर्भाग्य से मुझे इस बारे में कोई आधुनिक लेख नहीं मिला है। (1) क्या जावा / सी # स्ट्रिंग अभी भी 2017 में खराब है? (२) सी ++ के बारे में कैसे? (३) अब पायथन में नवीनतम और महानतम मामलों के बारे में बताएं, जब हमें लाखों संगतों को करने की आवश्यकता होती है। क्या हम भरोसा कर सकते हैं कि जुड़ने से रैखिक समय में काम होगा?
user1854182

"उपवास पर्याप्त" का क्या अर्थ है .join()? मुख्य प्रश्न यह है कि क्या यह) कन्सट्रक्शन (इसी तरह s = s + 'abc') के लिए स्ट्रिंग की एक प्रति बनाता है , जिसे ओ (एन) रनटाइम की आवश्यकता होती है, या बी) केवल कॉपी बनाने के बिना मौजूदा स्ट्रिंग में संलग्न होता है, जिसके लिए ओ (1) की आवश्यकता होती है ?
CGFoX

64

''.join(sequenceofstrings) वह है जो आमतौर पर सबसे अच्छा काम करता है - सबसे सरल और सबसे तेज़।


3
@mshsayem, पायथन में एक अनुक्रम किसी भी उल्लेखनीय वस्तु, यहां तक ​​कि एक फ़ंक्शन भी हो सकता है।
निक डंडौलकिस

2
मैं ''.join(sequence)मुहावरे से बिल्कुल प्यार करता हूं । यह विशेष रूप से अल्पविराम से अलग सूचियों का उत्पादन करने के लिए उपयोगी है: ', '.join([1, 2, 3])स्ट्रिंग देता है '1, 2, 3'
एंड्रयू केटॉन

7
@mshsayem: "".join(chr(x) for x in xrange(65,91))--- इस मामले में, शामिल होने का तर्क एक पुनरावृत्ति है , जो एक जनरेटर अभिव्यक्ति के माध्यम से बनाया गया है। कोई अस्थायी सूची नहीं है जिसका निर्माण किया जाता है।
balpha

2
@balpha: और अभी तक जेनरेटर संस्करण सूची बोध संस्करण की तुलना में धीमा है: C: \ temp> python -mtimeit "'' .join (chr (x) in xrange के लिए x (65,91))" 100000 लूप, सर्वोत्तम। 3: 9.71 usec प्रति लूप C: \ temp> python -mtimeit "'.join (xr के लिए [chr (x) in xrange (65,91)]" 100000 लूप्स, सबसे अच्छा 3 - 7.1 लूप प्रति लूप
hughdbrown

1
@hughdbrown, हाँ, जब आपके पास वज़ू (विशिष्ट समय के मामले) से मुक्त मेमोरी होती है, तो जेनेक्सपी की तुलना में बेहतर तरीके से सूची को अनुकूलित किया जा सकता है, अक्सर 20-30%। जब मेमोरी की तंग चीजें अलग होती हैं - हालांकि, समय में पुन: पेश करना मुश्किल होता है! -)
एलेक्स मार्टेली

58

पायथन 3.6 ने शाब्दिक स्ट्रिंग इंटरपोलेशन के साथ ज्ञात घटकों के स्ट्रिंग संयोजन के लिए खेल को बदल दिया ।

मोकेस्टिनन के उत्तर से परीक्षण मामले को देखते हुए , तार होने

domain = 'some_really_long_example.com'
lang = 'en'
path = 'some/really/long/path/'

दावेदार हैं

  • f'http://{domain}/{lang}/{path}'- 0.151 µ एस

  • 'http://%s/%s/%s' % (domain, lang, path) - 0.321 µ एस

  • 'http://' + domain + '/' + lang + '/' + path - 0.356 µs

  • ''.join(('http://', domain, '/', lang, '/', path)) - 0.249 µs (ध्यान दें कि एक निरंतर-लम्बाई टपल का निर्माण एक निरंतर-लंबाई सूची बनाने की तुलना में थोड़ा तेज है)।

इस प्रकार वर्तमान में सबसे छोटा और संभव सबसे सुंदर कोड भी सबसे तेज है।

पायथन 3.6 के अल्फा संस्करणों में f''स्ट्रिंग्स का कार्यान्वयन सबसे धीमा संभव था - वास्तव में उत्पन्न बाइट कोड इसके बराबर है''.join() अनावश्यक कॉल के साथ मामले केstr.__format__ जिसके बिना तर्क बिना selfअपरिवर्तित वापस आ जाएंगे । इन अक्षमताओं को 3.6 अंतिम से पहले संबोधित किया गया था।

गति को पायथन 2 के लिए सबसे तेज़ विधि के साथ विपरीत किया जा सकता है, जो कि +मेरे कंप्यूटर पर है; और 8 बिट्स स्ट्रिंग्स के साथ 0.203 µ लेता है , और यदि स्ट्रिंग्स सभी यूनिकोड हैं, तो 0.259 एस।


38

यह इस बात पर निर्भर करता है कि आप क्या कर रहे हैं।

पायथन 2.5 के बाद, + संचालक के साथ स्ट्रिंग संघनन बहुत तेज है। यदि आप बस कुछ मानों को संक्षिप्त कर रहे हैं, तो + ऑपरेटर का उपयोग करना सबसे अच्छा काम करता है:

>>> x = timeit.Timer(stmt="'a' + 'b'")
>>> x.timeit()
0.039999961853027344

>>> x = timeit.Timer(stmt="''.join(['a', 'b'])")
>>> x.timeit()
0.76200008392333984

हालाँकि, यदि आप एक लूप में एक स्ट्रिंग डाल रहे हैं, तो आप सूची में शामिल होने की विधि का उपयोग करके बेहतर हैं:

>>> join_stmt = """
... joined_str = ''
... for i in xrange(100000):
...   joined_str += str(i)
... """
>>> x = timeit.Timer(join_stmt)
>>> x.timeit(100)
13.278000116348267

>>> list_stmt = """
... str_list = []
... for i in xrange(100000):
...   str_list.append(str(i))
... ''.join(str_list)
... """
>>> x = timeit.Timer(list_stmt)
>>> x.timeit(100)
12.401000022888184

... लेकिन ध्यान दें कि आपको अंतर ध्यान देने योग्य होने से पहले अपेक्षाकृत अधिक संख्या में तार एक साथ लगाने होंगे।


2
1) आपके पहले माप में यह संभवतः सूची निर्माण है जो समय लेता है। टपल के साथ प्रयास करें। 2) सीपीथॉन समान रूप से अच्छा प्रदर्शन करता है, हालांकि अन्य पायथन कार्यान्वयन + और + = के साथ बदतर प्रदर्शन करते हैं
u0b34a0f6ae

22

जॉन फोहे के उत्तर के अनुसार, जब तक आपको नहीं करना है, तब तक अनुकूलन न करें, लेकिन यदि आप यहां हैं और यह सवाल पूछ रहे हैं, तो यह ठीक हो सकता है क्योंकि आप कर रहे हैं । मेरे मामले में, मुझे स्ट्रिंग चर से कुछ URL को इकट्ठा करने की आवश्यकता थी ... तेज। मैंने देखा कि कोई भी (अभी तक) स्ट्रिंग प्रारूप विधि पर विचार नहीं कर रहा है, इसलिए मैंने सोचा कि मैं कोशिश करूँगा और, ज्यादातर हल्के हित के लिए, मैंने सोचा कि मैं अच्छे औसत दर्जे के लिए स्ट्रिंग प्रक्षेप ऑपरेटर को टॉस करूँगा। सच कहूं, तो मुझे नहीं लगता था कि इनमें से कोई भी सीधे '+' ऑपरेशन या '' .join () में ढेर हो जाएगा। लेकिन अंदाज़ा लगाओ कि क्या है? मेरे पायथन 2.7.5 सिस्टम पर, स्ट्रिंग प्रक्षेप ऑपरेटर उन सभी को नियंत्रित करता है और string.format () सबसे खराब प्रदर्शनकर्ता है:

# concatenate_test.py

from __future__ import print_function
import timeit

domain = 'some_really_long_example.com'
lang = 'en'
path = 'some/really/long/path/'
iterations = 1000000

def meth_plus():
    '''Using + operator'''
    return 'http://' + domain + '/' + lang + '/' + path

def meth_join():
    '''Using ''.join()'''
    return ''.join(['http://', domain, '/', lang, '/', path])

def meth_form():
    '''Using string.format'''
    return 'http://{0}/{1}/{2}'.format(domain, lang, path)

def meth_intp():
    '''Using string interpolation'''
    return 'http://%s/%s/%s' % (domain, lang, path)

plus = timeit.Timer(stmt="meth_plus()", setup="from __main__ import meth_plus")
join = timeit.Timer(stmt="meth_join()", setup="from __main__ import meth_join")
form = timeit.Timer(stmt="meth_form()", setup="from __main__ import meth_form")
intp = timeit.Timer(stmt="meth_intp()", setup="from __main__ import meth_intp")

plus.val = plus.timeit(iterations)
join.val = join.timeit(iterations)
form.val = form.timeit(iterations)
intp.val = intp.timeit(iterations)

min_val = min([plus.val, join.val, form.val, intp.val])

print('plus %0.12f (%0.2f%% as fast)' % (plus.val, (100 * min_val / plus.val), ))
print('join %0.12f (%0.2f%% as fast)' % (join.val, (100 * min_val / join.val), ))
print('form %0.12f (%0.2f%% as fast)' % (form.val, (100 * min_val / form.val), ))
print('intp %0.12f (%0.2f%% as fast)' % (intp.val, (100 * min_val / intp.val), ))

परिणाम:

# python2.7 concatenate_test.py
plus 0.360787868500 (90.81% as fast)
join 0.452811956406 (72.36% as fast)
form 0.502608060837 (65.19% as fast)
intp 0.327636957169 (100.00% as fast)

यदि मैं एक छोटे डोमेन और छोटे पथ का उपयोग करता हूं, तो प्रक्षेप अभी भी जीतता है। अंतर अधिक स्पष्ट है, हालांकि, लंबे समय तक तार के साथ।

अब जब मेरे पास एक अच्छा परीक्षण स्क्रिप्ट था, तो मैंने पायथन 2.6, 3.3 और 3.4 के तहत भी परीक्षण किया, यहां परिणाम हैं। पायथन 2.6 में, प्लस ऑपरेटर सबसे तेज़ है! पायथन 3 पर, जीत में शामिल हों। नोट: ये परीक्षण मेरे सिस्टम पर बहुत ही दोहराने योग्य हैं। इसलिए, 'प्लस' 2.6 पर हमेशा तेज है, 'इंटेप' हमेशा 2.7 पर तेज है और 'ज्वाइन' हमेशा पायथॉन 3.x पर तेज है।

# python2.6 concatenate_test.py
plus 0.338213920593 (100.00% as fast)
join 0.427221059799 (79.17% as fast)
form 0.515371084213 (65.63% as fast)
intp 0.378169059753 (89.43% as fast)

# python3.3 concatenate_test.py
plus 0.409130576998 (89.20% as fast)
join 0.364938726001 (100.00% as fast)
form 0.621366866995 (58.73% as fast)
intp 0.419064424001 (87.08% as fast)

# python3.4 concatenate_test.py
plus 0.481188605998 (85.14% as fast)
join 0.409673971997 (100.00% as fast)
form 0.652010936996 (62.83% as fast)
intp 0.460400978001 (88.98% as fast)

# python3.5 concatenate_test.py
plus 0.417167026084 (93.47% as fast)
join 0.389929617057 (100.00% as fast)
form 0.595661019906 (65.46% as fast)
intp 0.404455224983 (96.41% as fast)

सबक सीखा:

  • कभी-कभी, मेरी धारणाएँ गलत हैं।
  • सिस्टम के खिलाफ टेस्ट एन.वी. आप उत्पादन में भाग रहे होंगे।
  • स्ट्रिंग प्रक्षेप अभी तक मरा नहीं है!

tl; डॉ:

  • यदि आप 2.6 का उपयोग कर रहे हैं, तो + ऑपरेटर का उपयोग करें।
  • यदि आप 2.7 का उपयोग कर रहे हैं तो '%' ऑपरेटर का उपयोग करें।
  • यदि आप 3.x का उपयोग कर रहे हैं '' .join ()।

2
नोट: शाब्दिक स्ट्रिंग प्रक्षेप अभी भी 3.6+ के लिए तेज़ है:f'http://{domain}/{lang}/{path}'
टेम्पोरलवुल्फ़

1
इसके अलावा, .format(): तीन रूपों, तेजी से धीमी गति से करने के लिए है "{}".format(x), "{0}".format(x),"{x}".format(x=x)
TemporalWolf

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

7

यह बहुत ज्यादा हर नए संयोजन के बाद नए स्ट्रिंग के सापेक्ष आकार पर निर्भर करता है। +ऑपरेटर के साथ , प्रत्येक कॉन्फैक्शन के लिए एक नया स्ट्रिंग बनाया जाता है। यदि मध्यस्थ के तार अपेक्षाकृत लंबे होते हैं, तो +तेजी से धीमी हो जाती है क्योंकि नया मध्यस्थ स्ट्रिंग संग्रहीत किया जा रहा है।

इस मामले पर विचार करें:

from time import time
stri=''
a='aagsdfghfhdyjddtyjdhmfghmfgsdgsdfgsdfsdfsdfsdfsdfsdfddsksarigqeirnvgsdfsdgfsdfgfg'
l=[]
#case 1
t=time()
for i in range(1000):
    stri=stri+a+repr(i)
print time()-t

#case 2
t=time()
for i in xrange(1000):
    l.append(a+repr(i))
z=''.join(l)
print time()-t

#case 3
t=time()
for i in range(1000):
    stri=stri+repr(i)
print time()-t

#case 4
t=time()
for i in xrange(1000):
    l.append(repr(i))
z=''.join(l)
print time()-t

परिणाम

1 0.00493192672729

2 0.000509023666382

3 0.00042200088501

4 0.000482797622681

1 और 2 के मामले में, हम एक बड़ा स्ट्रिंग जोड़ते हैं, और जुड़ते हैं () लगभग 10 गुना तेजी से प्रदर्शन करते हैं। 3 और 4 के मामले में, हम एक छोटी स्ट्रिंग जोड़ते हैं, और '+' थोड़ा तेज प्रदर्शन करता है


3

मैं एक ऐसी स्थिति में भाग गया, जहाँ मुझे अज्ञात आकार के एक परिशिष्ट तार की आवश्यकता थी। ये बेंचमार्क परिणाम हैं (अजगर 2.7.3):

$ python -m timeit -s 's=""' 's+="a"'
10000000 loops, best of 3: 0.176 usec per loop
$ python -m timeit -s 's=[]' 's.append("a")'
10000000 loops, best of 3: 0.196 usec per loop
$ python -m timeit -s 's=""' 's="".join((s,"a"))'
100000 loops, best of 3: 16.9 usec per loop
$ python -m timeit -s 's=""' 's="%s%s"%(s,"a")'
100000 loops, best of 3: 19.4 usec per loop

इससे पता चलता है कि '+ =' सबसे तेज़ है। स्काईमाइंड लिंक के परिणाम कुछ पुराने हैं।

(मुझे लगता है कि दूसरा उदाहरण पूरा नहीं हुआ है, अंतिम सूची में शामिल होने की आवश्यकता होगी। यह दिखाता है, हालांकि, बस सूची तैयार करने में स्ट्रिंग समवर्ती से अधिक समय लगता है।)


मुझे 3rd और 4th टेस्ट के लिए 1 सेकंड का समय मिल रहा है। आपको इतना अधिक समय क्यों मिल रहा है? pastebin.com/qabNMCHS
bad_keypoint

@ronnieaka: उन्हें सभी परीक्षणों के लिए 1-सेकंड का समय मिल रहा है। वह 3rd & 4th के लिए > 1 µ प्राप्त कर रहा है , जो आपने नहीं किया। मैं उन परीक्षणों (पायथन 2.7.5, लिनक्स पर) पर धीमा समय भी प्राप्त करता हूं। सीपीयू हो सकता है, संस्करण, झंडे का निर्माण, जो जानता है।
थानाटोस

ये बेंचमार्क परिणाम बेकार हैं। विशेष रूप से, पहला मामला, जो किसी भी स्ट्रिंग संघनन नहीं कर रहा है, बस दूसरे स्ट्रिंग मान बरकरार है।
अंती हापला

3

एक साल बाद, आइए pokthon 3.4.3 के साथ mkoistinen के उत्तर का परीक्षण करें:

  • प्लस 0.963564149000 (95.83% तेज)
  • 0.923408469000 (तेजी के रूप में 100.00%) में शामिल हों
  • फॉर्म 1.501130934000 (उपवास के रूप में 61.51%)
  • intp 1.019677452000 (उपवास के रूप में 90.56%)

कुछ नहीं बदला। ज्वाइन करना अभी भी सबसे तेज़ तरीका है। Intp के साथ यकीनन पठनीयता के मामले में सबसे अच्छा विकल्प होने के बावजूद आप intp का उपयोग करना चाहते हैं।


1
शायद यह मोकेस्टिनन उत्तर के अतिरिक्त हो सकता है क्योंकि यह पूर्ण विकसित उत्तर का एक छोटा सा है (या कम से कम आपके द्वारा उपयोग किए जा रहे कोड को जोड़ें)।
ट्रिलियनियन

1

@ जेसनबैकर के बेंचमार्क से प्रेरित, यहाँ एक सरल 10 स्टिंग्स की तुलना करते हुए "abcdefghijklmnopqrstuvxyz"दिखाया गया है.join() यह तेज है; चर में इस छोटी वृद्धि के साथ:

श्रृंखलन

>>> x = timeit.Timer(stmt='"abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz"')
>>> x.timeit()
0.9828147209324385

शामिल हों

>>> x = timeit.Timer(stmt='"".join(["abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz"])')
>>> x.timeit()
0.6114138159765048

इस प्रश्न के स्वीकृत उत्तर (लंबे समय तक स्क्रॉल करें) पर एक नज़र डालें: stackoverflow.com/questions/1349311/…
mshsayem

1

एक के लिए छोटे सेट की छोटी स्ट्रिंग (यानी 2 या 3 कोई कुछ वर्णों से अधिक का तार), प्लस अभी भी जिस तरह से तेजी से होता है। पायथन 2 और 3 में मोकिस्टीन की अद्भुत स्क्रिप्ट का उपयोग करना:

plus 2.679107467004 (100.00% as fast)
join 3.653773699996 (73.32% as fast)
form 6.594011374000 (40.63% as fast)
intp 4.568015249999 (58.65% as fast)

इसलिए जब आपका कोड अलग-अलग छोटे समागमों की एक बड़ी संख्या कर रहा है, तो प्लस पसंदीदा तरीका है यदि गति महत्वपूर्ण है।


1

संभवतः "पायथन 3.6 में नए एफ-स्ट्रिंग्स" तार को समेटने का सबसे कुशल तरीका है।

% S का उपयोग कर रहा है

>>> timeit.timeit("""name = "Some"
... age = 100
... '%s is %s.' % (name, age)""", number = 10000)
0.0029734770068898797

का उपयोग कर

>>> timeit.timeit("""name = "Some"
... age = 100
... '{} is {}.'.format(name, age)""", number = 10000)
0.004015227983472869

च का उपयोग करना

>>> timeit.timeit("""name = "Some"
... age = 100
... f'{name} is {age}.'""", number = 10000)
0.0019175919878762215

स्रोत: https://realpython.com/python-f-strings/

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