কোনও ফাংশনের মূল উদ্দেশ্য থেকে alচ্ছিক কার্যকারিতা ডিকুয়াল করার কোনও অজগর উপায় আছে কি?


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_functionনীতিটির বিপরীতে কিছুটা যায় যে কোনও ফাংশনটিতে কেবল একটি কাজ করা উচিত এবং এখন এর কোডটি স্লাগভাবে দীর্ঘতর এবং আরও জটিল। এই জাতীয় কোনও সাধারণ ক্রিয়াকলাপের জন্য এটি কোনও সমস্যা নয় তবে আমার প্রসঙ্গে আমার একে অপরকে কল করা বেশ জটিল ফাংশন রয়েছে এবং মুদ্রণ বিবৃতিগুলি প্রায়শই এখানে দেখানো থেকে আরও জটিল পদক্ষেপগুলিতে জড়িত, যার ফলে আমার কোডের জটিলতায় যথেষ্ট পরিমাণে বৃদ্ধি ঘটে (একের জন্য আমার ফাংশনগুলির লগিং সম্পর্কিত কোডের আরও লাইন ছিল এর প্রকৃত উদ্দেশ্য সম্পর্কিত লাইনগুলি তুলনায়!)।

তদুপরি, যদি আমি পরে সিদ্ধান্ত নিই যে আমি আর আমার ফাংশনটিতে কোনও মুদ্রণ বিবৃতি চাই না, আমাকে এই কার্যকারিতা সম্পর্কিত কোনও ভেরিয়েবলের পাশাপাশি example_functionসমস্ত printবিবৃতি নিজেই মুছে ফেলতে হবে , যা প্রক্রিয়া ক্লান্তিকর এবং ত্রুটি উভয়ই is -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


প্রশ্ন

এর মূল কার্যকারিতা থেকে মুদ্রণ কার্যকারিতা "ডিকুবল" করার কোনও অজগর উপায় আছে example_function?

আরও সাধারণভাবে, কোনও ফাংশনের মূল উদ্দেশ্য থেকে alচ্ছিক কার্যকারিতা ডিকুয়াল করার অজগর উপায় আছে কি?


আমি এ পর্যন্ত যা চেষ্টা করেছি:

এই মুহুর্তে আমি যে সমাধানটি পেয়েছি তা হ'ল ডেকপলিংয়ের জন্য কলব্যাকগুলি ব্যবহার করছে। উদাহরণস্বরূপ, কেউ এর 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মডিউলটি দেখুন
ক্রিস_আরান্ডস

@ ক্রিস_আর্যান্ডস ঠিক আছে .. লগিং মডিউলটি ব্যবহার করুন .. আপনি লগইনটি চালু এবং বন্ধ করতে পারেন .. নীচের লিঙ্কটি ব্যবহার করুন। stackoverflow.com/questions/2266646/...
Yatish কদম

2
loggingমডিউলটি এখানে কীভাবে সহায়তা করবে তা আমি দেখছি না । যদিও printপ্রসঙ্গটি সেট আপ করার সময় আমার প্রশ্ন বিবৃতিগুলি ব্যবহার করে, আমি আসলে কোনও ফাংশনের মূল উদ্দেশ্য থেকে কোনও ধরণের alচ্ছিক কার্যকারিতা কীভাবে ডিকুয়েল করব তার একটি সমাধান খুঁজছি। উদাহরণস্বরূপ, সম্ভবত আমি জিনিসগুলি রান করার সাথে সাথে প্লট করার জন্য একটি ফাংশন চাই। সেক্ষেত্রে আমি বিশ্বাস করি যে loggingমডিউলটি প্রযোজ্য হবে না।
JLagana

3
@ পাইথোনিক একটি বিশেষণ যা পাইথনের দর্শনকে সমর্থন করার জন্য পাইথন সিনট্যাক্স / স্টাইল / কাঠামো / ব্যবহারকে বর্ণনা করে। এটি কোনও সিনট্যাকটিক বা ডিজাইনের নিয়ম নয়, বরং এমন একটি পদ্ধতির যা একটি পরিষ্কার এবং রক্ষণাবেক্ষণযোগ্য পাইথন কোডবেস উত্পাদন করতে দায়বদ্ধভাবে ধরে রাখা দরকার। আপনার ক্ষেত্রে, কয়েকটি লাইন ট্রেস বা মুদ্রণ বিবৃতি থাকা রক্ষণাবেক্ষণের মানকে যুক্ত করে তারপরে; নিজের উপর কঠোর হবেন না। পূর্বোক্ত যে কোনও পদ্ধতির বিবেচনা করুন যা আপনি আদর্শ বলে মনে করেন।
নাইয়ার

