কীভাবে পাইথন ডেকোরেটর তৈরি করবেন যা প্যারামিটারগুলির সাথে বা ছাড়াই ব্যবহার করা যেতে পারে?


90

আমি পাইথন ডেকরেটর তৈরি করতে চাই যা পরামিতিগুলির সাহায্যে ব্যবহার করা যেতে পারে:

@redirect_output("somewhere.log")
def foo():
    ....

বা তাদের ছাড়াই (উদাহরণস্বরূপ আউটপুটটি স্ট্যান্ডারকে ডিফল্টরূপে পুনর্নির্দেশ করতে):

@redirect_output
def foo():
    ....

এটা কি আদৌ সম্ভব?

নোট করুন যে আমি আউটপুট পুনঃনির্দেশের সমস্যার জন্য আলাদা সমাধান খুঁজছি না, এটি আমি যে বাক্য গঠনটি অর্জন করতে চাই তার একটি উদাহরণ মাত্র।


ডিফল্ট- @redirect_outputচেহারাটি লক্ষণীয়ভাবে অযৌক্তিক। আমি পরামর্শ দিচ্ছি যে এটি একটি খারাপ ধারণা। প্রথম ফর্মটি ব্যবহার করুন এবং আপনার জীবনকে অনেক সহজ করুন।
এস .লট

যদিও আকর্ষণীয় প্রশ্ন - যতক্ষণ না আমি এটি দেখেছি এবং ডকুমেন্টেশনটি দেখেছি, আমি ধরে নিয়েছি যে @f @f (@f) এর সমান ছিল, এবং আমি এখনও মনে করি এটি হওয়া উচিত, সত্য হওয়া উচিত (যে কোনও সরবরাহিত যুক্তি কেবল সামলে নেওয়া হবে) ফাংশন যুক্তি দিকে)
Rog

উত্তর:


68

আমি জানি এই প্রশ্নটি পুরানো, তবে কয়েকটি মন্তব্য নতুন, এবং বাস্তবের সমস্ত সমাধান মূলত একইরকম, বেশিরভাগই খুব পরিষ্কার বা পড়া সহজ নয় to

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

def decorator(*args, **kwargs):
    if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
        # called as @decorator
    else:
        # called as @decorator(*args, **kwargs)

প্রথম ক্ষেত্রে, আপনি কোনও সাধারণ সাজসজ্জাকারী যা করেন তা করেন, ফাংশনটিতে পাসের একটি পরিবর্তিত বা মোড়ানো সংস্করণটি ফিরিয়ে দিন।

দ্বিতীয় ক্ষেত্রে, আপনি একটি 'নতুন' সাজসজ্জা ফিরিয়ে দেন যা কোনওভাবে * আরগস, ** কাওয়ারগুলি দিয়ে দেওয়া তথ্য ব্যবহার করে।

এটি বেশ ভাল এবং সমস্ত, তবে আপনার তৈরি প্রতিটি সজ্জাকারীর জন্য এটি লিখে রাখলে এটি খুব বিরক্তিকর হতে পারে এবং পরিষ্কার হিসাবে নয়। পরিবর্তে, আমাদের সাজসজ্জারগুলিকে পুনরায় না লিখে স্বয়ংক্রিয়ভাবে সংশোধন করতে সক্ষম হবেন ... তবে সাজসজ্জারের জন্য এটিই!

নিম্নলিখিত সাজসজ্জার সজ্জা ব্যবহার করে, আমরা আমাদের সাজসজ্জারকে deocrate করতে পারি যাতে সেগুলি যুক্তি ছাড়াই বা ছাড়াই ব্যবহার করা যায়:

def doublewrap(f):
    '''
    a decorator decorator, allowing the decorator to be used as:
    @decorator(with, arguments, and=kwargs)
    or
    @decorator
    '''
    @wraps(f)
    def new_dec(*args, **kwargs):
        if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
            # actual decorated function
            return f(args[0])
        else:
            # decorator arguments
            return lambda realf: f(realf, *args, **kwargs)

    return new_dec

এখন, আমরা @ ডাবলাপ্র্যাপ দিয়ে আমাদের সাজসজ্জাগুলি সাজাতে পারি, এবং তারা একটি সতর্কতার সাথে যুক্তি ছাড়াই এবং ছাড়াই কাজ করবে:

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

