2010-04-27 14 views
12

मैं हाल ही में अजगर तरह बारे में बहुत कुछ पढ़ रहा है ताकि मेरे सवालनिर्भरता इंजेक्शन पायथन-मार्ग कैसे करें?

है निर्भरता इंजेक्शन अजगर तरह से कैसे करना है?

मैं सामान्य परिदृश्यों के बारे में बात कर रहा हूं, उदाहरण के लिए, सेवा ए को प्राधिकरण जांच के लिए उपयोगकर्ता सेवा तक पहुंच की आवश्यकता है।

उत्तर

14

यह सब स्थिति पर निर्भर करता है। उदाहरण के लिए, यदि आप परीक्षण प्रयोजनों के लिए निर्भरता इंजेक्शन का उपयोग करें - ताकि आप आसानी से बाहर कुछ नकली कर सकते हैं - आप अक्सर पूरी तरह इंजेक्शन छोड़ कर सकते हैं:

subprocess.Popen = some_mock_Popen 
result = subprocess.call(...) 
assert some_mock_popen.result == result 

: आप के बजाय मॉड्यूल या वर्ग आप अन्यथा डालेगी बाहर नकली कर सकते हैं subprocess.call()subprocess.Popen() पर कॉल करेगा, और हम किसी विशेष तरीके से निर्भरता को इंजेक्ट किए बिना इसे नकल कर सकते हैं। हम सीधे subprocess.Popen को प्रतिस्थापित कर सकते हैं। (यह केवल एक उदाहरण है; वास्तविक जीवन में आप इसे अधिक मजबूत तरीके से करेंगे।)

यदि आप अधिक जटिल स्थितियों के लिए निर्भरता इंजेक्शन का उपयोग करते हैं, या जब पूरे मॉड्यूल या कक्षाओं का मज़ाक उड़ाते हैं तो उचित नहीं है (क्योंकि, उदाहरण के लिए, आप केवल एक विशेष कॉल को मजाक करना चाहते हैं) फिर निर्भरता के लिए कक्षा विशेषताओं या मॉड्यूल ग्लोबल्स का उपयोग करना सामान्य विकल्प है। उदाहरण के लिए, पर विचार एक my_subprocess.py:

from subprocess import Popen 

def my_call(...): 
    return Popen(...).communicate() 

आप आसानी से केवल Popen कॉल my_subprocess.Popen को बताए द्वारा my_call() द्वारा बनाई गई जगह ले सकता है; यह subprocess.Popen के लिए किसी भी अन्य कॉल प्रभाव पड़ेगा (लेकिन यह निश्चित रूप से, my_subprocess.Popen के लिए सभी कॉल की जगह लेंगे।) इसी प्रकार, वर्ग जिम्मेदार बताते हैं:

class MyClass(object): 
    Popen = staticmethod(subprocess.Popen) 
    def call(self): 
     return self.Popen(...).communicate(...) 

वर्ग का उपयोग कर इस तरह जिम्मेदार बताते है, जो शायद ही कभी आवश्यक विकल्पों पर विचार कर रहा है , आपको staticmethod का उपयोग करने की देखभाल करनी चाहिए। यदि आप नहीं करते हैं, और जिस वस्तु को आप सम्मिलित कर रहे हैं वह एक सामान्य फ़ंक्शन ऑब्जेक्ट या किसी अन्य प्रकार का वर्णनकर्ता है, जैसे किसी संपत्ति या, किसी वर्ग या उदाहरण से पुनर्प्राप्त होने पर कुछ खास करता है, तो यह गलत काम करेगा। इससे भी बदतर, अगर आपने का उपयोग किया है तो अभी एक वर्णक नहीं है (उदाहरण के लिए subprocess.Popen वर्ग की तरह) यह अब काम करेगा, लेकिन यदि प्रश्न में वस्तु सामान्य कार्य में बदल गई है, तो यह भ्रमित हो जाएगा।

आखिरकार, केवल सादे कॉलबैक हैं; अगर आप सिर्फ किसी विशेष सेवा के लिए एक वर्ग का एक विशेष उदाहरण टाई करना चाहते हैं, तो आप सिर्फ वर्ग प्रारंभकर्ता को सेवा (या एक या एक से सेवा की विधियों का) पारित कर सकते हैं, और यह है कि का उपयोग करें:

class MyClass(object): 
    def __init__(self, authenticate=None, authorize=None): 
     if authenticate is None: 
      authenticate = default_authenticate 
     if authorize is None: 
      authorize = default_authorize 
     self.authenticate = authenticate 
     self.authorize = authorize 
    def request(self, user, password, action): 
     self.authenticate(user, password) 
     self.authorize(user, action) 
     self._do_request(action) 

... 
helper = AuthService(...) 
# Pass bound methods to helper.authenticate and helper.authorize to MyClass. 
inst = MyClass(authenticate=helper.authenticate, authorize=helper.authorize) 
inst.request(...) 

इस तरह के आवृत्ति गुणों को सेट करते समय, आपको कभी भी वर्णक फायरिंग के बारे में चिंता करने की ज़रूरत नहीं है, इसलिए केवल कार्य (या कक्षाएं या अन्य कॉलबेल या उदाहरण) असाइन करना ठीक है।

+1

