2011-08-26 8 views
10

मैं उन तकनीकों की तलाश में हूं जो उपयोगकर्ताओं को किसी एप्लिकेशन में मॉड्यूल को ओवरराइड करने या नए मॉड्यूल के साथ एप्लिकेशन का विस्तार करने की अनुमति देते हैं।पायथन में उपयोगकर्ता कोड एक्सटेंशन सक्षम करने के लिए प्रचलित तकनीकें क्या हैं?

पाइड्रा नामक एक एप्लिकेशन की कल्पना करें। यह वर्तमान में एक सर्कल कक्षा प्रदान करता है, जो आकार को विरासत में लेता है। पैकेज पेड़ की तरह लग सकता है:

/usr/lib/python/ 
└── pydraw 
    ├── __init__.py 
    ├── shape.py 
    └── shapes 
     ├── circle.py 
     └── __init__.py 

अब मैं गतिशील खोज और उपयोगकर्ता मॉड्यूल है कि एक नए आकार, या शायद यह भी आकार वर्ग में ही लागू की लोडिंग सक्षम करना चाहते हैं लगता है। यह सबसे सरल एक उपयोगकर्ता के पेड़ जैसे आवेदन पेड़ के रूप में एक ही संरचना, के लिए के लिए लगता है:

/home/someuser/python/ 
└── pydraw 
    ├── __init__.py 
    ├── shape.py  <-- new superclass 
    └── shapes 
     ├── __init__.py 
     └── square.py <-- new user class 

दूसरे शब्दों में, मैं उपरिशायी और से समान नाम वाले फाइलों के साथ एक आवेदन पत्र पेड़ मुखौटा करना चाहते हैं उपयोगकर्ता का पेड़, या कम से कम उस स्पष्ट संरचना को पाइथन बिंदु से प्राप्त करें।

फिर, sys.path या PYTHONPATH कॉन्फ़िगर करके, pydraw.shapes.square खोज योग्य हो सकता है। हालांकि, पायथन की मॉड्यूल पथ खोज को मॉड्यूल जैसे मॉड्यूल नहीं मिलते हैं। मुझे लगता है कि ऐसा इसलिए है क्योंकि __method__ में पहले से ही एक अन्य पथ पर एक पैरेंट मॉड्यूल शामिल है।

आप इस कार्य को पायथन के साथ कैसे पूरा करेंगे?

उत्तर

1

आप विभिन्न स्थानों से गतिशील रूप से अजगर कोड लोड करना चाहते हैं, तो आप खोज __path__ pkgutil module का उपयोग करके विशेषताओं का विस्तार कर सकते हैं:

from pkgutil import extend_path 
__path__ = extend_path(__path__, __name__) 
:

प्रत्येक pydraw/__init__.py और pydraw/shapes/__init__.py में इन पंक्तियों के रखने से

आप आयात विवरण लिखने में सक्षम होंगे जैसे कि आपके पास एक अद्वितीय पैकेज था:

>>> import pydraw.shapes 
>>> pydraw.shapes.__path__ 
['/usr/lib/python/pydraw/shapes', '/home/someuser/python/pydraw/shapes'] 
>>> from pydraw.shapes import circle, square 
>>> 

आप अपने प्लगइन के ऑटो-पंजीकरण के बारे में सोच सकते हैं। आप अभी भी मॉड्यूल वैरिएबल सेट करके मूल पायथन कोड का उपयोग कर सकते हैं (जो सिंगलटन पैटर्न के रूप में कार्य करेगा)।

हर pydraw/shapes/__init__.py फ़ाइल में अंतिम पंक्ति जोड़ें:

from pkgutil import extend_path 
__path__ = extend_path(__path__, __name__) 

# your shape registry 
__shapes__ = [] 

अब आप इससे संबंधित मॉड्यूल (circle.py या square.py यहाँ) के शीर्ष में एक आकार रजिस्टर कर सकते हैं।

from pydraw.shapes import __shapes__ 
__shapes__.append(__name__) 

अंतिम जांच:

>>> from pydraw.shapes import circle,square 
>>> from pydraw.shapes import circle,square,__shapes__ 
>>> __shapes__ 
['pydraw.shapes.circle', 'pydraw.shapes.square'] 
+0

धन्यवाद जूलियन- expand_path ठीक वही है जो मैं ढूंढ रहा था! मुझे गलती से विश्वास था कि एक पैकेज केवल एक निर्देशिका पेड़ से लोड किया जा सकता है। – Reece

3

एक्सटेंशन की खोज थोड़ा भंगुर और जटिल हो सकता है, और यह भी आवश्यक है कि आप PYTHONPATH के सभी जो बहुत बड़ा हो सकता है के माध्यम से देखने के लिए।

