क्या किसी फ़ंक्शन के मुख्य उद्देश्य से वैकल्पिक कार्यक्षमता को कम करने का एक पायथोनिक तरीका है?


11

प्रसंग

मान लीजिए कि मेरे पास निम्नलिखित पायथन कोड है:

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

लेकिन लिखने, पढ़ने और बहस करने के लिए एक दर्द है।


6
अजगर loggingमॉड्यूल की जाँच करें
— क्रिस_अंड

@Chris_Rands सही है .. लॉगिंग मॉड्यूल का उपयोग करें .. जिस तरह से आप लॉगिंग को चालू और बंद कर सकते हैं .. निम्न लिंक का उपयोग करें। stackoverflow.com/questions/2266646/…
— यतीश कदम

2
मैं यह नहीं देखता कि loggingयहाँ मॉड्यूल कैसे मदद करेगा। हालांकि मेरा प्रश्न printसंदर्भ स्थापित करते समय बयानों का उपयोग करता है, मैं वास्तव में एक फ़ंक्शन के मुख्य उद्देश्य से किसी भी प्रकार की वैकल्पिक कार्यक्षमता को कैसे कम कर सकता हूं, इसका समाधान ढूंढ रहा हूं। उदाहरण के लिए, हो सकता है कि मैं चीजों को प्लॉट करने के लिए एक फ़ंक्शन चाहता हूं क्योंकि यह चलता है। उस स्थिति में मेरा मानना ​​है कि loggingमॉड्यूल भी लागू नहीं होगा।
— JLagana

3
@ पाइथोनिक एक विशेषण है जो अजगर के दर्शन को बनाए रखने के लिए अजगर वाक्य रचना / शैली / संरचना / उपयोग का वर्णन करता है। यह एक सिंटैक्टिक या डिज़ाइन नियम नहीं है, बल्कि एक दृष्टिकोण है जो एक स्वच्छ और बनाए रखने योग्य अजगर कोडबेस का उत्पादन करने के लिए जिम्मेदारी से बनाए रखने की आवश्यकता है। आपके मामले में, ट्रेस या प्रिंट स्टेटमेंट की कुछ पंक्तियों को बनाए रखने के लिए मूल्यों को जोड़ा जाता है, फिर है; अपने आप पर कठोर मत बनो। किसी भी उपरोक्त दृष्टिकोण पर विचार करें जो आपको लगता है कि आदर्श है।
— नायर

1
यह प्रश्न बहुत व्यापक है। हम विशिष्ट प्रश्नों (जैसे loggingप्रदर्शन का उपयोग करने के लिए सुझाव ) को संबोधित करने में सक्षम हो सकते हैं , लेकिन मनमाने कोड को अलग करने के लिए नहीं।
— चेपनर

जवाबों:


4

यदि आपको फ़ंक्शन के अंदर से डेटा का उपयोग करने के लिए फ़ंक्शन के बाहर कार्यक्षमता की आवश्यकता है, तो इसे समर्थन करने के लिए फ़ंक्शन के अंदर कुछ मैसेजिंग सिस्टम होना चाहिए। इसके अलावा कोई रास्ता नहीं है। कार्यों में स्थानीय चर बाहर से पूरी तरह से अलग हैं।

संदेश प्रणाली स्थापित करने में लॉगिंग मॉड्यूल काफी अच्छा है। यह केवल लॉग संदेश को प्रिंट करने तक ही सीमित नहीं है - कस्टम हैंडलर का उपयोग करके, आप कुछ भी कर सकते हैं।

एक संदेश प्रणाली जोड़ना आपके कॉलबैक उदाहरण के समान है, सिवाय इसके कि जिन स्थानों पर 'कॉलबैक' (लॉगिंग हैंडलर) को संभाला जाता है उन्हें example_function (लॉगर को संदेश भेजकर) अंदर कहीं भी निर्दिष्ट किया जा सकता है । लॉगिंग हैंडलर्स द्वारा आवश्यक कोई भी चर निर्दिष्ट किया जा सकता है जब आप संदेश भेजते हैं (आप अभी भी उपयोग कर सकते हैं locals(), लेकिन यह स्पष्ट रूप से आपके द्वारा आवश्यक चर घोषित करने के लिए सबसे अच्छा है)।

एक नया example_functionलग सकता है:

import logging

