पहला उदाहरण यह बहुत स्पष्ट है कि यह भी संदर्भ अनुकूलन के मामले है के लिए (वहाँ दो संदर्भों वास्तव में कर रहे हैं: वस्तु से ही एक और एक LOAD_FAST
; unicode_concatenate
PyUnicode_Append
करने के लिए नियंत्रण पार करने से पहले 1 करने के लिए इसे कम करने की कोशिश करेंगे) CPython द्वारा किया इस unicode_modifiable
फ़ंक्शन का उपयोग:
static int
unicode_modifiable(PyObject *unicode)
{
assert(_PyUnicode_CHECK(unicode));
if (Py_REFCNT(unicode) != 1)
return 0;
if (_PyUnicode_HASH(unicode) != -1)
return 0;
if (PyUnicode_CHECK_INTERNED(unicode))
return 0;
if (!PyUnicode_CheckExact(unicode))
return 0;
#ifdef Py_DEBUG
/* singleton refcount is greater than 1 */
assert(!unicode_is_singleton(unicode));
#endif
return 1;
}
लेकिन उदाहरण डेटा के रूप में दूसरे मामले में एक अजगर dict
के बजाय एक सरल चर में संग्रहीत किया जाता है, तो चीजें थोड़ा अलग मिलता है।
a.accum_ += 'foo'
वास्तव में a.accum_
का मूल्य प्रीफ़ेचिंग और ढेर के लिए उस पर भंडारण की आवश्यकता है। तो, अब स्ट्रिंग कम से कम तीन संदर्भ: उदाहरण शब्दकोश से एक, DUP_TOP
से एक और PyObject_GetAttr
से LOAD_ATTR
द्वारा उपयोग किया गया है। इसलिए, पाइथन इस मामले को अनुकूलित नहीं कर सकता क्योंकि उनमें से एक को संशोधित करने से अन्य संदर्भ भी प्रभावित होंगे।
>>> class A:
pass
...
>>> a = A()
>>> def func():
a.str = 'spam'
print a.str
return '_from_func'
...
>>> a.str = 'foo'
>>> a.str += func()
spam
आप उत्पादन यहाँ 'spam_from_func'
होने की अपेक्षा करेंगे, लेकिन यह क्योंकि a.str
के मूल मूल्य अजगर द्वारा जमा हो गया था इससे पहले कि func()
बुलाया गया था अलग होने जा रहा है।
>>> a.str
'foo_from_func'
बाइट कोड:
>>> import dis
>>> def func_class():
a = Foo()
a.accum = ''
a.accum += 'zzzzz\n'
...
>>> dis.dis(func_class)
2 0 LOAD_GLOBAL 0 (Foo)
3 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
6 STORE_FAST 0 (a)
3 9 LOAD_CONST 1 ('')
12 LOAD_FAST 0 (a)
15 STORE_ATTR 1 (accum)
4 18 LOAD_FAST 0 (a)
21 DUP_TOP
22 LOAD_ATTR 1 (accum)
25 LOAD_CONST 2 ('zzzzz\n')
28 INPLACE_ADD
29 ROT_TWO
30 STORE_ATTR 1 (accum)
33 LOAD_CONST 0 (None)
36 RETURN_VALUE
ध्यान दें कि यह अनुकूलन around 2004 में किया गया था (CPython 2।4) उपयोगकर्ताओं को a += b
या a = a + b
की धीमी गति से रोकने के लिए, इसलिए यह ज्यादातर सरल चर के लिए होता है और केवल तभी काम करता है जब अगला निर्देश STORE_FAST
(स्थानीय चर), STORE_DEREF
(बंद) और STORE_NAME
है। यह एक सामान्य समाधान नहीं है, the best way to do this in Python is to create a list and join its items using str.join
।
CPython कार्यान्वयन विस्तार: s
और t
दोनों तार कर रहे हैं, इस तरह के CPython के रूप में कुछ अजगर कार्यान्वयन आमतौर पर प्रपत्र s = s + t
या s += t
के कार्य के लिए एक में जगह अनुकूलन कर सकते हैं। जब लागू होता है, तो यह अनुकूलन वर्गबद्ध रन-टाइम को कम संभव बनाता है। यह अनुकूलन दोनों संस्करण और कार्यान्वयन निर्भर है। प्रदर्शन संवेदनशील कोड के लिए, str.join()
विधि का उपयोग करना बेहतर है जो लगातार रैखिक concatenation संस्करणों और कार्यान्वयन में प्रदर्शन का आश्वासन देता है।
यह निश्चित रूप से है क्योंकि पहले दृष्टिकोण में पहली बार स्ट्रिंग के जीवनकाल के दौरान केवल एक संदर्भ होगा और सीपीथॉन इस मामले को अनुकूलित कर सकता है, इसलिए ओ (एन) समय। दूसरे मामले में संदर्भ संख्या निश्चित रूप से 1 से बढ़ी है (लेकिन वास्तव में कहां?), इसलिए वर्गबद्ध समय। –
@ अश्विनी चौधरी: संदर्भ गणना दोनों मामलों में समान है, लेकिन जैसा कि उत्तर बताता है, पहले मामले के खिलाफ एक विशिष्ट अनुकूलन है, लेकिन दूसरे के खिलाफ नहीं, और यही कारण है कि दूसरे मामले में वर्गिक समय की आवश्यकता होती है। – doublep
@doublep मुझे यकीन नहीं है कि आप संदर्भों की गणना करने की कोशिश कैसे कर रहे हैं, लेकिन '+ =' और ** के दौरान '+ =' और ** से पहले refcount की जांच करें। इसे कम से कम 1. –