मेरे पास सी # पृष्ठभूमि है इसलिए मुझे कन्स्ट्रक्टर के माध्यम से स्पष्ट इंजेक्शन और सब कुछ हल करने के लिए कंटेनर का उपयोग करने के लिए उपयोग किया जाता है। प्रारंभकर्ता में किसी को भी डिफ़ॉल्ट निर्दिष्ट करना "गरीब आदमी की डी" जैसा दिखता है जो मेरे लिए सबसे अच्छा अभ्यास नहीं दिखता क्योंकि यह पर्याप्त स्पष्ट नहीं है। असाइनमेंट द्वारा मौजूदा विधियों को बदलने के बारे में समान विचार (क्या इसे 'बंदर पैचिंग' कहा जाता है?)। क्या मुझे अपनी मानसिकता बदलनी चाहिए क्योंकि पाइथन-रास्ता सी # -वे से अलग है? –

+2

हां, यदि आप कुशल, पायथनिक कोड लिखना चाहते हैं, तो आपको यह महसूस करना चाहिए कि पायथन एक अलग भाषा है। आप अलग-अलग चीजें अलग-अलग करते हैं। परीक्षण के लिए बंदरगाह मॉड्यूल वास्तव में बहुत आम है और काफी सुरक्षित है (विशेष रूप से जब आपके लिए विवरणों को संभालने के लिए उचित मॉकिंग लाइब्रेरी का उपयोग करते हैं।) बंदरगाह वर्ग (कक्षाओं पर विशिष्ट तरीकों को बदलना) एक अलग मामला है। कोई भी डिफ़ॉल्ट वास्तव में उस चीज़ पर नहीं है जिस पर आपको ध्यान देना चाहिए - वह उदाहरण वास्तव में कन्स्ट्रक्टर के माध्यम से डी के करीब है, आप केवल डिफ़ॉल्ट को छोड़ सकते हैं और यदि आप चाहें तो इसे एक आवश्यक तर्क बना सकते हैं। –

+0

@ थॉमसवाउटर आप इसे समझा सकते हैं: _ आप my_subprocess.Popen._ को असाइन करके my_call() द्वारा बनाए गए केवल पॉपन कॉल को आसानी से प्रतिस्थापित कर सकते हैं क्या आपका मतलब है कि 'subprocess.call'' subprocess.Popen' पर कॉल का आह्वान करता है? –

1

इस "सेटटर-केवल" इंजेक्शन रेसिपी के बारे में कैसे? http://code.activestate.com/recipes/413268/

यह काफी pythonic है, __get__()/__set__(), बल्कि आक्रामक के साथ "वर्णनकर्ता" प्रोटोकॉल का उपयोग कर, एक RequiredFeature उदाहरण Feature आवश्यक की str-नाम के साथ प्रारंभ के साथ अपने सभी गुण-सेटिंग कोड को बदलने के लिए की जरूरत पड़ेगी।

0

मैंने हाल ही में पाइथन के लिए एक डी फ्रेमवर्क जारी किया जो आपकी मदद कर सकता है। मुझे लगता है कि यह एक बिल्कुल ताजा ले लेता है, लेकिन मुझे यकीन नहीं है कि यह कैसे 'पायथनिक' है। अपने लिए न्यायाधीश। प्रतिक्रिया बहुत स्वागत है।

https://github.com/suned/serum

0

@Thomas Wouters के जवाब पूर्ण और के माध्यम से है। बस इसे जोड़ना: आम तौर पर मैं सबसे सरल दृष्टिकोण पसंद करता हूं, जिनमें जटिल ढांचे और वर्बोज़ सेटअप शामिल नहीं होते हैं। तो जो मैं सबसे अधिक उपयोग करता हूं वह है कि मेरी निर्भरता कन्स्ट्रक्टर तर्क के रूप में हो।

समस्या यह है कि मैं बॉयलरप्लेट है जिसे मैं कोड में जोड़ता हूं ताकि प्रत्येक निर्भरता की गारंटी हो सके।

सज्जाकार के एक बड़े प्रशंसक होने के नाते कार्यों 'दायरे से बॉयलरप्लेट कोड निकालने के लिए मैं सिर्फ एक डेकोरेटर कि संभाल करने के लिए बनाया:

@autowired एक अजगर आसान और साफ निर्भरता इंजेक्शन सक्षम करने के लिए 3 डेकोरेटर:

  • फ़ंक्शन को सभी
  • निर्भरताओं पर आटोवियरिंग से अवगत होना जरूरी नहीं है आश्रित आंशिक
  • कॉलर स्पष्ट रूप से निर्भरता को पारित करने में सक्षम है इस में

    def __init__(self, *, model: Model = None, service: Service = None): 
        if model is None: 
         model = Model() 
    
        if service is None: 
         service = Service() 
    
        self.model = model 
        self.service = service 
        # actual code 
    

    : y उदाहरणों वांछित

डेकोरेटर का पूरा उद्देश्य इस तरह कोड के लिए है अगर

@autowired 
def __init__(self, *, model: Model, service: Service): 
    self.model = model 
    self.service = service 
    # actual code 

कोई जटिल सामान, कोई सेटअप, कोई वर्कफ़्लो लागू नहीं किया गया। और अब आपका फ़ंक्शन कोड अब निर्भरता-प्रारंभिक कोड के साथ अव्यवस्थित नहीं है।

सजावटी दृष्टिकोण बहुत कम है लेकिन यह मामला हो सकता है कि एक पूर्ण रूप से ढांचा आपको बेहतर बनाता है। इसके लिए उत्कृष्ट मॉड्यूल जैसे Injector हैं।

संबंधित मुद्दे