সজ্জিত ফাংশন স্বাক্ষর সংরক্ষণ


111

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

এখানে একটি উদাহরণ:

def args_as_ints(f):
    def g(*args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    return g

@args_as_ints
def funny_function(x, y, z=3):
    """Computes x*y + 2*z"""
    return x*y + 2*z

>>> funny_function("3", 4.0, z="5")
22

এখন পর্যন্ত সবকিছু ঠিক আছে। তবে একটি সমস্যা আছে। সজ্জিত ফাংশনটি মূল ফাংশনের ডকুমেন্টেশন ধরে রাখে না:

>>> help(funny_function)
Help on function g in module __main__:

g(*args, **kwargs)

ভাগ্যক্রমে, সেখানে একটি কার্যনির্বাহী রয়েছে:

def args_as_ints(f):
    def g(*args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    g.__name__ = f.__name__
    g.__doc__ = f.__doc__
    return g

@args_as_ints
def funny_function(x, y, z=3):
    """Computes x*y + 2*z"""
    return x*y + 2*z

এবার, ফাংশনের নাম এবং ডকুমেন্টেশন সঠিক:

>>> help(funny_function)
Help on function funny_function in module __main__:

funny_function(*args, **kwargs)
    Computes x*y + 2*z

তবে এখনও একটি সমস্যা রয়েছে: ফাংশনের স্বাক্ষরটি ভুল। "* আরগস, ** কাওয়ার্গস" তথ্যটি অকেজো হওয়ার পরে রয়েছে।

কি করো? আমি দুটি সহজ তবে ত্রুটিযুক্ত কাজের কথা ভাবতে পারি:

1 - ডকস্ট্রিংয়ে সঠিক স্বাক্ষর অন্তর্ভুক্ত করুন:

def funny_function(x, y, z=3):
    """funny_function(x, y, z=3) -- computes x*y + 2*z"""
    return x*y + 2*z

সদৃশতার কারণে এটি খারাপ। স্বাক্ষরটি এখনও স্বয়ংক্রিয়ভাবে উত্পন্ন ডকুমেন্টেশনে সঠিকভাবে প্রদর্শিত হবে না। ফাংশনটি আপডেট করা এবং ডকাস্ট্রিং পরিবর্তন করা বা টাইপ করা ভুলে যাওয়া সহজ। [ এবং হ্যাঁ, আমি এই বিষয়ে সচেতন যে ডক্টরসিং ইতিমধ্যে ফাংশন বডিটিকে নকল করে। দয়া করে এটিকে উপেক্ষা করুন; ফানি_ফানশানটি কেবল একটি এলোমেলো উদাহরণ। ]

2 - কোনও ডেকরেটর ব্যবহার করবেন না, বা প্রতিটি নির্দিষ্ট স্বাক্ষরের জন্য একটি বিশেষ-উদ্দেশ্য সজ্জা ব্যবহার করবেন না:

def funny_functions_decorator(f):
    def g(x, y, z=3):
        return f(int(x), int(y), z=int(z))
    g.__name__ = f.__name__
    g.__doc__ = f.__doc__
    return g

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

আমি এমন একটি সমাধান খুঁজছি যা সম্পূর্ণরূপে সাধারণ এবং স্বয়ংক্রিয়।

সুতরাং প্রশ্নটি হল: সজ্জিত ফাংশন স্বাক্ষরটি তৈরির পরে কি সম্পাদনা করার কোনও উপায় আছে?

অন্যথায়, আমি কি কোনও সাজসজ্জা লিখতে পারি যা ফাংশনের স্বাক্ষরটি বের করে এবং সজ্জিত ফাংশনটি নির্মাণের সময় "* কাওয়ার্গস, ** কাওয়ারস" এর পরিবর্তে সেই তথ্য ব্যবহার করে? আমি কীভাবে এই তথ্যটি বের করব? আমি কীভাবে সজ্জিত ফাংশনটি তৈরি করব - এক্সিকিউট সহ?

অন্য কোন পন্থা?


1
কখনও "পুরানো" বলিনি। আমি কমবেশি ভাবছিলাম যে inspect.Signatureসজ্জিত ফাংশনগুলি মোকাবেলায় কী যুক্ত করেছে।
নাইটশেডকুইন

উত্তর:


78
  1. ডেকোরেটর মডিউল ইনস্টল করুন :

    $ pip install decorator
  2. অভিযোজিত সংজ্ঞা args_as_ints():

    import decorator
    
    @decorator.decorator
    def args_as_ints(f, *args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    
    @args_as_ints
    def funny_function(x, y, z=3):
        """Computes x*y + 2*z"""
        return x*y + 2*z
    
    print funny_function("3", 4.0, z="5")
    # 22
    help(funny_function)
    # Help on function funny_function in module __main__:
    # 
    # funny_function(x, y, z=3)
    #     Computes x*y + 2*z

পাইথন ৩.৪++

functools.wraps()stdlib থেকে পাইথন ৩.৪ থেকে স্বাক্ষর সংরক্ষণ করে:

import functools


def args_as_ints(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return func(*args, **kwargs)
    return wrapper


@args_as_ints
def funny_function(x, y, z=3):
    """Computes x*y + 2*z"""
    return x*y + 2*z


print(funny_function("3", 4.0, z="5"))
# 22
help(funny_function)
# Help on function funny_function in module __main__:
#
# funny_function(x, y, z=3)
#     Computes x*y + 2*z

functools.wraps()কমপক্ষে পাইথন 2.5 থেকে পাওয়া যায় তবে এটি সেখানে স্বাক্ষর সংরক্ষণ করে না:

help(funny_function)
# Help on function funny_function in module __main__:
#
# funny_function(*args, **kwargs)
#    Computes x*y + 2*z

বিজ্ঞপ্তি: *args, **kwargsপরিবর্তে x, y, z=3


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

1
@ মার্কলোডাটো: functools.wraps()ইতিমধ্যে পাইথন ৩.৪++ তে স্বাক্ষর সংরক্ষণ করেছে (উত্তরে বলা হয়েছে)। আপনি কি বলতে চান সেটিংস wrapper.__signature__আগের সংস্করণগুলিতে সহায়তা করে? (আপনি কোন সংস্করণগুলি পরীক্ষা করেছেন?)
jfs

1
@ মার্কলোডাটো: help()পাইথন ৩.৪-তে সঠিক স্বাক্ষর দেখায়। কেন আপনি ভাবেন functools.wraps()আইপিথন নষ্ট হয়ে গেছে?
jfs

1
@ মার্কলোডাটো: এটি ঠিক করার জন্য যদি আমাদের কোড লিখতে হয় তবে এটি ভেঙে গেছে। প্রদত্ত যে help(): সঠিক ফলাফলের উৎপন্ন করে প্রশ্ন কি সফ্টওয়্যার টুকরা করা উচিত সংশোধন করা হয়েছে functools.wraps()বা IPython? যাই হোক না কেন, ম্যানুয়ালি নির্ধারণ __signature__করা সর্বোত্তমভাবে কাজ করা - এটি কোনও দীর্ঘমেয়াদী সমাধান নয়।
jfs

1
মনে হচ্ছে inspect.getfullargspec()এখনও functools.wrapsঅজগর ৩.৪-তে সঠিক স্বাক্ষর দেয় না এবং এর inspect.signature()পরিবর্তে আপনাকে অবশ্যই ব্যবহার করতে হবে।
টুউকা মস্তোনেন

16

এটি পাইথনের স্ট্যান্ডার্ড লাইব্রেরি functoolsএবং বিশেষত functools.wrapsফাংশন সহ সমাধান করা হয়েছে, যা " মোড়ানো ফাংশনের মতো দেখতে একটি মোড়ক ফাংশন আপডেট করতে" ডিজাইন করা হয়েছে । এটি আচরণটি পাইথন সংস্করণের উপর নির্ভর করে, তবে নীচে দেখানো হয়েছে। প্রশ্ন থেকে উদাহরণটিতে প্রয়োগ করা হয়েছে, কোডটি দেখতে এমন হবে:

from functools import wraps

def args_as_ints(f):
    @wraps(f) 
    def g(*args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    return g


@args_as_ints
def funny_function(x, y, z=3):
    """Computes x*y + 2*z"""
    return x*y + 2*z

পাইথন 3 এ কার্যকর করা হলে, এটি নিম্নলিখিত উত্পাদন করে:

>>> funny_function("3", 4.0, z="5")
22
>>> help(funny_function)
Help on function funny_function in module __main__:

funny_function(x, y, z=3)
    Computes x*y + 2*z

এটির একমাত্র অপূর্ণতা পাইথন 2 এ তবে এটি ফাংশনের যুক্তির তালিকাকে আপডেট করে না। পাইথন 2 এ কার্যকর করা হলে, এটি তৈরি করবে:

>>> help(funny_function)
Help on function funny_function in module __main__:

funny_function(*args, **kwargs)
    Computes x*y + 2*z

এটি স্ফিংস কিনা তা নিশ্চিত নন তবে মোড়ক ফাংশনটি কোনও শ্রেণির একটি পদ্ধতি হলে এটি কাজ করবে বলে মনে হয় না। স্পিনিক্স ডেকরেটারের কল স্বাক্ষরের প্রতিবেদন অবিরত করে।
বর্ণমালাটিউপ

9

আপনি ব্যবহার করতে পারেন সজ্জিত এর সাথে একটি সজ্জিত মডিউল রয়েছে decorator:

@decorator
def args_as_ints(f, *args, **kwargs):
    args = [int(x) for x in args]
    kwargs = dict((k, int(v)) for k, v in kwargs.items())
    return f(*args, **kwargs)

তারপরে পদ্ধতির স্বাক্ষর এবং সহায়তা সংরক্ষণ করা হবে:

>>> help(funny_function)
Help on function funny_function in module __main__:

funny_function(x, y, z=3)
    Computes x*y + 2*z

সম্পাদনা: জেএফ সেবাস্তিয়ান উল্লেখ করেছেন যে আমি args_as_intsফাংশনটি সংশোধন করি নি - এটি এখন ঠিক করা হয়েছে।



6

দ্বিতীয় বিকল্প:

  1. মোড়ক মডিউল ইনস্টল করুন:

$ Easy_install মোড়ক

মোড়কের কাছে একটি বোনাস থাকে, শ্রেণি স্বাক্ষর সংরক্ষণ করে।


import wrapt
import inspect

@wrapt.decorator def args_as_ints(wrapped, instance, args, kwargs): if instance is None: if inspect.isclass(wrapped): # Decorator was applied to a class. return wrapped(*args, **kwargs) else: # Decorator was applied to a function or staticmethod. return wrapped(*args, **kwargs) else: if inspect.isclass(instance): # Decorator was applied to a classmethod. return wrapped(*args, **kwargs) else: # Decorator was applied to an instancemethod. return wrapped(*args, **kwargs) @args_as_ints def funny_function(x, y, z=3): """Computes x*y + 2*z""" return x * y + 2 * z >>> funny_function(3, 4, z=5)) # 22 >>> help(funny_function) Help on function funny_function in module __main__: funny_function(x, y, z=3) Computes x*y + 2*z

2

জেফস এর উত্তরে উপরে মন্তব্য করা হয়েছে ; যদি আপনি উপস্থিতি ( helpএবং inspect.signature) এর ক্ষেত্রে স্বাক্ষর নিয়ে উদ্বিগ্ন হন তবে ব্যবহার functools.wrapsকরা একেবারে ঠিক।

যদি আপনি আচরণের ক্ষেত্রে স্বাক্ষর নিয়ে উদ্বিগ্ন হন (বিশেষত TypeErrorযুক্তিগুলির সাথে functools.wrapsমেলে না এমন ক্ষেত্রে) এটি সংরক্ষণ করে না। আপনি বরং decoratorএটির জন্য বা তার মূল ইঞ্জিনটির নামকরণ করা আমার সাধারণীকরণের জন্য ব্যবহার করা উচিত makefun

from makefun import wraps

def args_as_ints(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("wrapper executes")
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return func(*args, **kwargs)
    return wrapper


@args_as_ints
def funny_function(x, y, z=3):
    """Computes x*y + 2*z"""
    return x*y + 2*z


print(funny_function("3", 4.0, z="5"))
# wrapper executes
# 22

help(funny_function)
# Help on function funny_function in module __main__:
#
# funny_function(x, y, z=3)
#     Computes x*y + 2*z

funny_function(0)  
# observe: no "wrapper executes" is printed! (with functools it would)
# TypeError: funny_function() takes at least 2 arguments (1 given)

সম্পর্কে এই পোস্টেfunctools.wraps দেখুন ।


1
এছাড়াও, ফলাফলটি inspect.getfullargspecকল করে রাখা হয় না functools.wraps
laike9m

@ Laike9m দরকারী অতিরিক্ত মন্তব্য করার জন্য ধন্যবাদ!
স্মিরি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.