নিম্নলিখিতগুলি এর ব্যবহারটি দেখায়:

def test_doublewrap():
    from util import doublewrap
    from functools import wraps    

    @doublewrap
    def mult(f, factor=2):
        '''multiply a function's return value'''
        @wraps(f)
        def wrap(*args, **kwargs):
            return factor*f(*args,**kwargs)
        return wrap

    # try normal
    @mult
    def f(x, y):
        return x + y

    # try args
    @mult(3)
    def f2(x, y):
        return x*y

    # try kwargs
    @mult(factor=5)
    def f3(x, y):
        return x - y

    assert f(2,3) == 10
    assert f2(2,5) == 30
    assert f3(8,1) == 5*7

31

ডিফল্ট মানগুলির সাথে কীওয়ার্ড আর্গুমেন্টগুলি ব্যবহার করা (কোকুইন প্রস্তাবিত হিসাবে) একটি ভাল ধারণা, তবে আপনার প্রথম বন্ধনী অন্তর্ভুক্ত করার প্রয়োজন হবে:

@redirect_output()
def foo():
    ...

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

আপনি যদি পাইথন 3.0.০ ব্যবহার করে থাকেন তবে আপনি এর জন্য কেবল কীওয়ার্ড যুক্তিই ব্যবহার করতে পারেন:

def redirect_output(fn=None,*,destination=None):
  destination = sys.stderr if destination is None else destination
  def wrapper(*args, **kwargs):
    ... # your code here
  if fn is None:
    def decorator(fn):
      return functools.update_wrapper(wrapper, fn)
    return decorator
  else:
    return functools.update_wrapper(wrapper, fn)

পাইথন ২.x এ এটি ভারারাজ ট্রিকস দ্বারা অনুকরণ করা যায়:

def redirected_output(*fn,**options):
  destination = options.pop('destination', sys.stderr)
  if options:
    raise TypeError("unsupported keyword arguments: %s" % 
                    ",".join(options.keys()))
  def wrapper(*args, **kwargs):
    ... # your code here
  if fn:
    return functools.update_wrapper(wrapper, fn[0])
  else:
    def decorator(fn):
      return functools.update_wrapper(wrapper, fn)
    return decorator

এই সংস্করণের যে কোনওটি আপনাকে এই জাতীয় কোড লেখার অনুমতি দেয়:

@redirected_output
def foo():
    ...

@redirected_output(destination="somewhere.log")
def bar():
    ...

4
আপনি কি রাখা your code here? সজ্জিত ফাংশনটিকে আপনি কীভাবে কল করবেন? fn(*args, **kwargs)কাজ করে না
lum

আমি মনে করি অনেক সহজ উত্তর আছে, একটি ক্লাস তৈরি করুন যা whichচ্ছিক যুক্তি দিয়ে সজ্জিত করবে। পূর্বনির্ধারিত একই আর্গুমেন্টের সাথে অন্য একটি ফাংশন তৈরি করুন এবং সাজসজ্জা শ্রেণীর একটি নতুন উদাহরণ ফিরে আসুন। এর মতো কিছু দেখতে পাওয়া উচিত: def f(a = 5): return MyDecorator( a = a) এবং class MyDecorator( object ): def __init__( self, a = 5 ): .... দুঃখিত এটির মন্তব্যটিতে এটি কঠোরভাবে লেখা তবে আমি আশা করি এটি বুঝতে যথেষ্ট সহজ
ওমর বেন হাইম

17

আমি জানি এটি একটি পুরানো প্রশ্ন, তবে আমি প্রস্তাবিত কোনও কৌশলই সত্যই পছন্দ করি না তাই আমি অন্য পদ্ধতি যুক্ত করতে চাই। আমি দেখেছি যে জাঙ্গো তাদের ডেকরেটারে একটি সত্যিই পরিষ্কার পদ্ধতি ব্যবহার login_requiredকরেdjango.contrib.auth.decorators । আপনি যেমন সাজসজ্জার দস্তাবেজগুলিতে দেখতে পাচ্ছেন , এটি একা হিসাবে @login_requiredবা তর্ক হিসাবে ব্যবহার করা যেতে পারে ,@login_required(redirect_field_name='my_redirect_field') ,।

