我正在编写一个存储一些数据的Python应用程序。为了存储数据,我用抽象方法编写了一个Connection类(使用Python的abc模块)。这个类是所有存储后端派生自的超类。每个存储后端只有一个目的,例如,将数据存储在纯文本文件或XML文件中。
所有存储后端(包括超类所在的模块)都在一个名为“data_handler”的包中。每个后端都在一个模块中。
我的应用程序应该能够同时在多个后端存储数据,并在运行时确定哪些存储后端可用。为此,我想写一个单例类,每个后端必须在导入时注册。但这在动态语言中似乎不太好(如果我误解了这一点,请纠正我)。另一种方法是使用import data_handler
导入包,然后获取包的__file__
属性,并在dir中搜索超级连接类的子类的所有Python文件。
我应该使用什么方法,或者还有其他(可能更好)的方法来执行此操作。
的Stefan
在运行时发现严格要求的后端或静态 在代码中枚举它们吗?
当我添加新的后端
时,这个功能非常值得注意编辑代码但是,您的应用程序是否应始终写入所有后端?
我将有一个可以注册可用处理程序的类。并且数据应写入每个注册的处理程序。但并非所有可用的处理程序都必须注册。
答案 0 :(得分:3)
不执行文件系统(!)并扫描后端的Python源代码!在最好的时候,这是一个丑陋的黑客,更糟糕的是,因为你根本不需要任何类似的东西!在导入时注册所有类是完全可以的。
将后端存储在class属性中而不是实例属性中;这样,所有Storage
个实例都将查看同一组后端:
>>> class Storage(object):
... backends = set()
...
... def register(self, backend):
... self.backends.add(backend)
...
每个后端都可以通过实例化自己的Storage
来注册自己,backends
可以访问类级>>> foo = Storage()
>>> foo.register("text")
>>> bar = Storage()
>>> bar.register("xml")
属性:
Storage
您可以通过实例化另一个>>> baz = Storage()
>>> baz.backends
{'xml', 'text'}
来读取此属性,这将读取相同的变量:
Connection
您甚至可以将后端实例存储在>>> class Connection(object,metaclass=abc.ABCMeta):
... @abc.abstractmethod
... def register(self, backend):
... pass
...
... backends = set()
...
>>> class TextBackend(Connection):
... def register(self):
... super().backends.add(self)
...
... def __init__(self):
... self.register()
...
>>> class XMLBackend(Connection):
... def register(self):
... super().backends.add(self)
...
... def __init__(self):
... self.register()
...
>>> foo = TextBackend()
>>> bar = XMLBackend()
>>> Connection.backends
{<__main__.XMLBackend object at 0x027ADAB0>, \
<__main__.TextBackend object at 0x027ADA50>}
的类属性中,并在实例化时注册每个后端:
{{1}}
答案 1 :(得分:1)
如果要在各种Python发行版中分发这些后端,您可能需要查看setuptools / distribute入口点。这是一篇关于如何将它们用于动态插件查找服务的文章:
http://aroberge.blogspot.com/2008/12/plugins-part-6-setuptools-based.html
答案 2 :(得分:0)
但是,您的应用始终是否应该写入所有后端?如果没有,你可以使用(像往常一样)另一层间接,例如
storage = Storage()
storage.use(TextBackend, XMLBackend, YamlBackend)
storage.write(data)
或类似的东西,Storage
是调度程序,它只是循环后端并调用适当的序列化程序。
这当然非常粗糙。
答案 3 :(得分:0)
你可以使用像这样的函数:
def loadClass(fullclassname):
sepindex=fullclassname.rindex('.')
classname=fullclassname[sepindex+1:]
modname=fullclassname[:sepindex]
#dynmically import the class in the module
imod=__import__(modname,None,None,classname)
classtype=getattr(imod,classname)
return classtype
其中fullclassname是要加载的类的完全虚线限定符。
示例(伪代码,但想法就在那里):
对于软件包可用性扫描,只执行一些通配,然后为了查找最终类名,您可以在每个具有getStorage()的模块中声明一个Plugin类
#scan for modules , getPluginPackagesUnder (to be defined) returns the dotted name for all packages under a root path (using some globbing, listdir or whatever method)
pluginpackages=getPluginPackagesUnder("x/y/z")
storagelist=[]
for pgpck in plunginpackages:
pluginclass=loadClass("%s.Plugin"%pgpck)
storageinstance=Plugin().getStorage()
storagelist.append(storageinstance)
因此,您可以动态扫描现有的存储插件