# Helper function
def send_message(logger, level=logging.DEBUG, **kwargs):
  logger.log(level, "", extra=kwargs)

# Your example function with logging information
def example_function(numbers, n_iters):
    logger = logging.getLogger("example_function")
    # If you have a logging system set up, then we don't want the messages sent here to propagate to the root logger
    logger.propagate = False
    sum_all = 0
    for number in numbers:
        send_message(logger, action="processing", number=number)
        for i_iter in range(n_iters):
            number = number/2
            send_message(logger, action="division", i_iter=i_iter, number=number)
        sum_all += number
        send_message(logger, action="sum", sum=sum_all)
    return sum_all

यह उन तीन स्थानों को निर्दिष्ट करता है जहाँ संदेशों को संभाला जा सकता था। अपने दम पर, यह स्वयं example_functionकी कार्यक्षमता के अलावा कुछ नहीं करेगा example_function। यह कुछ भी प्रिंट नहीं करेगा, या कोई अन्य कार्यक्षमता नहीं करेगा।

में अतिरिक्त कार्यक्षमता जोड़ने के लिए example_function, फिर आपको लकड़हारे को हैंडलर जोड़ने की आवश्यकता होगी।

उदाहरण के लिए, यदि आप भेजे गए चर में से कुछ मुद्रण करना चाहते हैं (आपके debuggingउदाहरण के समान ), तो आप कस्टम हैंडलर को परिभाषित करते हैं, और इसे जोड़ते हैंexample_function लकड़हारे में :

class ExampleFunctionPrinter(logging.Handler):
    def emit(self, record):
        if record.action == "processing":
          print("Processing number {}".format(record.number))
        elif record.action == "division":
          print(record.number)
        elif record.action == "sum":
          print("sum_all: {}".format(record.sum))

example_function_logger = logging.getLogger("example_function")
example_function_logger.setLevel(logging.DEBUG)
example_function_logger.addHandler(ExampleFunctionPrinter())

यदि आप किसी ग्राफ़ पर परिणामों को प्लॉट करना चाहते हैं, तो बस दूसरे हैंडलर को परिभाषित करें:

class ExampleFunctionDivisionGrapher(logging.Handler):
    def __init__(self, grapher):
      self.grapher = grapher

    def emit(self, record):
      if record.action == "division":
        self.grapher.plot_point(x=record.i_iter, y=record.number)

example_function_logger = logging.getLogger("example_function")
example_function_logger.setLevel(logging.DEBUG)
example_function_logger.addHandler(
    ExampleFunctionDivisionGrapher(MyFancyGrapherClass())
)

आप जो भी हैंडलर चाहते हैं उसमें परिभाषित और जोड़ सकते हैं। वे की कार्यक्षमता से पूरी तरह से अलग होंगेexample_function , और केवल उन चर का उपयोग कर सकते हैं जो example_functionउन्हें देता है।

हालाँकि लॉगिंग का उपयोग मैसेजिंग सिस्टम के रूप में किया जा सकता है, लेकिन PyPubSub जैसे पूर्णतः मैसेजिंग सिस्टम में जाना बेहतर हो सकता है , ताकि यह किसी भी वास्तविक लॉगिंग में हस्तक्षेप न करे जो आप कर रहे हैं:

from pubsub import pub

# Your example function
def example_function(numbers, n_iters):
    sum_all = 0
    for number in numbers:
        pub.sendMessage("example_function.processing", number=number)
        for i_iter in range(n_iters):
            number = number/2
            pub.sendMessage("example_function.division", i_iter=i_iter, number=number)
        sum_all += number
        pub.sendMessage("example_function.sum", sum=sum_all)
    return sum_all

# If you need extra functionality added in, then subscribe to the messages.
# Otherwise nothing will happen, other than the normal example_function functionality.
def handle_example_function_processing(number):
    print("Processing number {}".format(number))

def handle_example_function_division(i_iter, number):
    print(number)

def handle_example_function_sum(sum):
    print("sum_all: {}".format(sum))

pub.subscribe(
    "example_function.processing",
    handle_example_function_processing
)
pub.subscribe(
    "example_function.division",
    handle_example_function_division
)
pub.subscribe(
    "example_function.sum",
    handle_example_function_sum
)