তারা এটি করার উপায়টি বেশ সহজ। তারা তাদের সাজসজ্জার যুক্তির আগে একটি kwarg( function=None) যুক্ত করে। যদি সাজসজ্জার একা ব্যবহৃত হয়, functionতবে এটি আসল ফাংশনটি এটি সজ্জিত করা হবে, অন্যদিকে যদি এটি যুক্তি দিয়ে ডাকা হয় তবে তা functionহবে None

উদাহরণ:

from functools import wraps

def custom_decorator(function=None, some_arg=None, some_other_arg=None):
    def actual_decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            # Do stuff with args here...
            if some_arg:
                print(some_arg)
            if some_other_arg:
                print(some_other_arg)
            return f(*args, **kwargs)
        return wrapper
    if function:
        return actual_decorator(function)
    return actual_decorator

@custom_decorator
def test1():
    print('test1')

>>> test1()
test1

@custom_decorator(some_arg='hello')
def test2():
    print('test2')

>>> test2()
hello
test2

@custom_decorator(some_arg='hello', some_other_arg='world')
def test3():
    print('test3')

>>> test3()
hello
world
test3

আমি এই পদ্ধতির সন্ধান করি যা জাঙ্গো এখানে প্রস্তাবিত অন্যান্য কৌশলগুলির চেয়ে আরও মার্জিত এবং বোঝার জন্য ব্যবহার করে।


হ্যাঁ, আমি এই পদ্ধতিটি পছন্দ করি। নোট কি আপনি যে আছে যখন প্রসাধক অন্যথায় প্রথম অবস্থানগত ARG নির্ধারিত হয় কলিং kwargs ব্যবহার করতে functionএবং তারপর কিছু বিরতি কারণ প্রসাধক চেষ্টা যে প্রথম অবস্থানগত ARG ডাকতে যেমন যদি এটা আপনার সজ্জিত ফাংশন ছিলেন।
ডাস্টিন ওয়াইট

12

আপনাকে উভয় কেস সনাক্ত করতে হবে, উদাহরণস্বরূপ প্রথম আর্গুমেন্টের ধরণ ব্যবহার করে এবং তদনুসারে মোড়ক (পরামিতি ছাড়াই ব্যবহার করা হয়) বা ডিকোরেটার (যখন যুক্তির সাথে ব্যবহার করা হয়) ফিরে আসুন।

from functools import wraps
import inspect

def redirect_output(fn_or_output):
    def decorator(fn):
        @wraps(fn)
        def wrapper(*args, **args):
            # Redirect output
            try:
                return fn(*args, **args)
            finally:
                # Restore output
        return wrapper

    if inspect.isfunction(fn_or_output):
        # Called with no parameter
        return decorator(fn_or_output)
    else:
        # Called with a parameter
        return decorator

@redirect_output("output.log")সিনট্যাক্স ব্যবহার করার সময় , redirect_outputএকটি একক যুক্তি দিয়ে ডাকা হয় "output.log"এবং এটি অবশ্যই একটি যুক্তিযুক্ত হিসাবে সজ্জিত করার জন্য ফাংশন গ্রহণ করে ডেকরেটরকে ফিরে আসতে হবে। যখন হিসাবে ব্যবহার করা হয়@redirect_output , এটি যুক্তি হিসাবে সাজাইয়া ফাংশন সঙ্গে সরাসরি বলা হয়।

বা অন্য কথায়: দ্য @ বাক্য গঠনটি অবশ্যই একটি অভিব্যক্তি দ্বারা অনুসরণ করা আবশ্যক যার ফলস্বরূপ একটি ক্রিয়াকলাপটি কোনও ক্রিয়াকে তার একমাত্র যুক্তি হিসাবে সজ্জিত করা এবং সজ্জিত ফাংশনটি ফিরিয়ে দেওয়া is এক্সপ্রেশন নিজেই একটি ফাংশন কল হতে পারে, যা ক্ষেত্রে এটি @redirect_output("output.log")। কনভোলিউটেড তবে সত্য :-)


9

এখানে বেশ কয়েকটি উত্তর ইতিমধ্যে সুন্দরভাবে আপনার সমস্যার সমাধান করেছে। শৈলীর প্রতি সম্মানের সাথে, তবে আমি functools.partialডেভিড বেজলির পাইথন কুকবুক 3- এ যেমন পরামর্শ দিয়েছি তেমন ব্যবহার করে এই সাজসজ্জার যন্ত্রটি সমাধান করা পছন্দ করি :

