2010-08-17 10 views
7

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

 
/Plugins 
/Plugins/__init__.py 
/Plugins/Plugin1.py 
/Plugins/Plugin2.py 
etc... 

प्रत्येक .py फ़ाइल एक वर्ग है कि PluginBaseClass से निकला होता है। तो मुझे Plugins पैकेज में प्रत्येक मॉड्यूल को सूचीबद्ध करने की आवश्यकता है और फिर PluginBaseClass लागू करने वाले किसी भी वर्ग की खोज करें। आदर्श रूप में मैं इस तरह कुछ करने के लिए सक्षम होना चाहते हैं:

for klass in iter_plugins(project.Plugins): 
    action = klass() 
    action.run() 

मैं कुछ अन्य उत्तर वहाँ देखा है, लेकिन मेरी स्थिति अलग है। मेरे पास मूल पैकेज में वास्तविक आयात है (यानी: import project.Plugins) और मुझे मॉड्यूल की खोज के बाद कक्षाएं खोजने की आवश्यकता है।

+1

आप की आवश्यकता कर सकते हैं कि वे अपने वर्ग के लिए एक जादू नाम का उपयोग करें? – nmichaels

+1

मुझे माफ़ कर दो (मैं पाइथन के लिए अपेक्षाकृत नया हूं), लेकिन जादू वर्ग का नाम क्या है? कुछ '__Plugin1__' जैसा है? –

+3

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

उत्तर

1

स्कैनिंग मॉड्यूल अच्छा विचार नहीं है। यदि आपको कक्षा रजिस्ट्री की आवश्यकता है तो आपको metaclasses पर देखना चाहिए या zope.interface जैसे मौजूदा समाधानों का उपयोग करना चाहिए। metaclasses के माध्यम से सरल समाधान है कि तरह लग रहे हो सकता है:

from functools import reduce 
class DerivationRegistry(type): 
    def __init__(cls,name,bases,cls_dict): 
     type.__init__(cls,name,bases,cls_dict) 
     cls._subclasses = set() 
     for base in bases: 
      if isinstance(base,DerivationRegistry): 
       base._subclasses.add(cls) 

    def getSubclasses(cls): 
     return reduce(set.union, 
         (succ.getSubclasses() for succ in cls._subclasses if isinstance(succ,DerivationRegistry)), 
         cls._subclasses) 

class Base(object): 
    __metaclass__ = DerivationRegistry 

class Cls1(object): 
    pass 

class Cls2(Base): 
    pass 

class Cls3(Cls2,Cls1): 
    pass 

class Cls4(Cls3): 
    pass 

print(Base.getSubclasses()) 
0

आप नहीं जानते कि क्या समय की Plugins आगे में होने जा रहा है, तो आप पैकेज की निर्देशिका में अजगर फ़ाइलों की एक सूची प्राप्त कर सकते हैं, और उन्हें इतनी तरह आयात:

# compute a list of modules in the Plugins package 

import os 
import Plugins 
plugin_modules = [f[:-3] for f in os.listdir(os.path.dirname(Plugins.__file__)) 
        if f.endswith('.py') and f != '__init__.py'] 

क्षमा करें, कि समझ अपेक्षाकृत नया पाइथन के लिए एक मुट्ठी भर हो सकता है। यहाँ एक अधिक वर्बोज़ संस्करण है (आसान का पालन करने के हो सकता है):

plugin_modules = [] 

package_path = Plugins.__file__ 
file_list = os.listdir(os.path.dirname(package_path)) 
for file_name in file_list: 
    if file_name.endswith('.py') and file_name != '__init__.py': 
     plugin_modules.append(file_name) 

तो फिर तुम मॉड्यूल पाने के लिए __import__ उपयोग कर सकते हैं:

# get the first one 
plugin = __import__('Plugins.' + plugin_modules[0]) 
+0

खैर कि मॉड्यूल के नाम हो जाता है, लेकिन जब मैं आयात की तरह आपको बताएंगे कि, मैं वापस मॉड्यूल है कि एक कम है। तो '__import __ (" प्लगइन्स है मिलता है। Plugin1 ")' 'बाहर प्रिंट <से मॉड्यूल 'प्लगइन्स' 'डी: \ स्रोत \ प्लगइन्स \\ __ init __ pyc।'>' यह एक ही समस्या मैं भी अन्य तरीकों से पड़ा है –

4

संपादित करें: यहाँ एक संशोधित समाधान है। मुझे एहसास हुआ कि मैं अपने पिछले एक परीक्षण के दौरान गलती कर रहा था, और यह वास्तव में जिस तरह से आप उम्मीद करेंगे काम नहीं करता है। तो यहाँ एक अधिक पूर्ण समाधान है:

import os 
from imp import find_module 
from types import ModuleType, ClassType 