1
এই প্রশ্নটি খুব বিস্তৃত। আমরা সুনির্দিষ্ট প্রশ্নগুলিতে সম্বোধন করতে সক্ষম হতে পারি (প্রদর্শন হিসাবে ব্যবহারের পরামর্শ হিসাবে logging), তবে কীভাবে স্বেচ্ছাসেবক কোড আলাদা করবেন না।
চিপনার

উত্তর:


4

যদি ফাংশনের অভ্যন্তর থেকে ডেটা ব্যবহার করতে আপনার যদি ফাংশনের বাইরে কার্যকারিতা প্রয়োজন হয় তবে এটি সমর্থন করার জন্য ফাংশনের অভ্যন্তরে কিছু মেসেজিং সিস্টেম থাকা দরকার। এই সমস্যা এড়ানোর কোনো উপায় নেই। ফাংশনে স্থানীয় ভেরিয়েবলগুলি বাইরে থেকে সম্পূর্ণ বিচ্ছিন্ন।

লগিং মডিউলটি একটি বার্তা সিস্টেম স্থাপনে বেশ ভাল। এটি কেবল লগ বার্তা মুদ্রণের মধ্যে সীমাবদ্ধ নয় - কাস্টম হ্যান্ডলারগুলি ব্যবহার করে, আপনি যে কোনও কিছু করতে পারেন।