from functools import partial, wraps

def decorator(func=None, foo='spam'):
    if func is None:
         return partial(decorator, foo=foo)

    @wraps(func)
    def wrapper(*args, **kwargs):
        # do something with `func` and `foo`, if you're so inclined
        pass

    return wrapper

হ্যাঁ, আপনি ঠিক করতে পারেন

@decorator()
def f(*args, **kwargs):
    pass

চটকদার কাজগুলি ব্যতীত, আমি এটি দেখতে অদ্ভুত লাগছে এবং আমি কেবল সজ্জিত করার বিকল্প থাকা পছন্দ করি @decorator

গৌণ মিশনের উদ্দেশ্য হিসাবে, কোনও ফাংশনের আউটপুট পুনর্নির্দেশের বিষয়টি এই স্ট্যাক ওভারফ্লো পোস্টে সম্বোধন করা হয়েছে ।


আপনি যদি আরও গভীরভাবে ডুব দিতে চান তবে পাইথন কুকবুক 3-এর অধ্যায় 9 (মেটাগ্রোগ্রামিং) দেখুন , যা অনলাইনে পড়ার জন্য অবাধে উপলভ্য ।

বেজলির দুর্দান্ত ভিডিওটি পাইথন 3 মেটাপ্রোগ্র্যামিং-এ কিছু উপাদান সরাসরি ডেমোড করা হয়েছে (আরও বেশি!)

শুভ কোডিং :)


8

অজগর সাজসজ্জারকে আপনি আর্গুমেন্ট দেন কিনা তা নির্ভর করে মৌলিকভাবে আলাদাভাবে ডাকা হয়। সাজসজ্জা আসলে একটি মাত্র (সিনট্যাক্টিক্যালি সীমাবদ্ধ) এক্সপ্রেশন।

আপনার প্রথম উদাহরণে:

@redirect_output("somewhere.log")
def foo():
    ....

ফাংশনটি redirect_outputপ্রদত্ত যুক্তি দিয়ে ডাকা হয়, যা একটি সজ্জা ফাংশন ফেরত প্রত্যাশিত হয়, যা নিজেই fooআর্গুমেন্ট হিসাবে ডাকা হয় , যা (শেষ অবধি) চূড়ান্ত সজ্জিত ফাংশনটি প্রত্যাশিত বলে প্রত্যাশিত।

সমতুল্য কোডটি এর মতো দেখায়:

def foo():
    ....
d = redirect_output("somewhere.log")
foo = d(foo)

আপনার দ্বিতীয় উদাহরণের সমতুল্য কোডটি দেখে মনে হচ্ছে:

def foo():
    ....
d = redirect_output
foo = d(foo)

সুতরাং আপনি যা করতে চান তা করতে পারেন তবে সম্পূর্ণ নির্বিঘ্নে:

import types
def redirect_output(arg):
    def decorator(file, f):
        def df(*args, **kwargs):
            print 'redirecting to ', file
            return f(*args, **kwargs)
        return df
    if type(arg) is types.FunctionType:
        return decorator(sys.stderr, arg)
    return lambda f: decorator(arg, f)

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

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

@redirect_output()
def foo():
    ....

সাজসজ্জার ফাংশন কোডটি দেখতে এরকম হবে:

def redirect_output(file = sys.stderr):
    def decorator(file, f):
        def df(*args, **kwargs):
            print 'redirecting to ', file
            return f(*args, **kwargs)
        return df
    return lambda f: decorator(file, f)

2

আসলে, @ bj0 এর সমাধানে ক্যাভিয়েট কেস সহজেই পরীক্ষা করা যায়:

def meta_wrap(decor):
    @functools.wraps(decor)
    def new_decor(*args, **kwargs):
        if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
            # this is the double-decorated f. 
            # Its first argument should not be a callable
            doubled_f = decor(args[0])
            @functools.wraps(doubled_f)
            def checked_doubled_f(*f_args, **f_kwargs):
                if callable(f_args[0]):
                    raise ValueError('meta_wrap failure: '
                                'first positional argument cannot be callable.')
                return doubled_f(*f_args, **f_kwargs)
            return checked_doubled_f 
        else:
            # decorator arguments
            return lambda real_f: decor(real_f, *args, **kwargs)

    return new_decor

এখানে এই ব্যর্থ-নিরাপদ সংস্করণের জন্য কয়েকটি পরীক্ষার কেস দেওয়া আছে meta_wrap

    @meta_wrap
    def baddecor(f, caller=lambda x: -1*x):
        @functools.wraps(f)
        def _f(*args, **kwargs):
            return caller(f(args[0]))
        return _f

    @baddecor  # used without arg: no problem
    def f_call1(x):
        return x + 1
    assert f_call1(5) == -6

    @baddecor(lambda x : 2*x) # bad case
    def f_call2(x):
        return x + 1
    f_call2(5)  # raises ValueError

    # explicit keyword: no problem
    @baddecor(caller=lambda x : 100*x)
    def f_call3(x):
        return x + 1
    assert f_call3(5) == 600

4
ধন্যবাদ এটি সহায়ক!
প্রগি আগরওয়াল

0

উপরের থেকে আরও সম্পূর্ণ উত্তর দিতে:

"কোনও সাজসজ্জা তৈরির উপায় আছে যা যুক্তি সহ এবং ছাড়া উভয়ই ব্যবহার করা যেতে পারে?"

না কোনও জেনেরিক উপায় নেই কারণ দুটি পৃথক ব্যবহারের কেস সনাক্ত করতে অজগর ভাষায় বর্তমানে কিছু অনুপস্থিত রয়েছে।

তবে হ্যাঁ যেমন ইতিমধ্যে bj0s এর মতো অন্যান্য জবাবগুলির দ্বারা ইঙ্গিত করা হয়েছে , সেখানে একটি আড়ম্বরপূর্ণ কাজ রয়েছে যেটি পেয়েছে প্রথম অবস্থানগত আর্গুমেন্টের ধরন এবং মান পরীক্ষা করা (এবং অন্য কোনও যুক্তিটির অ-ডিফল্ট মান নেই কিনা তা পরীক্ষা করা)। যদি আপনি গ্যারান্টিযুক্ত হন যে ব্যবহারকারীরা কখনই আপনার ডেকরেটারের প্রথম যুক্তি হিসাবে কলযোগ্যকে পাস করবে না , তবে আপনি এই কর্মবিধি ব্যবহার করতে পারেন। নোট করুন যে এটি শ্রেণি সজ্জকারদের জন্য একই (পূর্ববর্তী বাক্যে শ্রেণি দ্বারা কলযোগ্য প্রতিস্থাপন করুন)।

উপরের বিষয়টি নিশ্চিত করার জন্য, আমি সেখানে বেশ কিছু গবেষণা করেছি এবং এমনকী একটি লাইব্রেরিও প্রয়োগ করেছি decopatchযা উপরের বর্ণিত সমস্ত কৌশলগুলির সংমিশ্রণ ব্যবহার করে (এবং আরও অনেকগুলি, যার অন্তর্ভুক্তি সহ) "নির্ভর করে যা সবচেয়ে বুদ্ধিমান কর্মক্ষেত্রই হয়" নির্ভর করে আপনার প্রয়োজন অনুসারে

তবে প্রকৃতপক্ষে সর্বোত্তম হ'ল এখানে কোনও লাইব্রেরি না লাগানো এবং পাইথন ভাষা থেকে এই বৈশিষ্ট্যটি সরাসরি পাওয়া। যদি আমার মতো, আপনিও মনে করেন যে দুঃখের বিষয় যে পাইথন ভাষা আজকের মতো এই প্রশ্নের সুস্পষ্ট উত্তর দিতে সক্ষম হয় না , পাইথন বাগট্র্যাকারে এই ধারণাটি সমর্থন করতে দ্বিধা করবেন না : https: //bugs.python .org / ইস্যু 36553 !

অজগরকে আরও ভাল ভাষা বানাতে আপনার সহায়তার জন্য অনেক ধন্যবাদ :)


0

এটি কোনও হতাশ ছাড়াই কাজটি করে:

from functools import wraps