उत्तर के लिए धन्यवाद, RPalmer। loggingमॉड्यूल का उपयोग करके आपके द्वारा प्रदान किया गया कोड वास्तव में मेरे द्वारा उपयोग किए गए printऔर ifबयानों की तुलना में अधिक संगठित और बनाए रखने योग्य है । हालांकि, यह फ़ंक्शन की मुख्य कार्यक्षमता से मुद्रण कार्यक्षमता को कम नहीं करता है example_function। यही है, example_functionएक बार में दो काम करने की मुख्य समस्या अभी भी बनी हुई है, इसके कोड को जितना मैं चाहता हूं उससे अधिक जटिल बना सकता है।
— JLagana

उदाहरण के लिए मेरे कॉलबैक सुझाव के साथ इसकी तुलना करें। कॉलबैक का उपयोग करते हुए, example_functionअब केवल एक कार्यक्षमता है, और मुद्रण सामग्री (या जो भी अन्य कार्यक्षमता हमारे पास है) इसके बाहर होती है।
— JLagana

हाय @ जनलाना। मेरा example_functionमुद्रण कार्यशीलता से विघटित है - फ़ंक्शन के लिए केवल अतिरिक्त कार्यक्षमता संदेश भेज रही है। यह आपके कॉलबैक उदाहरण के समान है, सिवाय इसके कि यह केवल विशिष्ट चर भेजता है जो आप चाहते हैं, बल्कि सभी के बजाय locals()। यह अतिरिक्त कार्यशीलता (मुद्रण, रेखांकन, आदि) करने के लिए लॉग हैंडलर (जो आप लकड़हारे को कहीं और देते हैं) पर निर्भर है। आपको किसी भी हैंडलर को अटैच करने की आवश्यकता नहीं है, जिस स्थिति में संदेश भेजे जाने पर कुछ नहीं होगा। मैंने इसे और अधिक स्पष्ट करने के लिए अपनी पोस्ट अपडेट की है।
— आरपीएलमेर

मैं सही खड़ा हूं, आपके उदाहरण ने मुद्रण की कार्यक्षमता को मुख्य कार्यक्षमता से अलग कर दिया example_function। इसे अतिरिक्त स्पष्ट करने के लिए धन्यवाद! मैं वास्तव में इस जवाब को पसंद करता हूं, भुगतान किया जा रहा एकमात्र मूल्य पासिंग संदेशों की जोड़ा जटिलता है, जो कि, जैसा कि आपने उल्लेख किया है, अपरिहार्य प्रतीत होता है। PyPubSub के संदर्भ के लिए भी धन्यवाद, जिसने मुझे पर्यवेक्षक पैटर्न पर पढ़ने के लिए प्रेरित किया ।
— जालना

1

यदि आप सिर्फ प्रिंट स्टेटमेंट के साथ रहना चाहते हैं, तो आप एक डेकोरेटर का उपयोग कर सकते हैं जो एक तर्क जोड़ता है जो प्रिंटिंग को कंसोल को चालू / बंद करता है।

यहां एक डेकोरेटर है जो कीवर्ड-केवल तर्क और verbose=Falseकिसी फ़ंक्शन के डिफ़ॉल्ट मान को जोड़ता है , डॉकस्ट्रिंग और हस्ताक्षर को अपडेट करता है। फ़ंक्शन को कॉल करना-के रूप में अपेक्षित आउटपुट देता है। फ़ंक्शन को कॉल करने verbose=Trueसे प्रिंट स्टेटमेंट चालू होंगे और अपेक्षित आउटपुट वापस आएगा। यह एक if debug:ब्लॉक के साथ हर प्रिंट प्रस्तुत करने के लिए नहीं होने का अतिरिक्त लाभ है ।

from functools import wraps
from inspect import cleandoc, signature, Parameter
import sys
import os