কোনও বার্তা সিস্টেম যুক্ত করা আপনার কলব্যাক উদাহরণের সাথে সমান, কেবল যেখানে 'কলব্যাকস' (লগিং হ্যান্ডলারগুলি) পরিচালনা করা হয় সে জায়গাগুলির ভিতরে example_function (লগারে বার্তাগুলি প্রেরণ করে) যে কোনও জায়গায় নির্দিষ্ট করা যায় except লগিং হ্যান্ডলারগুলির দ্বারা প্রয়োজনীয় যে কোনও ভেরিয়েবলগুলি আপনি বার্তাটি প্রেরণ করার সময় নির্দিষ্ট করা যেতে পারে (আপনি এখনও ব্যবহার করতে পারেন 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তাদের দেওয়া ভেরিয়েবলগুলিই ব্যবহার করতে পারে ।

যদিও লগিং মেসেজিং সিস্টেম হিসাবে ব্যবহার করা যেতে পারে তবে পাইপাবসব-র মতো একটি পূর্ণাঙ্গ মেসেজিং সিস্টেমে চলে যাওয়া ভাল হবে যাতে এটি যে কোনও প্রকৃত লগিংয়ের সাথে হস্তক্ষেপ না করে:

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একবারে দুটি কাজ করার মূল সমস্যাটি এখনও রয়ে গেছে, এর কোডটি আমি যা করতে চাই তার থেকে এটি আরও জটিল করে তোলে।
জেলেগানা

উদাহরণস্বরূপ আমার কলব্যাক পরামর্শের সাথে এটির তুলনা করুন। কলব্যাকগুলি ব্যবহার করে example_functionএখন কেবল একটি কার্যকারিতা রয়েছে এবং মুদ্রণ সামগ্রী (বা আমরা যে কোনও কার্যকারিতা নিতে চাই) এর বাইরে ঘটে।
জেএলগানা

হাই @ জে লাগানা আমার example_functionমুদ্রণ কার্যকারিতা থেকে decoupled - ফাংশন একমাত্র যুক্ত কার্যকারিতা বার্তা প্রেরণ করা হয়। এটি আপনার কলব্যাকের উদাহরণের সাথে সমান, এটি কেবলমাত্র সমস্তটির পরিবর্তে আপনার সুনির্দিষ্ট ভেরিয়েবলগুলি প্রেরণ করে locals()। অতিরিক্ত কার্যকারিতা (মুদ্রণ, গ্রাফিং ইত্যাদি) করণ লগ হ্যান্ডলারের (যা আপনি অন্য কোথাও লগারের সাথে সংযুক্ত করেছেন) up আপনাকে কোনও হ্যান্ডলার সংযুক্ত করার দরকার নেই, সেক্ষেত্রে বার্তা প্রেরণে কিছুই ঘটবে না। এটিকে আরও স্পষ্ট করতে আমি আমার পোস্ট আপডেট করেছি।
RPalmer

আমি সংশোধন হয়ে দাঁড়িয়েছি, আপনার উদাহরণটির মুখ্য কার্যকারিতা থেকে মুদ্রণের কার্যকারিতা ডিক্লুপ করে দিয়েছে example_function। এটি এখন অতিরিক্ত পরিষ্কার করার জন্য ধন্যবাদ! আমি এই উত্তরটি সত্যিই পছন্দ করি, কেবল মূল্য দেওয়া অর্থ হ'ল বার্তা প্রেরণের অতিরিক্ত জটিলতা, যা আপনি উল্লেখ করেছেন, আপত্তিযোগ্য বলে মনে হচ্ছে। পাইপাবসব-এর উল্লেখের জন্য ধন্যবাদ, যা আমাকে পর্যবেক্ষক বিন্যাসে পড়তে পরিচালিত করে
JLagana

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বিবৃতি এবং ফাংশনের শরীরে কোনও যুক্ত যুক্তি যুক্ত করতে হবে।
জেএলগানা

উদাহরণস্বরূপ আমার কলব্যাক পরামর্শের সাথে এটির তুলনা করুন। কলব্যাকগুলি ব্যবহার করে, উদাহরণস্বরূপ: এখন কেবলমাত্র একটি কার্যকারিতা রয়েছে এবং মুদ্রণ সামগ্রী (বা আমরা যে কোনও কার্যকারিতা নিতে চাই) এর বাইরে ঘটে।
জেএলগানা

সবশেষে, আমরা একমত যে গণনার পদক্ষেপগুলি মুদ্রণ করা যদি ব্যবহারকারীর উপকার হয় তবে আমি কোথাও মুদ্রণ আদেশগুলি যুক্ত করে আটকে যাব। তবে আমি এগুলি তাদের example_functionশরীরের বাইরে থাকতে চাই , যাতে এটির জটিলতা কেবল তার মূল কার্যকারিতার জটিলতার সাথে যুক্ত থাকে। আমার এই সমস্ত বাস্তব জীবনের প্রয়োগে, আমার একটি মূল ফাংশন রয়েছে যা ইতিমধ্যে উল্লেখযোগ্য জটিল। এর শরীরে মুদ্রণ / চক্রান্ত / লগিং বিবৃতি যুক্ত করা এটিকে একটি জন্তুতে পরিণত করে যা রক্ষণাবেক্ষণ এবং ডিবাগ করা বেশ চ্যালেঞ্জক ছিল।
জেলেগানা

1

আপনি debug_modeশর্তটি encapsulating একটি ফাংশন সংজ্ঞায়িত করতে পারেন এবং পছন্দসই alচ্ছিক ফাংশন এবং তার যুক্তিগুলি সেই ফাংশনে ( এখানে প্রস্তাবিত হিসাবে ) পাস করতে পারেন :

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। উদাহরণস্বরূপ আমার কলব্যাক পরামর্শের সাথে এটির তুলনা করুন। কলব্যাকগুলি ব্যবহার করে, উদাহরণস্বরূপ: এখন কেবলমাত্র একটি কার্যকারিতা রয়েছে এবং মুদ্রণ সামগ্রী (বা আমরা যে কোনও কার্যকারিতা নিতে চাই) এর বাইরে ঘটে।
জে লাগানা

1

আমি আমার উত্তরটি সরলকরণের সাথে আপডেট করেছি: ফাংশনটি example_functionএকটি একক কলব্যাক বা হুককে একটি ডিফল্ট মান দিয়ে এমনভাবে example_functionপাস করা হয় যে এটি পাস হয়েছে কিনা তা পরীক্ষা করার দরকার নেই:

hook=lambda *args, **kwargs: None

উপরেরটি একটি ল্যাম্বডা এক্সপ্রেশন যা প্রত্যাবর্তন করে Noneএবংexample_functionhook ফাংশনের মধ্যে বিভিন্ন স্থানে অবস্থানিক এবং কীওয়ার্ড প্যারামিটারের সংমিশ্রণের জন্য এই ডিফল্ট মানটিকে কল করতে পারে।

নীচের উদাহরণে, আমি কেবলমাত্র " "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উদাহরণ পেয়ে বার্তাটি লগ করতে পারে । আপনার প্রয়োজন হলে লগিংয়ের সমস্ত nessশ্বর্য থাকতে পারে তবে আপনার প্রয়োজন না থাকলে সরলতা।


উত্তরের জন্য ধন্যবাদ, রোনাল্ড। ফাংশনের বিভিন্ন অংশে কলব্যাকগুলি সম্পাদন করার জন্য কলব্যাক ধারণাটি প্রসারিত করার ধারণাটি (এবং তাদের কাছে একটি প্রসঙ্গের ভেরিয়েবল পাস করা) যাবার পক্ষে সেরা উপায় বলে মনে হচ্ছে indeed কলব্যাকগুলি লিখতে এবং যুক্ত জটিলতায় যুক্তিসঙ্গত মূল্যে এটি আরও সহজ করে তোলে example_function
জেএলগানা

ডিফল্ট মান সহ দুর্দান্ত স্পর্শ; প্রচুর ifবক্তব্য অপসারণ করার এটি সহজ উপায় :)
জেএলগানা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.