प्रसंग
मान लीजिए कि मेरे पास निम्नलिखित पायथन कोड है:
def example_function(numbers, n_iters):
sum_all = 0
for number in numbers:
for _ in range(n_iters):
number = halve(number)
sum_all += number
return sum_all
ns = [1, 3, 12]
print(example_function(ns, 3))
example_functionयहाँ बस nsसूची में तत्वों में से प्रत्येक के माध्यम से जा रहा है और परिणामों को जमा करते हुए उन्हें 3 बार आधा कर रहा है। इस स्क्रिप्ट को चलाने का आउटपुट बस है:
2.0
चूंकि 1 / (2 ^ 3) * (1 + 3 + 12) = 2।
अब, मान लें कि (किसी भी कारण से, शायद डिबगिंग या लॉगिंग के लिए), मैं मध्यवर्ती चरणों के बारे में कुछ प्रकार की जानकारी प्रदर्शित करना चाहूंगा जो कि example_functionले रही है। शायद मैं इस फ़ंक्शन को कुछ इस तरह से फिर से लिखूंगा:
def example_function(numbers, n_iters):
sum_all = 0
for number in numbers:
print('Processing number', number)
for i_iter in range(n_iters):
number = number/2
print(number)
sum_all += number
print('sum_all:', sum_all)
return sum_all
जो अब, जब पहले के समान तर्कों के साथ बुलाया जाता है, तो निम्न आउटपुट करता है:
Processing number 1
0.5
0.25
0.125
sum_all: 0.125
Processing number 3
1.5
0.75
0.375
sum_all: 0.5
Processing number 12
6.0
3.0
1.5
sum_all: 2.0
यह वही है जो मैंने इरादा किया था। हालांकि, यह उस सिद्धांत के खिलाफ एक सा हो जाता है कि एक फ़ंक्शन को केवल एक ही काम करना चाहिए, और अब के लिए कोड example_functionsligthly लंबा और अधिक जटिल है। इस तरह के एक साधारण कार्य के लिए यह कोई समस्या नहीं है, लेकिन मेरे संदर्भ में मेरे पास एक-दूसरे को कॉल करने के लिए काफी जटिल कार्य हैं, और प्रिंटिंग स्टेटमेंट में अक्सर यहां दिखाए गए से अधिक जटिल कदम शामिल होते हैं, जिसके परिणामस्वरूप मेरे कोड की जटिलता में काफी वृद्धि हुई है (एक के लिए) मेरे कार्यों में लॉगिंग से संबंधित कोड की अधिक लाइनें थीं, जबकि इसके वास्तविक उद्देश्य से संबंधित लाइनें थीं!)।
इसके अलावा, अगर मैं बाद में फैसला करता हूं कि मैं अब अपने कार्य में कोई प्रिंटिंग स्टेटमेंट नहीं चाहता हूं, तो मुझे इस कार्यक्षमता से संबंधित किसी भी चर के साथ मैन्युअल रूप से गुजरना होगा example_functionऔर सभी printस्टेटमेंट को हटाना होगा , एक प्रक्रिया जो थकाऊ और त्रुटि दोनों है। -prone।
स्थिति और भी बदतर हो जाती है अगर मैं फ़ंक्शन निष्पादन के दौरान हमेशा मुद्रण या मुद्रण की संभावना नहीं printरखता हूं, जिससे मुझे या तो दो अत्यंत समान कार्यों ( बयानों के साथ , एक के बिना) घोषित करने के लिए अग्रणी होता है, जो बनाए रखने के लिए भयानक है, या कुछ इस तरह परिभाषित करने के लिए:
def example_function(numbers, n_iters, debug_mode=False):
sum_all = 0
for number in numbers:
if debug_mode:
print('Processing number', number)
for i_iter in range(n_iters):
number = number/2
if debug_mode:
print(number)
sum_all += number
if debug_mode:
print('sum_all:', sum_all)
return sum_all
जिसके परिणामस्वरूप एक फूला हुआ और (उम्मीद है) अनावश्यक रूप से जटिल कार्य होता है, यहां तक कि हमारे सरल मामले में भी example_function।
सवाल
वहाँ एक "decouple" मुद्रण कार्यक्षमता के मूल कार्यक्षमता से "pythonic" तरीका है example_function?
आम तौर पर, एक फ़ंक्शन के मुख्य उद्देश्य से वैकल्पिक कार्यक्षमता को डिकूप करने का एक पैथोनिक तरीका है?
मैंने अब तक क्या प्रयास किया है:
फिलहाल मुझे जो समाधान मिला है, वह डिकॉउलिंग के लिए कॉलबैक का उपयोग कर रहा है। उदाहरण के लिए, कोई example_functionइस तरह से फिर से लिख सकता है :
def example_function(numbers, n_iters, callback=None):
sum_all = 0
for number in numbers:
for i_iter in range(n_iters):
number = number/2
if callback is not None:
callback(locals())
sum_all += number
return sum_all
और फिर एक कॉलबैक फ़ंक्शन को परिभाषित करना जो मैं चाहता हूं कि मुद्रण कार्यक्षमता जो भी करता है:
def print_callback(locals):
print(locals['number'])
और example_functionइस तरह कॉलिंग :
ns = [1, 3, 12]
example_function(ns, 3, callback=print_callback)
जो तब आउटपुट:
0.5
0.25
0.125
1.5
0.75
0.375
6.0
3.0
1.5
2.0
यह बेस कार्यक्षमता से मुद्रण कार्यक्षमता को सफलतापूर्वक डिकॉउंड करता है example_function । हालांकि, इस दृष्टिकोण के साथ मुख्य समस्या यह है कि कॉलबैक फ़ंक्शन को केवल एक विशिष्ट भाग में ही चलाया जा सकता है example_function(इस मामले में ठीक वर्तमान संख्या को आधा करने के बाद), और सभी प्रिंटिंग बिल्कुल वहीं होनी है। यह कभी-कभी कॉलबैक फ़ंक्शन के डिज़ाइन को काफी जटिल बनाता है (और कुछ व्यवहारों को प्राप्त करना असंभव बनाता है)।
उदाहरण के लिए, यदि कोई ठीक उसी प्रकार की प्रिंटिंग प्राप्त करना चाहता है जैसा कि मैंने प्रश्न के पिछले भाग में किया था (यह दिखाते हुए कि किस नंबर को संसाधित किया जा रहा है, साथ ही इसके संबंधित पड़ाव के साथ) परिणामस्वरूप कॉलबैक होगा:
def complicated_callback(locals):
i_iter = locals['i_iter']
number = locals['number']
if i_iter == 0:
print('Processing number', number*2)
print(number)
if i_iter == locals['n_iters']-1:
print('sum_all:', locals['sum_all']+number)
जिसके परिणामस्वरूप पहले जैसा ही उत्पादन होता है:
Processing number 1.0
0.5
0.25
0.125
sum_all: 0.125
Processing number 3.0
1.5
0.75
0.375
sum_all: 0.5
Processing number 12.0
6.0
3.0
1.5
sum_all: 2.0
लेकिन लिखने, पढ़ने और बहस करने के लिए एक दर्द है।
loggingयहाँ मॉड्यूल कैसे मदद करेगा। हालांकि मेरा प्रश्न printसंदर्भ स्थापित करते समय बयानों का उपयोग करता है, मैं वास्तव में एक फ़ंक्शन के मुख्य उद्देश्य से किसी भी प्रकार की वैकल्पिक कार्यक्षमता को कैसे कम कर सकता हूं, इसका समाधान ढूंढ रहा हूं। उदाहरण के लिए, हो सकता है कि मैं चीजों को प्लॉट करने के लिए एक फ़ंक्शन चाहता हूं क्योंकि यह चलता है। उस स्थिति में मेरा मानना है कि loggingमॉड्यूल भी लागू नहीं होगा।
loggingप्रदर्शन का उपयोग करने के लिए सुझाव ) को संबोधित करने में सक्षम हो सकते हैं , लेकिन मनमाने कोड को अलग करने के लिए नहीं।
loggingमॉड्यूल की जाँच करें