def verbosify(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        def toggle(*args, verbose=False, **kwargs):
            if verbose:
                _stdout = sys.stdout
            else:
                _stdout = open(os.devnull, 'w')
            with redirect_stdout(_stdout):
                return func(*args, **kwargs)
        return toggle(*args, **kwargs)
    # update the docstring
    doc = '\n\nOption:\n-------\nverbose : bool\n    '
    doc += 'Turns on/off print lines in the function.\n '
    wrapper.__doc__ = cleandoc(wrapper.__doc__ or '\n') + doc
    # update the function signature to include the verbose keyword
    sig = signature(func)
    param_verbose = Parameter('verbose', Parameter.KEYWORD_ONLY, default=False)
    sig_params = tuple(sig.parameters.values()) + (param_verbose,)
    sig = sig.replace(parameters=sig_params)
    wrapper.__signature__ = sig
    return wrapper

अपने फ़ंक्शन को रैप करने से अब आप उपयोग करके प्रिंट कार्यों को चालू / बंद कर सकते हैं verbose।

@verbosify
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

उदाहरण:

example_function([1,3,12], 3)
# returns:
2.0

example_function([1,3,12], 3, verbose=True)
# returns/prints:
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
2.0

जब आप निरीक्षण करते हैं example_function, तो आप अद्यतन प्रलेखन को भी देखेंगे। चूँकि आपके फ़ंक्शन में डॉकस्ट्रिंग नहीं है, यह सिर्फ डेकोरेटर में है।

help(example_function)
# prints:
Help on function example_function in module __main__:

example_function(numbers, n_iters, *, verbose=False)
    Option:
    -------
    verbose : bool
        Turns on/off print lines in the function.

कोडिंग दर्शन के संदर्भ में। समारोह है कि दुष्प्रभाव नहीं होने के बाद एक कार्यात्मक प्रोग्रामिंग प्रतिमान है। अजगर कर सकता है एक कार्यात्मक भाषा सकती है, लेकिन इसे विशेष रूप से इस तरह से डिज़ाइन नहीं किया गया है। मैं हमेशा उपयोगकर्ता को ध्यान में रखते हुए अपना कोड डिजाइन करता हूं।

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


उत्तर के लिए धन्यवाद, जेम्स। प्रदान किए गए कोड वास्तव में मेरे द्वारा प्रस्तावित printऔर उपयोग ifकिए गए बयानों की तुलना में अधिक संगठित और बनाए रखने योग्य हैं । इसके अलावा, यह वास्तव में example_functionमुख्य कार्यक्षमता से मुद्रण की कार्यक्षमता का हिस्सा कम करने का प्रबंधन करता है , जो बहुत अच्छा था (मुझे यह भी पसंद है कि डेकोरेटर स्वचालित रूप से डॉकस्ट्रिंग, गुड टच को जोड़ता है)। हालाँकि, यह मुख्य कार्यक्षमता से मुद्रण कार्यक्षमता को पूरी तरह से कम नहीं करता है example_function: आपको अभी भी printफ़ंक्शन और बॉडी के साथ किसी भी तर्क को जोड़ना होगा ।
— JLagana

उदाहरण के लिए मेरे कॉलबैक सुझाव के साथ इसकी तुलना करें। कॉलबैक का उपयोग करते हुए, example_function में अब केवल एक कार्यक्षमता है, और मुद्रण सामग्री (या जो भी अन्य कार्यक्षमता हमारे पास है) उसके बाहर होती है।
— JLagana

अंत में, हम इस बात से सहमत हैं कि यदि गणना चरणों को प्रिंट करना उपयोगकर्ता के लिए एक लाभ है, तो मैं कहीं न कहीं मुद्रण आदेशों को जोड़ने के साथ अटक जाऊंगा। लेकिन मैं चाहता हूं कि वे example_functionशरीर के बाहर हों , ताकि यह जटिलता केवल इसकी मुख्य कार्यक्षमता की जटिलता से जुड़ी रहे। इस सब के मेरे वास्तविक जीवन के आवेदन में, मेरा एक मुख्य कार्य है जो पहले से ही काफी जटिल है। अपने शरीर में प्रिंटिंग / प्लॉटिंग / लॉगिंग स्टेटमेंट को जोड़ने से यह एक ऐसा जानवर बन जाता है जिसे बनाए रखना और डिबग करना काफी चुनौतीपूर्ण हो गया है।
— JLagana

1

आप debug_modeशर्त को एनकैप्सुलेट करने वाले फ़ंक्शन को परिभाषित कर सकते हैं और उस फ़ंक्शन के लिए वांछित वैकल्पिक फ़ंक्शन और उसके तर्क पास कर सकते हैं (जैसा कि यहां बताया गया है ):

def DEBUG(function, *args):
    if debug_mode:
        function(*args)

def example_function(numbers, n_iters):
    sum_all = 0
    for number in numbers:
        DEBUG(print, 'Processing number', number)
        for i_iter in range(n_iters):
            number = number/2
            DEBUG(print, number)
        sum_all += number
        DEBUG(print, 'sum_all:', sum_all)
    return sum_all

ns = [1, 3, 12]
debug_mode = True
print(example_function(ns, 3))

ध्यान दें कि debug_mode स्पष्ट रूप से कॉल करने से पहले एक मान निर्दिष्ट किया जाना चाहिए DEBUG।

इसके अलावा अन्य कार्यों को लागू करना संभव है print।

के लिए एक संख्यात्मक मान का उपयोग करके आप इस अवधारणा को कई डीबग स्तरों तक भी बढ़ा सकते हैं debug_mode।


उत्तर के लिए धन्यवाद, गर्ड। वास्तव में आपके समाधान को ifसभी जगह बयानों की आवश्यकता से छुटकारा मिल जाता है, और मुद्रण को चालू और बंद करना भी आसान हो जाता है। हालाँकि, यह मुख्य कार्यक्षमता से मुद्रण कार्यक्षमता को कम नहीं करता है example_function। उदाहरण के लिए मेरे कॉलबैक सुझाव के साथ इसकी तुलना करें। कॉलबैक का उपयोग करते हुए, example_function में अब केवल एक कार्यक्षमता है, और मुद्रण सामग्री (या जो भी अन्य कार्यक्षमता हमारे पास है) उसके बाहर होती है।
— JLagana

1

मैंने अपने उत्तर को एक सरलीकरण के साथ अद्यतन किया है: फ़ंक्शन example_functionको एक एकल कॉलबैक या हुक को डिफ़ॉल्ट मान के साथ पारित किया जाता है जैसे कि example_functionअब यह देखने के लिए परीक्षण करने की आवश्यकता नहीं है कि यह पारित किया गया था या नहीं:

hook=lambda *args, **kwargs: None

उपरोक्त एक लंबोदर अभिव्यक्ति है जो लौटती है Noneऔर फ़ंक्शन के भीतर विभिन्न स्थानों पर स्थितीय और कीवर्ड मापदंडों के संयोजन के साथ example_functionइस डिफ़ॉल्ट मान को कॉल कर सकती है hook।

नीचे दिए गए उदाहरण में, मैं केवल " "end_iteration"और "result" घटनाओं में दिलचस्पी रखता हूं ।

def example_function(numbers, n_iters, hook=lambda *args, **kwargs: None):
    hook("init")
    sum_all = 0
    for number in numbers:
        for i_iter in range(n_iters):
            hook("start_iteration", number)
            number = number/2
            hook("end_iteration", number)
        sum_all += number
    hook("result", sum_all)
    return sum_all

if __name__ == '__main__':
    def my_hook(event_type, *args):
        if event_type in ["end_iteration", "result"]:
            print(args[0])

    print('sum = ', example_function([1, 3, 12], 3))
    print('sum = ', example_function([1, 3, 12], 3, my_hook))

प्रिंटों:

sum =  2.0
0.5
0.25
0.125
1.5
0.75
0.375
6.0
3.0
1.5
2.0
sum =  2.0

हुक फ़ंक्शन जितना चाहें उतना सरल या विस्तृत हो सकता है। यहां यह ईवेंट प्रकार की जांच कर रहा है और एक साधारण प्रिंट कर रहा है। लेकिन यह एक प्राप्त कर सकता हैlogger उदाहरण और संदेश को लॉग इन । यदि आपको इसकी आवश्यकता है, तो आपको लॉगिंग की सभी समृद्धि हो सकती है, लेकिन यदि आप इसे सरल नहीं करते हैं।


जवाब के लिए धन्यवाद, रोनाल्ड। फ़ंक्शन के विभिन्न भागों में कॉलबैक निष्पादित करने के लिए कॉलबैक विचार को विस्तारित करने का विचार (और उनके लिए एक संदर्भ चर को पारित करना) वास्तव में जाने का सबसे अच्छा तरीका लगता है। इससे कॉलबैक लिखना बहुत आसान हो जाता है और उचित जटिलता में जोड़ा जटिलता में हो जाता है example_function।
— JLagana

डिफ़ॉल्ट मान के साथ अच्छा स्पर्श; बहुत सारे ifबयानों को हटाने का यह एक सरल तरीका है :)
— JLagana
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.