स्पष्टीकरण
यहां मुद्दा यह है कि i
फ़ंक्शन f
के निर्माण के समय का मान सहेजा नहीं जाता है। बल्कि, जब यह कहा जाता है f
के मूल्य को देखता i
है ।
यदि आप इसके बारे में सोचते हैं, तो यह व्यवहार पूर्ण समझ में आता है। वास्तव में, यह एकमात्र उचित तरीका कार्य कर सकता है। कल्पना कीजिए कि आपके पास एक ऐसा फ़ंक्शन है जो वैश्विक चर को एक्सेस करता है, जैसे:
global_var = 'foo'
def my_function():
print(global_var)
global_var = 'bar'
my_function()
जब आप इस कोड को पढ़ते हैं, तो आप निश्चित रूप से - "बार" प्रिंट करने की अपेक्षा करेंगे, न कि "फू", क्योंकि global_var
फ़ंक्शन घोषित होने के बाद मूल्य बदल गया है। आपके अपने कोड में भी यही बात हो रही है: जब तक आप कॉल करते हैं f
, तब तक मान i
बदल गया है और सेट हो गया है2
।
समाधान
वास्तव में इस समस्या को हल करने के कई तरीके हैं। यहाँ कुछ विकल्प दिए गए हैं:
i
डिफ़ॉल्ट तर्क के रूप में इसका उपयोग करके बाध्यकारी को जल्दी से बाध्य करें
क्लोजर वैरिएबल्स (जैसे i
) के विपरीत , फंक्शन को परिभाषित करने पर डिफ़ॉल्ट तर्कों का तुरंत मूल्यांकन किया जाता है:
for i in range(3):
def f(i=i): # <- right here is the important bit
return i
functions.append(f)
यह कैसे / क्यों काम करता है, इस बारे में थोड़ी जानकारी देने के लिए: फ़ंक्शन के डिफ़ॉल्ट तर्कों को फ़ंक्शन की विशेषता के रूप में संग्रहीत किया जाता है; इस प्रकार वर्तमान मूल्य का i
स्नैपशॉट लिया और सहेजा गया है।
>>> i = 0
>>> def f(i=i):
... pass
>>> f.__defaults__ # this is where the current value of i is stored
(0,)
>>> # assigning a new value to i has no effect on the function's default arguments
>>> i = 5
>>> f.__defaults__
(0,)
i
एक क्लोजर में वर्तमान मूल्य पर कब्जा करने के लिए एक फ़ंक्शन फ़ैक्टरी का उपयोग करें
आपकी समस्या की जड़ i
एक परिवर्तनशील चर है। हम इस समस्या के इर्द-गिर्द एक और वैरिएबल बनाकर काम कर सकते हैं जो कभी नहीं बदलने की गारंटी है - और ऐसा करने का सबसे आसान तरीका एक बंद है :
def f_factory(i):
def f():
return i # i is now a *local* variable of f_factory and can't ever change
return f
for i in range(3):
f = f_factory(i)
functions.append(f)
का प्रयोग करें functools.partial
के वर्तमान मूल्य बाध्य करने i
के लिएf
functools.partial
आपको किसी मौजूदा फ़ंक्शन में तर्क संलग्न करने देता है। एक तरह से, यह भी एक प्रकार का फ़ंक्शन कारखाना है।
import functools
def f(i):
return i
for i in range(3):
f_with_i = functools.partial(f, i) # important: use a different variable than "f"
functions.append(f_with_i)
चेतावनी: ये समाधान केवल काम आप यदि आवंटित चर करने के लिए एक नया मान। यदि आप चर में संग्रहित वस्तु को संशोधित करते हैं, तो आप फिर से उसी समस्या का अनुभव करेंगे:
>>> i = [] # instead of an int, i is now a *mutable* object
>>> def f(i=i):
... print('i =', i)
...
>>> i.append(5) # instead of *assigning* a new value to i, we're *mutating* it
>>> f()
i = [5]
ध्यान दें कि i
हम इसे एक डिफ़ॉल्ट तर्क में बदल देने के बावजूद कैसे बदल गए हैं! यदि आपका कोड उत्परिवर्तित होता है i
, तो आपको अपने फ़ंक्शन की एक प्रति बांधनी चाहिए i
, जैसे:
def f(i=i.copy()):
f = f_factory(i.copy())
f_with_i = functools.partial(f, i.copy())