def memoize(fn=None, hours=48.0):
  def deco(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
      return fn(*args, **kwargs)
    return wrapper

  if callable(fn): return deco(fn)
  return deco

0

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

class decorator_with_arguments(object):

    def __new__(cls, decorated_function=None, **kwargs):

        self = super().__new__(cls)
        self._init(**kwargs)

        if not decorated_function:
            return self
        else:
            return self.__call__(decorated_function)

    def _init(self, arg1="default", arg2="default", arg3="default"):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3

    def __call__(self, decorated_function):

        def wrapped_f(*args):
            print("Decorator arguments:", self.arg1, self.arg2, self.arg3)
            print("decorated_function arguments:", *args)
            decorated_function(*args)

        return wrapped_f

@decorator_with_arguments(arg1=5)
def sayHello(a1, a2, a3, a4):
    print('sayHello arguments:', a1, a2, a3, a4)

@decorator_with_arguments()
def sayHello(a1, a2, a3, a4):
    print('sayHello arguments:', a1, a2, a3, a4)

@decorator_with_arguments
def sayHello(a1, a2, a3, a4):
    print('sayHello arguments:', a1, a2, a3, a4)

যদি ডেকোরেটারটি যুক্তিযুক্ত সাথে ব্যবহার করা হয় তবে এর সমান হয় না:

result = decorator_with_arguments(arg1=5)(sayHello)(a1, a2, a3, a4)

একটি দেখতে পাবে যে আর্গুমেন্টগুলি arg1সঠিকভাবে কনস্ট্রাক্টরকে দেওয়া হয়েছে এবং সজ্জিত ফাংশনটি পাস করা হয়েছে__call__

তবে যদি সাজসজ্জাটি যুক্তি ছাড়াই ব্যবহার করা হয় তবে এর সমান than

result = decorator_with_arguments(sayHello)(a1, a2, a3, a4)

আপনি দেখতে পাচ্ছেন যে এক্ষেত্রে সজ্জিত ফাংশনটি সরাসরি কনস্ট্রাক্টরকে __call__দেওয়া হয় এবং কলটি সম্পূর্ণ বাদ দেওয়া হয়। এজন্য আমাদের এই ক্ষেত্রে যত্ন নেওয়ার জন্য যুক্তি নিয়োগ করতে হবে__new__ ম্যাজিক পদ্ধতিতে ।

আমরা এর __init__পরিবর্তে কেন ব্যবহার করতে পারি না __new__? কারণটি সহজ: পাইথন কোনওটিই বাদ দিয়ে অন্য কোনও মান ফিরিয়ে দেওয়া নিষিদ্ধ করে__init__

সতর্কতা

এই অ্যাপ্রোক্যাচের একটি পার্শ্ব প্রতিক্রিয়া রয়েছে। এটি ফাংশনের স্বাক্ষর সংরক্ষণ করবে না!


-1

আপনি কি ডিফল্ট মান সহ কীওয়ার্ড যুক্তি দিয়ে চেষ্টা করেছেন? কিছুটা এইরকম

def decorate_something(foo=bar, baz=quux):
    pass

-2

সাধারণত আপনি পাইথনে ডিফল্ট যুক্তি দিতে পারেন ...

def redirect_output(fn, output = stderr):
    # whatever

যদিও এটি সাজসজ্জার সাথে কাজ করে তবে তা নিশ্চিত নয়। কেন হবে না এমন কোনও কারণ আমি জানি না।


4
আপনি যদি @dec (abc) বলে থাকেন তবে ফাংশনটি সরাসরি ডিসে পাস করা হয় না। dec (abc) কিছু দেয়, এবং এই ফেরতের মানটি সাজসজ্জার হিসাবে ব্যবহৃত হয়। সুতরাং ডিক (এবিসি) কে একটি ফাংশন ফিরিয়ে দিতে হবে, যা সজ্জিত ফাংশনটিকে প্যারামিটার হিসাবে পাস করবে। (এছাড়াও thobes কোড দেখুন)
হেলায়

-2

ভার্টেকের উত্তরে বিল্ডিং:

imports sys

def redirect_output(func, output=None):
    if output is None:
        output = sys.stderr
    if isinstance(output, basestring):
        output = open(output, 'w') # etc...
    # everything else...

এটি @redirect_output("somewhere.log") def foo()উদাহরণস্বরূপ যেমন সাজসজ্জা হিসাবে ব্যবহার করা যাবে না ।
ehabkost
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.