简体   繁体   English

Python从模块动态导入类

[英]Python import classes from modules dynamically

I'm working with an API which has many sub components. 我正在使用具有许多子组件的API。 I'm making a toolkit to interact with this API and I would like to make it so that I can dynamically load some of the classes based on variables, for example, loading all classes which have a variable api_module = True set inside. 我正在制作一个与此API进行交互的工具包,因此我想使其能够基于变量动态加载某些类,例如,加载内部设置了变量api_module = True的所有类。

.
├── APIModules
│   ├── ccu.py
│   ├── __init__.py
│   └── OpenAPI.py

The CCU class within the ccu file ccu文件中的CCU类

class CCU:
    api_module = True
    actions    = ['post_new_purge','get_purge_queue','get_purge_status']

This will help me to dynamically document and grow the toolkit as more classes are added without having to maintain lists of imports each time a new sub component is added. 这将帮助我在添加更多类时动态记录和扩展工具包,而不必每次添加新的子组件时都维护导入列表。 Is there an easy way to do this? 是否有捷径可寻?

I know I can get a list of all the python files in the APIModules directory with a glob, but I then want to load and check any classes within the files without knowing what they might be called. 我知道我可以使用glob获取APIModules目录中所有python文件的列表,但是随后我想加载并检查文件中的所有类,而不知道它们可能被称为什么。

import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")

Since you would need to import the class to read its attributes, and you are trying to figure out if you should import it in the first place, you have a catch 22 here. 由于您需要导入该类以读取其属性,并且试图弄清楚是否应该首先导入它,因此这里有一个catch 22。

This leaves you with the approach of parsing the .py file with other methods and deciding based on that whether to import the module in question or not. 这样,您便可以使用其他方法来解析.py文件,并根据该方法决定是否导入相关模块。 But this approach is also messy and too complicated. 但是这种方法也很混乱而且太复杂。

The most simple approach, I think, would be to change the point of view completely. 我认为,最简单的方法是彻底改变观点。 Instead of having api_module = True inside every module, you can just have one list of active modules inside your main application, and decide based on that: 除了在每个模块内部都没有api_module = True ,您还可以在主应用程序内部只有一个活动模块列表,然后根据该列表进行决定:

api_modules = ["module_1", "module_2", "module_3"]

This reduces the amount of maintenance (you only have to maintain one single line of code) and provides the same amount of flexibility. 这减少了维护量(您只需要维护一行代码),并提供了相同的灵活性。

You could use metaclasses along with a registry of classes to track any new classes that are added to the system. 您可以将元类与类注册表一起使用,以跟踪添加到系统中的任何新类。

There's a walkthrough here that should give you the help you need http://effbot.org/zone/metaclass-plugins.htm 这里有一个演练,应该为您提供所需的帮助http://effbot.org/zone/metaclass-plugins.htm

Whether or not it's a good idea is another question (I think sqlalchemy uses this pattern for the declarative base and it works well). 这是否一个好主意是另一个问题(我认为sqlalchemy使用此模式作为声明性基础,并且效果很好)。

# from http://effbot.org/zone/metaclass-plugins.htm
registry = [] # list of subclasses

class Plugin(object):
    class __metaclass__(type):
        def __init__(cls, name, bases, dict):
            type.__init__(name, bases, dict)
            registry.append((name, cls))

# in your plugin modules
class SpamPlugin(Plugin):
    pass

class BaconPlugin(Plugin):
    pass

# in your plugin loader
# import all plugin modules

# loop over registered plugins
for name, cls in registry:
    if cls is not Plugin:
        print name, cls
        # here you could check various attributes of the class to
        # see if it was one you wanted to use

Edit I think I've sort of misunderstood slightly - you don't want to have to import the modules one by one either - but you could write something to import all of the files in the api folder. 编辑我想我有点误解了-您也不想一个接一个地导入模块-但是您可以编写一些内容来导入api文件夹中的所有文件。 Then each of the classes would inherit from the base API class that maintains the registry. 然后,每个类都将从维护注册表的基本API类继承。 When you want to work with anything you go to the registry to find the classes you're interested in. 当您想使用任何东西时,请转到注册表以查找您感兴趣的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM