पायथन में स्ट्रिंग संयोजन बनाम स्ट्रिंग प्रतिस्थापन


98

पायथन में, स्ट्रिंग के स्थानापन्न बनाम स्ट्रिंग प्रतिस्थापन का उपयोग करने के लिए कहां और कब मुझे ईल्यूड करता है। जैसा कि स्ट्रिंग के संघनन ने प्रदर्शन में बड़ी वृद्धि देखी है, क्या यह (अधिक) एक व्यावहारिक निर्णय के बजाय एक शैलीगत निर्णय है?

एक ठोस उदाहरण के लिए, लचीले यूआरआई के निर्माण को कैसे संभालना चाहिए:

DOMAIN = 'http://stackoverflow.com'
QUESTIONS = '/questions'

def so_question_uri_sub(q_num):
    return "%s%s/%d" % (DOMAIN, QUESTIONS, q_num)

def so_question_uri_cat(q_num):
    return DOMAIN + QUESTIONS + '/' + str(q_num)

संपादित करें: स्ट्रिंग की सूची में शामिल होने और नामित प्रतिस्थापन का उपयोग करने के बारे में भी सुझाव दिए गए हैं। ये केंद्रीय विषय पर भिन्न होते हैं, जो कि, किस तरह से किस समय करना सही तरीका है? प्रतिक्रियाओं के लिए धन्यवाद!


मजेदार, रूबी में, स्ट्रिंग प्रक्षेप आमतौर पर संघनन की तुलना में अधिक तेज होता है ...
केल्टिया

आप वापस लौट आए "" .join ([DOMAIN, QUESTIONS, str (q_num)])
जिम्मी

मैं कोई रूबी विशेषज्ञ नहीं हूं, लेकिन मैं शर्त लगाऊंगा कि प्रक्षेप तेज है क्योंकि रूबी में तार परस्पर जुड़े हुए हैं। पायथन में स्ट्रिंग्स अपरिवर्तनीय अनुक्रम हैं।
gotgenes

1
यूआरआई के बारे में बस थोड़ी सी टिप्पणी। यूआरआई स्ट्रिंग्स की तरह बिल्कुल नहीं हैं। यूआरआई हैं, इसलिए जब आप कॉन्टेक्ट करते हैं या उनकी तुलना करते हैं तो आपको बहुत सावधान रहना होगा। उदाहरण: एक सर्वर http पर पोर्ट 80 पर अपना अभ्यावेदन देने वाला। example.org (अंत में कोई स्लैह नहीं) example.org/ (स्लैश) example.org:80/ (स्लैह + पोर्ट 80) एक ही यूरी है लेकिन समान नहीं है स्ट्रिंग।
karlcow

जवाबों:


55

मेरी मशीन के अनुसार कॉन्टैटेनेशन (काफी) तेज है। लेकिन शैलीगत रूप से, मैं प्रतिस्थापन की कीमत का भुगतान करने के लिए तैयार हूं यदि प्रदर्शन महत्वपूर्ण नहीं है। खैर, और अगर मुझे प्रारूपण की आवश्यकता है, तो सवाल पूछने की भी कोई जरूरत नहीं है ... प्रक्षेप / टेंपलेटिंग का उपयोग करने के अलावा कोई विकल्प नहीं है।

>>> import timeit
>>> def so_q_sub(n):
...  return "%s%s/%d" % (DOMAIN, QUESTIONS, n)
...
>>> so_q_sub(1000)
'http://stackoverflow.com/questions/1000'
>>> def so_q_cat(n):
...  return DOMAIN + QUESTIONS + '/' + str(n)
...
>>> so_q_cat(1000)
'http://stackoverflow.com/questions/1000'
>>> t1 = timeit.Timer('so_q_sub(1000)','from __main__ import so_q_sub')
>>> t2 = timeit.Timer('so_q_cat(1000)','from __main__ import so_q_cat')
>>> t1.timeit(number=10000000)
12.166618871951641
>>> t2.timeit(number=10000000)
5.7813972166853773
>>> t1.timeit(number=1)
1.103492206766532e-05
>>> t2.timeit(number=1)
8.5206360154188587e-06

>>> def so_q_tmp(n):
...  return "{d}{q}/{n}".format(d=DOMAIN,q=QUESTIONS,n=n)
...
>>> so_q_tmp(1000)
'http://stackoverflow.com/questions/1000'
>>> t3= timeit.Timer('so_q_tmp(1000)','from __main__ import so_q_tmp')
>>> t3.timeit(number=10000000)
14.564135316080637

>>> def so_q_join(n):
...  return ''.join([DOMAIN,QUESTIONS,'/',str(n)])
...
>>> so_q_join(1000)
'http://stackoverflow.com/questions/1000'
>>> t4= timeit.Timer('so_q_join(1000)','from __main__ import so_q_join')
>>> t4.timeit(number=10000000)
9.4431309007150048

10
क्या आपने वास्तविक बड़े तार (जैसे 100000 चार्ट) के साथ परीक्षण किए?
ड्रंक

24

नाम प्रतिस्थापन के बारे में मत भूलना:

def so_question_uri_namedsub(q_num):
    return "%(domain)s%(questions)s/%(q_num)d" % locals()

4
इस कोड में कम से कम 2 खराब प्रोग्रामिंग प्रथाएं हैं: वैश्विक चर की उम्मीद (डोमेन और प्रश्न फ़ंक्शन के अंदर घोषित नहीं किए जाते हैं) और एक प्रारूप () फ़ंक्शन की तुलना में अधिक चर पास करना। डाउनवोटिंग क्योंकि यह उत्तर खराब कोडिंग प्रथाओं को सिखाता है।
जिपरेली

12

एक पाश में समाप्‍त तारों से सावधान रहें! स्ट्रिंग संघनन की लागत परिणाम की लंबाई के लिए आनुपातिक है। लूपिंग आपको सीधे एन-स्क्वेर्ड की भूमि पर ले जाता है। कुछ भाषाएँ सबसे हाल ही में आवंटित स्ट्रिंग के लिए संघनन का अनुकूलन करेंगी, लेकिन संकलक पर आपके द्विघात एल्गोरिथ्म को रैखिक में अनुकूलित करने के लिए गणना करना जोखिम भरा है। joinस्ट्रिंग्स की एक पूरी सूची लेने वाले आदिम ( ?) का उपयोग करने के लिए सबसे अच्छा , एक ही आवंटन करता है, और एक ही बार में उन सभी को समेटता है।


16
यह वर्तमान नहीं है। अजगर के नवीनतम संस्करणों में, जब आप लूप में स्ट्रैटेनेट करते हैं तो एक छिपा हुआ स्ट्रिंग बफर बनाया जाता है।
सीन ओसेवा

5
@ सीन: हां, जैसा कि मैंने कहा, कुछ भाषाएं अनुकूलन करेंगी, लेकिन यह एक जोखिम भरा अभ्यास है।
नॉर्मन राम्से

11

"जैसा कि स्ट्रिंग के संयोजन ने प्रदर्शन में बड़ी वृद्धि देखी है ..."

यदि प्रदर्शन मायने रखता है, तो यह जानना अच्छा है।

हालाँकि, मैंने जो प्रदर्शन समस्याएं देखी हैं, वे कभी भी स्ट्रिंग ऑपरेशन के लिए कम नहीं हुई हैं। मैं आम तौर पर I / O, छँटाई और O ( n 2 ) संचालन में अड़चनों के कारण परेशानी में पड़ गया हूँ ।

जब तक स्ट्रिंग ऑपरेशन प्रदर्शन सीमाएं नहीं हैं, तब तक मैं उन चीजों के साथ रहूँगा जो स्पष्ट हैं। अधिकतर, जब यह एक पंक्ति या उससे कम होता है, तो इसका प्रतिस्थापन होता है, जब इसका अर्थ होता है, और बड़े होने पर एक टेम्पलेट टूल (जैसे माको)।


10

आप क्या संक्षिप्त करना / प्रक्षेप करना चाहते हैं और आप परिणाम कैसे प्रारूपित करना चाहते हैं, इस पर आपको निर्णय लेना चाहिए।

  • स्ट्रिंग प्रक्षेप आप आसानी से स्वरूपण जोड़ने के लिए अनुमति देता है। वास्तव में, आपका स्ट्रिंग प्रक्षेप संस्करण आपके अवतरण संस्करण के समान कार्य नहीं करता है; यह वास्तव में q_numपैरामीटर से पहले एक अतिरिक्त फ़ॉरवर्ड स्लैश जोड़ता है । एक ही काम करने के लिए, आपको return DOMAIN + QUESTIONS + "/" + str(q_num)उस उदाहरण में लिखना होगा ।

  • इंटरपोलेशन से संख्यात्मक को प्रारूपित करना आसान हो जाता है; "%d of %d (%2.2f%%)" % (current, total, total/current)संघनन रूप में बहुत कम पठनीय होगा।

  • जब आपके पास स्ट्रिंग-ize के लिए निश्चित संख्या में आइटम नहीं हैं, तो कॉनसेनटेशन उपयोगी है।

यह भी जान लें कि पायथन 2.6 स्ट्रिंग प्रक्षेप का एक नया संस्करण पेश करता है, जिसे स्ट्रिंग टेम्प्लेटिंग कहा जाता है :

def so_question_uri_template(q_num):
    return "{domain}/{questions}/{num}".format(domain=DOMAIN,
                                               questions=QUESTIONS,
                                               num=q_num)

स्ट्रिंग टेम्प्लेटिंग को अंततः% -interpolation को बदलने के लिए स्लेट किया गया है, लेकिन यह काफी समय तक नहीं होगा, मुझे लगता है।


जब भी आप अजगर 3.0 में जाने का फैसला करेंगे, तो यह होगा। इसके अलावा, इस तथ्य के लिए पीटर की टिप्पणी देखें कि आप वैसे भी% ऑपरेटर के साथ नामित प्रतिस्थापन कर सकते हैं।
जॉन फोहि

"जब आपके पास स्ट्रिंग-आयज़ के लिए निश्चित संख्या में आइटम नहीं हैं, तो कॉन्टेनेटेशन उपयोगी है।" - आप एक सूची / सरणी का मतलब है? उस स्थिति में, क्या आप उन्हें शामिल नहीं कर सकते ()?
स्ट्रेजर

"क्या आप उन्हें शामिल नहीं कर सकते हैं?" - हाँ (यह मानते हुए कि आप वस्तुओं के बीच एक समान विभाजक चाहते हैं)। सूची और जनरेटर की समझ string.join के साथ शानदार काम करते हैं।
टिम लेशर

1
"ठीक है, यह तब होगा जब आप अजगर 3.0 में जाने का फैसला करेंगे" - नहीं, py3k अभी भी% ऑपरेटर का समर्थन करता है। अगला संभावित अपचयन बिंदु 3.1 है, इसलिए इसमें अभी भी कुछ जीवन है।
टिम लेशर

2
2 साल बाद ... अजगर 3.2 रिलीज के करीब है और% शैली का प्रक्षेप अभी भी ठीक है।
कोरी गोल्डबर्ग

8

मैं सिर्फ जिज्ञासा से बाहर अलग-अलग स्ट्रिंग संघनन / प्रतिस्थापन विधियों की गति का परीक्षण कर रहा था। विषय पर एक Google खोज मुझे यहाँ लाया। मुझे लगा कि मैं अपने परीक्षा परिणाम इस उम्मीद में पोस्ट करूंगा कि इससे किसी को निर्णय लेने में मदद मिल सकती है।

    import timeit
    def percent_():
            return "test %s, with number %s" % (1,2)

    def format_():
            return "test {}, with number {}".format(1,2)

    def format2_():
            return "test {1}, with number {0}".format(2,1)

    def concat_():
            return "test " + str(1) + ", with number " + str(2)

    def dotimers(func_list):
            # runs a single test for all functions in the list
            for func in func_list:
                    tmr = timeit.Timer(func)
                    res = tmr.timeit()
                    print "test " + func.func_name + ": " + str(res)

    def runtests(func_list, runs=5):
            # runs multiple tests for all functions in the list
            for i in range(runs):
                    print "----------- TEST #" + str(i + 1)
                    dotimers(func_list)

... दौड़ने के बाद runtests((percent_, format_, format2_, concat_), runs=5), मैंने पाया कि इन छोटे तारों पर अन्य लोगों की तुलना में% विधि लगभग दोगुनी थी। कॉन्कैट विधि हमेशा सबसे धीमी (मुश्किल से) थी। format()विधि में पदों को स्विच करते समय बहुत छोटे अंतर थे , लेकिन नियमित प्रारूप विधि की तुलना में पदों को स्विच करना हमेशा कम से कम .01 धीमा था।

परीक्षण के परिणामों का नमूना:

    test concat_()  : 0.62  (0.61 to 0.63)
    test format_()  : 0.56  (consistently 0.56)
    test format2_() : 0.58  (0.57 to 0.59)
    test percent_() : 0.34  (0.33 to 0.35)

