অন্য প্রশ্নের উত্তরের একটি মন্তব্যে , কেউ বলেছে যে তারা কি functools.wraps
করছে তা নিশ্চিত নয় । সুতরাং, আমি এই প্রশ্নটি জিজ্ঞাসা করছি যাতে ভবিষ্যতের রেফারেন্সের জন্য স্ট্যাকওভারফ্লোতে এর একটি রেকর্ড থাকবে: functools.wraps
ঠিক কী করে ?
অন্য প্রশ্নের উত্তরের একটি মন্তব্যে , কেউ বলেছে যে তারা কি functools.wraps
করছে তা নিশ্চিত নয় । সুতরাং, আমি এই প্রশ্নটি জিজ্ঞাসা করছি যাতে ভবিষ্যতের রেফারেন্সের জন্য স্ট্যাকওভারফ্লোতে এর একটি রেকর্ড থাকবে: functools.wraps
ঠিক কী করে ?
উত্তর:
আপনি যখন কোনও ডেকরেটর ব্যবহার করেন, আপনি একটি ফাংশন অন্যটির সাথে প্রতিস্থাপন করছেন। অন্য কথায়, যদি আপনি একটি সজ্জা আছে
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
তারপরে আপনি যখন বলবেন
@logged
def f(x):
"""does some math"""
return x + x * x
এটি বলার মতোই
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
এবং আপনার ফাংশন ফাংশন f
দিয়ে প্রতিস্থাপিত হয় with_logging
। দুর্ভাগ্যক্রমে, এর অর্থ হ'ল যদি আপনি বলেন তবে
print(f.__name__)
এটি মুদ্রণ করবে with_logging
কারণ এটি আপনার নতুন ফাংশনের নাম। প্রকৃতপক্ষে, আপনি যদি ডকাস্ট্রিংয়ের দিকে নজর দেন তবে f
এটি ফাঁকা হবে কারণ with_logging
কোনও ডক্টর্টিং নেই, এবং তাই আপনার লেখা ডক্ট্রাস্টিং আর থাকবে না। এছাড়াও, যদি আপনি সেই ফাংশনের জন্য পাইডোক ফলাফলটি দেখেন তবে এটি একটি যুক্তি হিসাবে তালিকাভুক্ত হবে না x
; পরিবর্তে এটি গ্রহণ হিসাবে তালিকাভুক্ত করা হবে *args
এবং **kwargs
কারণ হ'ল_লগিং যা গ্রহণ করে।
যদি কোনও ডেকোরেটার ব্যবহার করার অর্থ হ'ল কোনও ফাংশন সম্পর্কে এই তথ্যটি হারাতে থাকে তবে এটি মারাত্মক সমস্যা হবে। আমাদের জন্য তাই functools.wraps
। এটি একটি ডেকোরেটারে ব্যবহৃত একটি ফাংশন গ্রহণ করে এবং ফাংশনটির নাম, ডকস্ট্রিং, আর্গুমেন্টের তালিকা ইত্যাদির অনুলিপি করার কার্যকারিতা যুক্ত করে এবং যেহেতু wraps
নিজেই একটি সজ্জাকারী হয় তাই নিম্নলিখিত কোডটি সঠিক কাজ করে:
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print(f.__name__) # prints 'f'
print(f.__doc__) # prints 'does some math'
functools.wraps
এই কাজের প্রয়োজন , এটি কেবল প্রথম স্থানে সজ্জা বিন্যাসের অংশ হওয়া উচিত নয়? আপনি কখন @ wraps ব্যবহার করতে চান না ?
@wraps
অনুলিপি করা মানগুলিতে বিভিন্ন ধরণের পরিবর্তন বা টিকা দেওয়ার জন্য তাদের নিজস্ব সংস্করণ তৈরি করে। মৌলিকভাবে, এটি পাইথন দর্শনের একটি এক্সটেনশন যা স্পষ্টতই প্রচ্ছন্নতার চেয়ে ভাল এবং বিশেষ কেসগুলি নিয়ম ভাঙার পক্ষে যথেষ্ট বিশেষ নয়। (কোডটি বেশ সহজ এবং @wraps
আমি প্রায়শই আমার সজ্জকারদের জন্য ফাংশনগুলির চেয়ে ক্লাস ব্যবহার করি। আমার এটির সাথে কিছুটা সমস্যা হচ্ছিল কারণ কোনও বস্তুর কোনও ফাংশন প্রত্যাশিত একই বৈশিষ্ট্যগুলি থাকবে না। উদাহরণস্বরূপ, কোনও বস্তুর বৈশিষ্ট্য থাকবে না __name__
। আমার এটির সাথে একটি নির্দিষ্ট সমস্যা ছিল যা জ্যাঙ্গো ত্রুটিটি জানিয়েছিল যেখানে "অবজেক্টটির কোনও বৈশিষ্ট্য নেই __name__
'" এটি জানা খুব কঠিন । দুর্ভাগ্যক্রমে, ক্লাস-স্টাইলের সাজসজ্জার জন্য, আমি বিশ্বাস করি না যে @ মোড়ানো কাজটি করবে। পরিবর্তে আমি এর মতো একটি বেস সজ্জা শ্রেণি তৈরি করেছি:
class DecBase(object):
func = None
def __init__(self, func):
self.__func = func
def __getattribute__(self, name):
if name == "func":
return super(DecBase, self).__getattribute__(name)
return self.func.__getattribute__(name)
def __setattr__(self, name, value):
if name == "func":
return super(DecBase, self).__setattr__(name, value)
return self.func.__setattr__(name, value)
এই শ্রেণিটি সমস্ত বৈশিষ্ট্য সজ্জিত করা ফাংশনে কল করে। সুতরাং, আপনি এখন একটি সাধারণ সাজসজ্জা তৈরি করতে পারেন যা চেক করে যে 2 টি আর্গুমেন্ট এর মতো নির্দিষ্ট করা হয়েছে:
class process_login(DecBase):
def __call__(self, *args):
if len(args) != 2:
raise Exception("You can only specify two arguments")
return self.func(*args)
@wraps
বলেছেন, @wraps
কেবল এটি একটি সুবিধার কাজ functools.update_wrapper()
। শ্রেণি সাজসজ্জার ক্ষেত্রে, আপনি update_wrapper()
সরাসরি আপনার __init__()
পদ্ধতি থেকে কল করতে পারেন । সুতরাং, আপনি তৈরি করতে প্রয়োজন হবে না DecBase
এ সব, আপনি শুধু উপর অন্তর্ভুক্ত করতে পারে __init__()
এর process_login
লাইন: update_wrapper(self, func)
। এখানেই শেষ.
অজগর 3.5+ হিসাবে:
@functools.wraps(f)
def g():
pass
এর একটি উপনাম g = functools.update_wrapper(g, f)
। এটি ঠিক তিনটি জিনিস করে:
__module__
, __name__
, __qualname__
, __doc__
, এবং __annotations__
গুণাবলীর f
উপর g
। এই ডিফল্ট তালিকাটি রয়েছে WRAPPER_ASSIGNMENTS
, আপনি এটি ফান্টুলস উত্সে দেখতে পারেন ।__dict__
এর g
থেকে সব উপাদানের সঙ্গে f.__dict__
। ( WRAPPER_UPDATES
উত্স দেখুন)__wrapped__=f
বৈশিষ্ট্য সেট করেg
ফলাফলটি হ'ল g
একই নাম, ডকস্ট্রিং, মডিউল নাম, এবং এর চেয়ে স্বাক্ষর হিসাবে উপস্থিত f
। একমাত্র সমস্যা হ'ল স্বাক্ষর সম্পর্কিত এটি আসলে সত্য নয়: এটি কেবল inspect.signature
ডিফল্টরূপে মোড়কের চেইন অনুসরণ করে। আপনি ডকটিতেinspect.signature(g, follow_wrapped=False)
বর্ণিত হিসাবে ব্যবহার করে এটি পরীক্ষা করতে পারেন । এর বিরক্তিকর পরিণতি রয়েছে:
Signature.bind()
।এখন functools.wraps
এবং সজ্জাকারদের মধ্যে কিছুটা বিভ্রান্তি দেখা দিয়েছে , কারণ সজ্জিতগুলি বিকাশের জন্য খুব ঘন ঘন ব্যবহারের ক্ষেত্রে ফাংশন মোড়ানো হয়। তবে দুটিই সম্পূর্ণ স্বাধীন ধারণা are আপনি যদি এই পার্থক্যটি বুঝতে আগ্রহী হন তবে আমি উভয়ের জন্য সহায়ক লাইব্রেরি প্রয়োগ করেছি: সহজেই সাজসজ্জার লেখার জন্য ডিকোপ্যাচ এবং স্বাক্ষর-সংরক্ষণের প্রতিস্থাপনের জন্য মেকফান@wraps
। নোট করুন যে makefun
বিখ্যাত decorator
গ্রন্থাগারের তুলনায় একই প্রমাণিত কৌশলের উপর নির্ভর করে ।
এটি মোড়ানো সম্পর্কে উত্স কোড:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Update a wrapper function to look like the wrapped function
wrapper is the function to be updated
wrapped is the original function
assigned is a tuple naming the attributes assigned directly
from the wrapped function to the wrapper function (defaults to
functools.WRAPPER_ASSIGNMENTS)
updated is a tuple naming the attributes of the wrapper that
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)
"""
for attr in assigned:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
# Return the wrapper so this can be used as a decorator via partial()
return wrapper
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Decorator factory to apply update_wrapper() to a wrapper function
Returns a decorator that invokes update_wrapper() with the decorated
function as the wrapper argument and the arguments to wraps() as the
remaining arguments. Default arguments are as for update_wrapper().
This is a convenience function to simplify applying partial() to
update_wrapper().
"""
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
পূর্বশর্ত: আপনার অবশ্যই সজ্জা ব্যবহার করতে হবে এবং বিশেষত মোড়ক সহ অবশ্যই ব্যবহার করতে হবে know এই মন্তব্যটি এটি কিছুটা স্পষ্টভাবে ব্যাখ্যা করেছে বা এই লিঙ্কটি এটি বেশ ভালভাবে ব্যাখ্যা করেছে।
যখনই আমরা উদাহরণস্বরূপ ব্যবহার করি: @ র্যাপগুলি তার পরে আমাদের নিজস্ব মোড়ক ফাংশন অনুসরণ করে। এই লিঙ্কে প্রদত্ত বিবরণ অনুযায়ী , এটি বলে যে
functools.wraps একটি মোড়কের ফাংশনটি সংজ্ঞায়িত করার সময়, একটি ফাংশন ডেকোরেটর হিসাবে আপডেট_রেপ্পার () ডাকে সুবিধাজনক ফাংশন।
এটি আংশিক (আপডেট_ওয়ালা, মোড়ানো = মোড়ানো, নির্ধারিত = বরাদ্দ, আপডেট = আপডেট) সমতুল্য।
সুতরাং @ র্যাপস ডেকোরেটর আসলে ফান্টাকুলস. পার্টিশিয়ালকে কল দেয় (ফানক [, * আরগস] [, ** কীওয়ার্ড])।
Functools.partial () সংজ্ঞা বলছে যে
আংশিক () আংশিক ফাংশন অ্যাপ্লিকেশনের জন্য ব্যবহৃত হয় যা কোনও ফাংশনের আর্গুমেন্ট এবং / অথবা কীওয়ার্ডগুলির কিছু অংশকে সরল স্বাক্ষরযুক্ত একটি নতুন অবজেক্টের ফলে "স্থির করে দেয়"। উদাহরণস্বরূপ, আংশিক () একটি কলযোগ্য তৈরি করতে ব্যবহার করা যেতে পারে যা int () ফাংশনের মতো আচরণ করে যেখানে বেস আর্গুমেন্টটি দুটিতে ডিফল্ট হয়:
>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18
যা আমাকে এই সিদ্ধান্তে নিয়ে আসে যে, @ র্যাপগুলি আংশিক () কে কল দেয় এবং এটি আপনার প্যাঁচার ফাংশনটিকে প্যারামিটার হিসাবে পাস করে। আংশিক () শেষে সরলীকৃত সংস্করণটি প্রদান করে অর্থাৎ মোড়ক ফাংশনের ভিতরে যা আছে তার বস্তু এবং মোড়ক ফাংশন নিজেই নয়।
সংক্ষেপে, functools.wraps কেবল একটি নিয়মিত ফাংশন। আসুন এই সরকারী উদাহরণ বিবেচনা করা যাক । সাহায্যে সোর্স কোড , আমরা ও বাস্তবায়ন সম্পর্কে আরো বিস্তারিত চলমান পদক্ষেপ নিম্নরূপ দেখতে পারেন:
মোড়কের = O1 .__ কল __ (মোড়কের)
বাস্তবায়ন চেক করা হচ্ছে __call__ , আমরা এই পদক্ষেপ, (বাম দিকে) এর পর দেখতে মোড়কের হয়ে বস্তু দ্বারা ফলে self.func (* self.args, * args, ** newkeywords) সৃষ্টির চেক করা হচ্ছে O1 মধ্যে __new__ , আমরা জানা self.func ফাংশন update_wrapper । এটি প্যারামিটার * আরগগুলি , ডান হাতের মোড়কটিকে এর প্রথম প্যারামিটার হিসাবে ব্যবহার করে। শেষ পদক্ষেপ চেক করা হচ্ছে update_wrapper , এক ডান দিকে দেখতে পারেন মোড়কের হিসাবে প্রয়োজন পরিবর্তিত গুণাবলীর কিছু ফিরিয়ে দেওয়া হয়।