我正在编写一个Python应用程序,它将命令作为参数,例如:
$ python myapp.py command1
Run Code Online (Sandbox Code Playgroud)
我希望应用程序是可扩展的,即能够添加实现新命令的新模块,而无需更改主应用程序源.树看起来像:
myapp/
__init__.py
commands/
__init__.py
command1.py
command2.py
foo.py
bar.py
Run Code Online (Sandbox Code Playgroud)
所以我希望应用程序在运行时找到可用的命令模块并执行适当的命令模块.
Python定义了一个__import__函数,它接受一个模块名称的字符串:
__import __(name,globals = None,locals = None,fromlist =(),level = 0)
该函数导入模块名称,可能使用给定的全局变量和局部变量来确定如何解释包上下文中的名称.fromlist给出了应该从name给出的模块导入的对象或子模块的名称.
所以目前我有类似的东西:
command = sys.argv[1]
try:
command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
# Display error message
command_module.run()
Run Code Online (Sandbox Code Playgroud)
这很好用,我只是想知道是否可能有更惯用的方法来完成我们正在使用此代码.
请注意,我特别不想使用鸡蛋或扩展点.这不是一个开源项目,我不希望有"插件".重点是简化主应用程序代码,并在每次添加新命令模块时无需修改它.
我已经看到很多人从模块中提取所有类的例子,通常是这样的:
# foo.py
class Foo:
pass
# test.py
import inspect
import foo
for name, obj in inspect.getmembers(foo):
if inspect.isclass(obj):
print obj
Run Code Online (Sandbox Code Playgroud)
真棒.
但我无法找到如何从当前模块中获取所有类.
# foo.py
import inspect
class Foo:
pass
def print_classes():
for name, obj in inspect.getmembers(???): # what do I do here?
if inspect.isclass(obj):
print obj
# test.py
import foo
foo.print_classes()
Run Code Online (Sandbox Code Playgroud)
这可能是非常明显的事情,但我找不到任何东西.谁能帮我吗?
我已经看到了以下问题,但它并没有让我想到我想要的地方:如何在Python中获取当前模块中所有类的列表?
特别是,我不想要导入的类,例如,如果我有以下模块:
from my.namespace import MyBaseClass
from somewhere.else import SomeOtherClass
class NewClass(MyBaseClass):
pass
class AnotherClass(MyBaseClass):
pass
class YetAnotherClass(MyBaseClass):
pass
Run Code Online (Sandbox Code Playgroud)
如果我使用clsmembers = inspect.getmembers(sys.modules[__name__], inspect.isclass)链接问题中的接受答案建议,它将返回MyBaseClass并且SomeOtherClass除了本模块中定义的3之外.
我怎么才能得到NewClass,AnotherClass而且YetAnotherClass?
我正在开发一个插件系统,插件模块加载如下:
def load_plugins():
plugins=glob.glob("plugins/*.py")
instances=[]
for p in plugins:
try:
name=p.split("/")[-1]
name=name.split(".py")[0]
log.debug("Possible plugin: %s", name)
f, file, desc=imp.find_module(name, ["plugins"])
plugin=imp.load_module('plugins.'+name, f, file, desc)
getattr(plugin, "__init__")(log)
instances=instances+plugin.get_instances()
except Exception as e:
log.info("Failed to load plugin: "+str(p))
log.info("Error: %s " % (e))
log.info(traceback.format_exc(e))
return instances
Run Code Online (Sandbox Code Playgroud)
代码有效,但是对于插件代码中的每个import语句,我都会收到如下警告:
plugins/plugin.py:2: RuntimeWarning: Parent module 'plugins' not found while handling absolute import
import os
Run Code Online (Sandbox Code Playgroud)
没有报告主程序代码的错误,插件工作.
有人可以解释警告意味着什么,我做错了什么.我是否需要单独创建一个空的插件模块并导入它以保持python的快乐?
我正在尝试找到创建类装饰器的最佳方法,该装饰器执行以下操作:
__init__被称为目前,我只是保存对"原始" __init__方法的引用,并将其替换为__init__调用原始和我的附加功能的方法.它看起来类似于:
orig_init = cls.__init__
def new_init(self, *args, **kwargs):
"""
'Extend' wrapped class' __init__ so we can attach to all signals
automatically
"""
orig_init(self, *args, **kwargs)
self._debugSignals()
cls.__init__ = new_init
Run Code Online (Sandbox Code Playgroud)
有没有更好的方法来"扩充"原件__init__或在其他地方注入我的电话?我真正需要的只是self._debugSignals()在创建对象后的某个时间调用它.我也希望它自动发生,这就是为什么我认为以后__init__是一个好地方.
值得一提的是这个装饰器的一些背景知识.你可以在这里找到完整的代码.装饰器的要点是自动附加到任何PyQt信号并在它们被发射时打印.当我装饰我自己的子类时QtCore.QObject,装饰器工作正常,但是我最近一直试图自动装饰所有QObject孩子.
我希望在应用程序中有一个"调试"模式,我可以自动打印所有信号,以确保事情按照我的预期进行.我相信这会导致TONS的调试,但我仍然希望看到发生了什么.
问题是我当前版本的装饰器在更换时QtCore.QObject.__init__导致了段错误.我试图调试这个,但代码是所有SIP生成的,我没有太多的经验.
所以,我想知道是否有更安全,更pythonic的方式来注入一个函数调用后__init__,希望避免段错误.
我正在尝试编写一个简单的插件系统。
a) 为此,我找到了使用 3.6 魔术函数__init_subclass__ /sf/answers/2934703201/的以下代码,该函数改编自 PEP 487,它注册列表中父类中定义的每个类plugins。
b)我现在想“强制”插件的编写者实现一个特定的方法,该方法将由我的程序调用。理想情况下,这会在导入时警告用户,他的插件将无法工作,但不会阻止程序运行。为此,我尝试将 ABC 添加为插件的父类,并添加一个@abstractmethod.
我最初尝试 ABC 和__init_subclass__:
from abc import ABC, abstractmethod
class Plugin(ABC):
plugins = []
@classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
try:
__class__.plugins.append(cls()) # Does not raise exception
except Exception as e:
print("Warning: Your plugin might not work, missing abstract method")
@abstractmethod
def do_something(self):
...
class Render(Plugin):
def __init__(self):
print("Render init")
# does not implement 'do_something'
for plugin in Plugin.plugins:
plugin.do_something() # Calls 'do_something' …Run Code Online (Sandbox Code Playgroud) python ×6
decorator ×1
import ×1
inspect ×1
plugins ×1
pyqt4 ×1
python-2.7 ×1
python-3.7 ×1
reflection ×1