मैं इन्हें चलाता था क्योंकि मैं अपनी लिपियों में स्ट्रिंग संयोजन का उपयोग करता हूं, और मैं सोच रहा था कि लागत क्या थी। मैंने उन्हें सुनिश्चित करने के लिए अलग-अलग आदेशों में दौड़ाया कि कुछ भी हस्तक्षेप नहीं कर रहा था, या बेहतर प्रदर्शन पहले या आखिरी हो रहा था। एक साइड नोट पर, मैंने कुछ लंबे स्ट्रिंग जनरेटर को उन कार्यों में फेंक दिया जैसे कि "%s" + ("a" * 1024)और नियमित रूप से कंकट लगभग 3 गुना तेज (1.1 बनाम 2.8) का उपयोग कर रहा था।format और %विधियों । मुझे लगता है कि यह स्ट्रिंग्स पर निर्भर करता है, और आप क्या हासिल करने की कोशिश कर रहे हैं। यदि प्रदर्शन वास्तव में मायने रखता है, तो अलग-अलग चीजों की कोशिश करना और उनका परीक्षण करना बेहतर हो सकता है। मैं गति पर पठनीयता का चयन करता हूं, जब तक कि गति एक समस्या नहीं बन जाती, लेकिन यह सिर्फ मुझे लगता है। एसओ को मेरी कॉपी / पेस्ट पसंद नहीं आया, मुझे इसे सही दिखाने के लिए हर चीज पर 8 जगह रखनी पड़ी। मैं आमतौर पर 4 का उपयोग करता हूं।


1
आपको इस बात पर गंभीरता से विचार करना चाहिए कि आप क्या कर रहे हैं। एक के लिए आपका कॉनैट धीमा है क्योंकि आपके पास इसमें दो स्ट्रैस कास्ट हैं। स्ट्रिंग्स के साथ परिणाम विपरीत होता है, क्योंकि स्ट्रिंग कॉनैट वास्तव में सभी विकल्पों की तुलना में तेजी से होता है जब केवल तीन स्ट्रिंग्स का संबंध होता है।
जस्टस विंगर्ट

@JustusWingert, यह अब दो साल का है। मैंने बहुत कुछ सीखा है क्योंकि मैंने यह 'परीक्षण' पोस्ट किया है। ईमानदारी से, इन दिनों मैं उपयोग करता हूं str.format()और str.join()सामान्य रूप से सामान्य से अधिक हूं । मैं पीईपी 498 से 'एफ-स्ट्रिंग्स' के लिए भी नजर रख रहा हूं , जिसे हाल ही में स्वीकार किया गया है। str()प्रदर्शन को प्रभावित करने वाली कॉल के लिए , मुझे यकीन है कि आप इसके बारे में सही हैं। मुझे पता नहीं था कि उस समय कितने महंगे फंक्शन कॉल थे। मुझे अभी भी लगता है कि जब कोई संदेह हो तो परीक्षण किया जाना चाहिए।
सीजे वेलबॉर्न

के साथ एक त्वरित परीक्षण के बाद join_(): return ''.join(["test ", str(1), ", with number ", str(2)]), यह joinभी प्रतिशत की तुलना में धीमी है।
जाबोर

4

याद रखें, शैलीगत निर्णय कर रहे हैं , व्यावहारिक निर्णय अगर तुम कभी बनाए रखने या अपने कोड डिबगिंग पर योजना :-) वहाँ नुथ (? संभवतः होरे के हवाले से) से एक प्रसिद्ध उद्धरण है: "हम, छोटे क्षमता के बारे में भूल जाना चाहिए समय के 97% के बारे में कहते हैं: सभी बुराईयो की जड़ समयपूर्व इष्टतमीकरण है।"

जब तक आप सावधान न हों (कहते हैं) एक O (n) कार्य को O (n 2 ) कार्य में बदल दें, तो मैं आपको जो भी समझने में आसान होगा, उसके साथ जाऊंगा।


0

जहाँ भी मैं कर सकता हूँ मैं प्रतिस्थापन का उपयोग करता हूँ। यदि मैं एक लूप फॉर-लूप का निर्माण कर रहा हूं तो मैं केवल कॉनटेनैशन का उपयोग करता हूं।


7
"फॉर-लूप में एक स्ट्रिंग का निर्माण" - अक्सर यह एक ऐसा मामला होता है जिसमें आप '' .जॉन और जेनरेटर एक्सप्रेशन का उपयोग कर सकते हैं ..
जॉन

-1

वास्तव में सही काम करना है, इस मामले में (पथ निर्माण) का उपयोग करना है os.path.join। स्ट्रिंग संगति या प्रक्षेप नहीं


1
यह ओएस रास्तों के लिए सच है (जैसे आपके फाइलसिस्टम पर) लेकिन इस उदाहरण में URI का निर्माण करते समय नहीं। URI का हमेशा विभाजक के रूप में '/' होता है।
आंद्रे ब्लम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.