इसके बजाए, एक कॉन्फ़िगरेशन फ़ाइल है जो प्लगइन को सूचीबद्ध करती है जो लोड की जानी चाहिए। यह उन्हें मॉड्यूल नाम के रूप में सूचीबद्ध करके किया जा सकता है, और यह भी आवश्यक है कि वे PythonPATH पर स्थित हैं, या बस पूर्ण पथ सूचीबद्ध कर रहे हैं।

यदि आप प्रति उपयोगकर्ता स्तर की कॉन्फ़िगरेशन चाहते हैं, तो मेरे पास एक वैश्विक कॉन्फ़िगरेशन फ़ाइल होगी जो मॉड्यूल सूचीबद्ध करती है, और प्रति उपयोगकर्ता एक, और कुछ डिस्कवरी तंत्र को आजमाने के बजाय इन कॉन्फ़िगरेशन फ़ाइलों को पढ़ें।

इसके अलावा, आप न केवल प्लगइन्स जोड़ने के लिए, लेकिन अपने आवेदन के घटकों ओवरराइड करने के लिए कोशिश कर रहे हैं। इसके लिए मैं ज़ोप घटक वास्तुकला का उपयोग करूंगा। हालांकि यह अभी तक पाइथन 3 तक पूरी तरह से पोर्ट नहीं किया गया है, लेकिन इसे इस तरह के मामलों में उपयोग करने के लिए डिज़ाइन किया गया है, हालांकि आपके विवरण से यह एक साधारण मामला प्रतीत होता है, और जेडसीए अधिक हो सकता है। लेकिन वैसे भी इसे देखो।

1

एक विधि मैं ऐसी समस्या को संभालने के लिए प्रयोग किया जाता है एक provider pattern साथ था।

अपने मॉड्यूल shape.py में:

class BaseShape: 
    def __init__(self): 
     pass 
provider("BaseShape", BaseShape) 

और उपयोगकर्ता के आकार में।py मॉड्यूल:

class UserBaseShape: 
    def __init__(self): 
     pass 
provider("BaseShape", UserBaseShape) 

साथ provider विधि कुछ इस तरह कर रही है:

def provider(provide_key, provider_class): 
    global_providers[provide_key] = provider_class 

और जब आप एक वस्तु instanciate करने की जरूरत है, इस

class ProvideFactory: 
    def get(self, provide_key, *args, **kwargs): 
     return global_providers[provide_key](*args, **kwargs) 
+0

आप शायद इस ऊपर थोड़ा सज्जाकार का उपयोग करके साफ कर सकते हैं। मैं वास्तव में नहीं देखता कि शब्द "प्रदाता पैटर्न" कहां से आता है; यह मुझे एक साधारण साधारण अमूर्त कारखाने की तरह दिखता है। –

+0

@ करल कंचटेल: हाँ, हम कक्षा सजावट का उपयोग कर सकते हैं, लेकिन इस मामले में मैंने इस विचार को समझाने की कोशिश की, वास्तव में सभी को लागू नहीं किया;)। प्रदाता पैटर्न के लिए, आप सही हैं, यह सार फैक्ट्री पैटर्न के बहुत करीब है, मैं इसे प्रदाता पैटर्न नामित करता था क्योंकि यह जिस तरह से मुझे सिखाया गया है ... –

+0

यह जेडसीए के समान है, लेकिन निश्चित रूप से बहुत कम सुविधाओं के साथ। जब आप बाहरी निर्भरताओं को नहीं चाहते हैं तो यह सरल मामलों में समझ में आ सकता है। –

0

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

  • एक प्लग-इन निर्देशिका को विन्यस्त अपनी पूरी फाइल सिस्टम की निगरानी से बातें बहुत आसान बना देता है।
  • एक अलग थ्रेड में फ़ाइल परिवर्तन की तलाश में शायद सबसे अच्छा समाधान
  • अगर एक नई .py फ़ाइल पाया जाता है, तो आप इसे __import__ में निर्मित समारोह
  • का उपयोग कर यदि एक .py है आयात कर सकते हैं बदल गया है, तो आप फिर से आयात कर सकते हैं यह reload में निर्मित समारोह
  • एक वर्ग बदल रहा है, तो साथ तो उस वर्ग के उदाहरणों अभी भी पुराने वर्ग के उदाहरण की तरह व्यवहार करेगा, इसलिए जब आवश्यक
  • उदाहरणों से बनाना सुनिश्चित करें

संपादित करें:

यदि आप अपनी प्लगइन्स निर्देशिका को PythonPATH में पहली निर्देशिका के रूप में जोड़ते हैं, तो उस निर्देशिका को Pythonpath में अन्य निर्देशिकाओं पर प्राथमिकता होगी, उदा।

import sys 
sys.path.insert(0, 'PLUGIN_DIR') 
संबंधित मुद्दे

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