您忽略了谈论最重要的部分,即数据的形状。这真的是这里最重要的事情。“设计模式”让人分心——其中许多模式的存在是因为 Python 没有的语言限制,并且引入了不必要的僵化。
- 首先查看数据的形状。例如:
- 首先你有 XML
- 然后你有一些从 XML 中提取的数据集合(一个简单的 dict?一个嵌套的 dict?你需要什么数据?是同质的还是异构的?这是最重要的,但你不谈论它!)
- 然后在 SQL 后端序列化/持久化这些数据。
- 然后设计方法、属性甚至只是字典或元组中的项目的“接口”(口头描述),以促进对该数据的操作。如果你保持简单并坚持使用原生 Python 类型,你甚至可能不需要类,只需要函数和字典/元组。
- 重复迭代,直到获得应用程序所需的抽象级别。
例如,“提取器”的接口可能是“产生 xml 字符串的迭代”。请注意,这可以是生成器,也可以是带有__iter__
andnext()
方法的类!无需定义抽象类和子类!
添加到数据中的可配置多态性取决于数据的确切形状。例如,您可以使用约定:
# persisters.py
def persist_foo(data):
pass
# main.py
import persisters
data = {'type':'foo', 'values':{'field1':'a','field2':[1,2]}}
try:
foo_persister = getitem(persisters, 'persist_'+data['type'])
except AttributeError:
# no 'foo' persister is available!
或者,如果您需要进一步抽象(也许您需要添加您无法控制的新模块),您可以使用注册表(这只是一个字典)和模块约定:
# registry.py
def register(registry, method, type_):
"""Returns a decorator that registers a callable in a registry for the method and type"""
def register_decorator(callable_):
registry.setdefault(method, {})[type_] = callable_
return callable_
return register_decorator
def merge_registries(r1, r2):
for method, type_ in r2.iteritems():
r1.setdefault(method, {}).update(r2[method])
def get_callable(registry, method, type_):
try:
callable_ = registry[method][type]
except KeyError, e:
e.message = 'No {} method for type {} in registry'.format(method, type)
raise e
return callable_
def retrieve_registry(module):
try:
return module.get_registry()
except AttributeError:
return {}
def add_module_registry(yourregistry, *modules)
for module in modules:
merge_registries(yourregistry, module)
# extractors.py
from registry import register
_REGISTRY = {}
def get_registry():
return _REGISTRY
@register(_REGISTRY, 'extract', 'foo')
def foo_extractor(abc):
print 'extracting_foo'
# main.py
import extractors, registry
my_registry = {}
registry.add_module_registry(my_registry, extractors)
foo_extracter = registry.get_callable(my_registry, 'extract', 'foo')
如果需要,您可以轻松地在此结构之上构建一个全局注册表(尽管您应该避免全局状态,即使它不太方便。)
如果您正在构建公共框架并且您需要最大程度的可扩展性和形式主义并且愿意为复杂性付出代价,您可以查看类似zope.interface
. (金字塔使用它。)
与其推出自己的 extract-transform-load 应用程序,不如考虑使用 scrapy?使用scrapy,您将编写一个“Spider”,它被赋予一个字符串并返回项目序列(您的数据)或请求(请求更多字符串,例如要获取的URL)。这些项目被发送到一个可配置的项目管道,该管道在传递它们之前对它接收到的项目做任何它想要的事情(例如持久化在数据库中)。
即使你不使用 Scrapy,你也应该采用以数据为中心的类似管道的设计,并且更喜欢从抽象的“可调用”和“可迭代”接口的角度思考,而不是具体的“类”和“模式”。