इसलिए मैंने हाल ही में ज्ञापन के बारे में एक प्रश्न पूछा और कुछ महान जवाब प्राप्त हुए, और अब मैं इसे अगले स्तर पर ले जाना चाहता हूं। काफी गुस्सा होने के बाद, मुझे एक ज्ञापन सजावट का संदर्भ कार्यान्वयन नहीं मिला जो कि कीवर्ड तर्कों वाले फ़ंक्शन को कैश करने में सक्षम था। वास्तव में, उनमें से अधिकतर ने *args
का उपयोग कैश लुकअप के लिए कुंजी के रूप में किया था, जिसका अर्थ है कि यदि आप किसी फ़ंक्शन को याद रखना चाहते हैं जो सूचियों या डिक्ट्स को तर्क के रूप में स्वीकार करता है तो यह भी टूट जाएगा।क्या पाइथन में एक ज्ञापन सजावट के लिए कीवर्ड तर्कों का समर्थन करने के लिए एक पाइथोनिक तरीका है?
मेरे मामले में, फ़ंक्शन के लिए पहला तर्क स्वयं द्वारा एक अद्वितीय पहचानकर्ता है, जो कैश लुकअप के लिए एक कोर कुंजी के रूप में उपयोग के लिए उपयुक्त है, हालांकि मैं कीवर्ड तर्कों का उपयोग करने की क्षमता चाहता था और फिर भी उसी कैश तक पहुंचने की क्षमता चाहता था। मेरा मतलब है, my_func('unique_id', 10)
और my_func(foo=10, func_id='unique_id')
दोनों को एक ही कैश किए गए परिणाम को वापस करना चाहिए।
ऐसा करने के लिए, हमें जो भी खोजना है, वह कहने के लिए एक साफ और पायदानपूर्ण तरीका है 'जो भी खोजशब्द है, वह पहले तर्क के अनुरूप है)। यही वह है जिसके साथ मैं आया:
class memoize(object):
def __init__(self, cls):
if type(cls) is FunctionType:
# Let's just pretend that the function you gave us is a class.
cls.instances = {}
cls.__init__ = cls
self.cls = cls
self.__dict__.update(cls.__dict__)
def __call__(self, *args, **kwargs):
"""Return a cached instance of the appropriate class if it exists."""
# This is some dark magic we're using here, but it's how we discover
# that the first argument to Photograph.__init__ is 'filename', but the
# first argument to Camera.__init__ is 'camera_id' in a general way.
delta = 2 if type(self.cls) is FunctionType else 1
first_keyword_arg = [k
for k, v in inspect.getcallargs(
self.cls.__init__,
'self',
'first argument',
*['subsequent args'] * (len(args) + len(kwargs) - delta)).items()
if v == 'first argument'][0]
key = kwargs.get(first_keyword_arg) or args[0]
print key
if key not in self.cls.instances:
self.cls.instances[key] = self.cls(*args, **kwargs)
return self.cls.instances[key]
पागल बात यह है कि यह वास्तव में काम करता है। उदाहरण के लिए, अगर आप इस तरह सजाने यदि:
@memoize
class FooBar:
instances = {}
def __init__(self, unique_id, irrelevant=None):
print id(self)
फिर अपने कोड से आप या तो FooBar('12345', 20)
या FooBar(irrelevant=20, unique_id='12345')
और वास्तव में FooBar का एक ही उदाहरण मिल कॉल कर सकते हैं। इसके बाद आप पहले तर्क के लिए एक अलग वर्ग के साथ एक अलग वर्ग को परिभाषित कर सकते हैं, क्योंकि यह सामान्य तरीके से काम करता है (यानी, सजावटी को इस काम के लिए सजाए जाने वाले वर्ग के बारे में कुछ भी जानने की आवश्यकता नहीं है)।
समस्या है, यह एक धर्मभ्रष्ट गड़बड़ ;-)
यह काम करता है क्योंकि inspect.getcallargs
एक dict तर्क आप इसे आपूर्ति करने के लिए परिभाषित कीवर्ड मानचित्रण देता है, तो मैं यह आपूर्ति कुछ जाली तर्क और उसके बाद के लिए dict का निरीक्षण पारित किया गया पहला तर्क।
यदि कोई ऐसी चीज मौजूद थी, तो बहुत अच्छा होगा, inspect.getcallargs
का एनालॉग है जो कीवर्ड तर्कों के एक नियम के रूप में तर्कों की सूची के रूप में एकीकृत दोनों प्रकार के तर्कों को वापस कर देता है। यही कारण है कि कुछ इस तरह की अनुमति होगी:
def __call__(self, *args, **kwargs):
key = inspect.getcallargsaslist(self.cls.__init__, None, *args, **kwargs)[1]
if key not in self.cls.instances:
self.cls.instances[key] = self.cls(*args, **kwargs)
return self.cls.instances[key]
अन्य तरह से मैं से निपटने के लिए इस सीधे देखने कैश कुंजी के रूप में inspect.getcallargs
द्वारा प्रदान की dict का उपयोग करेंगे के देख सकते हैं, लेकिन उस से समान तार बनाने के लिए एक repeatable तरह से की आवश्यकता होगी समान हैश, जो मैंने सुना है, पर भरोसा नहीं किया जा सकता है (मुझे लगता है कि मुझे चाबियाँ सॉर्ट करने के बाद स्ट्रिंग का निर्माण करना होगा)।
क्या किसी के पास इस पर कोई विचार है? क्या यह कीवर्ड तर्कों के साथ एक फ़ंक्शन को कॉल करना और परिणामों को कैश करना गलत है? या बस बहुत मुश्किल है?
मुझे इसकी प्रकृति पसंद है लेकिन मैं उस हिस्से के बारे में चिंतित हूं जहां 'कुंजी' रिटर्न 'tuple (a.items()) '। क्या यह अलग-अलग लेकिन समान डिक्ट्स के लिए एक ही क्रम में चाबियाँ सॉर्ट करने की गारंटी है? मैंने सुना है कि समान रूप से दिए गए दोहराए जाने वाले तारों का उत्पादन करने के लिए 'str ({1: 2,3: 4})' जैसे चीजों पर निर्भर नहीं है और बहुत ही गलत है। – robru
ऐसा लगता है कि 'inspect.getargspec (func) .args [0]' विशिष्ट प्रश्न का सटीक उत्तर है जिसे मैंने पूछा (पहली तर्क का नाम कैसे ढूंढें), लेकिन मैं इसे विस्तारित करना चाहता हूं अधिक सामान्य समाधान। एक बार मेरे पास इसे ठीक करने के लिए कुछ समय बाद मैं अपने परिणाम पोस्ट करूंगा। – robru
@Robru: dict sorting के बारे में अच्छी बात है। 'Tuple में बदल दिया गया (क्रमबद्ध (a.items())) (एक और विकल्प' frozenset (a.items()) ') होगा। – georg