def iter_plugins(package): 
    """Receives package (as a string) and, for all of its contained modules, 
    generates all classes that are subclasses of PluginBaseClass.""" 

    # Despite the function name, "find_module" will find the package 
    # (the "filename" part of the return value will be None, in this case) 
    filename, path, description = find_module(package) 

    # dir(some_package) will not list the modules within the package, 
    # so we explicitly look for files. If you need to recursively descend 
    # a directory tree, you can adapt this to use os.walk instead of os.listdir 
    modules = sorted(set(i.partition('.')[0] 
          for i in os.listdir(path) 
          if i.endswith(('.py', '.pyc', '.pyo')) 
          and not i.startswith('__init__.py'))) 
    pkg = __import__(package, fromlist=modules) 
    for m in modules: 
     module = getattr(pkg, m) 
     if type(module) == ModuleType: 
      for c in dir(module): 
       klass = getattr(module, c) 
       if (type(klass) == ClassType and 
        klass is not PluginBaseClass and 
        issubclass(klass, PluginBaseClass)): 
        yield klass 

मेरे पिछले समाधान था:

from types import ModuleType 
import Plugins 

classes = [] 
for item in dir(Plugins): 
    module = getattr(Plugins, item) 
    # Get all (and only) modules in Plugins 
    if type(module) == ModuleType: 
     for c in dir(module): 
      klass = getattr(module, c) 
      if isinstance(klass, PluginBaseClass): 
       classes.append(klass) 
असल

, और भी बेहतर, आप कुछ प्रतिरूपकता चाहते हैं::

आप की तरह कुछ की कोशिश कर सकते

from types import ModuleType 

def iter_plugins(package): 
    # This assumes "package" is a package name. 
    # If it's the package itself, you can remove this __import__ 
    pkg = __import__(package) 
    for item in dir(pkg): 
     module = getattr(pkg, item) 
     if type(module) == ModuleType: 
      for c in dir(module): 
       klass = getattr(module, c) 
       if issubclass(klass, PluginBaseClass): 
        yield klass 
+0

यह प्रतीत नहीं होता है।। प्लगइन मॉड्यूल की खोज के लिए काम करते हैं। अगर मैं यह 'Plugins.Plugin1' तो इसे' isinstance' कॉल जब तक काम करता है जो के बाद से हम अभी तक कक्षा instantiated नहीं किया है काम नहीं करता है गुजरती हैं। –

+0

'issubclass' काम कर देता है। बस पैकेज के उप-मॉड्यूल को लोड करने के बारे में जानने की आवश्यकता है। –

+0

आह, क्षमा करें, मेरा मतलब है "issबक्लास", नहीं "Isinstance"। मैं जवाब तय कर रहा हूँ। – rbp

5

आप __all__ को __init__.py में परिभाषित कर सकते हैं (और शायद चाहिए) आपके पैकेज में submodules की एक सूची; ऐसा इसलिए है कि आप from Plugins import * करने वाले लोगों का समर्थन करते हैं। आपने ऐसा किया है, तो आप के साथ

import Plugins 
import sys 
modules = { } 
for module in Plugins.__all__: 
    __import__(module) 
    modules[ module ] = sys.modules[ module ] 
    # iterate over dir(module) as above 

कारण एक और उत्तर यहां पोस्ट में विफल रहता है कि __import__ सबसे कम स्तर के मॉड्यूल का आयात करता है, लेकिन रिटर्न शीर्ष स्तर के एक (docs देखने मॉड्यूल से अधिक पुनरावृति कर सकते हैं)। मुझे नहीं पता क्यों।

+0

मैं इससे बचना चाहता हूं। प्लगइन्स पैकेज में एक समय में 500 सक्रिय प्लगइन्स हो सकते हैं। –

+0

@ जेसन: मेरा मानना ​​है कि ऐसा करने का यह एकमात्र मजबूत तरीका है, उदाहरण के लिए symlinks, मॉड्यूल की गतिशील रचना, उस तरह की चीज। हालांकि, अगर आप की गारंटी है कि सभी मॉड्यूल निर्देशिका में अजगर फ़ाइलों के रूप में उपलब्ध नहीं होगा, आप के बजाय अधिक (मॉड्यूल मॉड्यूल के लिए os.listdir में ("।") यदि module.endswith (".py")) पुनरावृति कर सकते हैं '' । एनबी ने टिप्पणी करने के लिए संपादित किया कि क्यों '__import__' गलत मान लौटा रहा है। – katrielalex

+0

'__import__' कथन पर सूचक उपयोगी था। अब जब मैंने उससे पहले काम किया है, ऐसा लगता है कि 'dir()' वास्तव में उप-मॉड्यूल वापस नहीं करता है। क्या इसे पूरा करने का कोई और तरीका है, आपने सुना है? –

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

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