您忽略了最重要的部分,即数据的形状。这真的是最重要的事情。 "设计模式" 是一个干扰因素 - 许多这些模式存在是因为 Python 没有的语言限制,并引入了不必要的严格性。
1. 首先看看你的数据形状。例如:
- 首先你有 XML
- 然后你有从 XML 中提取的一些数据集合(一个简单的字典?一个嵌套的字典?你需要什么数据?它是同质还是异质的?这是最重要的事情,但你没有谈论它!)
- 然后将这些数据序列化 / 持久化在 SQL 后端中。
2. 然后设计方法、属性或甚至只是字典或元组中的项的“接口”(口头描述),以便对该数据进行操作。如果保持简单并坚持使用本机 Python 类型,则可能不需要类,只需函数和字典/元组。
3. 反复迭代,直到您获得所需的应用程序抽象级别。
例如,“提取器”的接口可能是“可迭代的,产生XML字符串”。请注意,这可以是生成器或具有__iter__和next()方法的类!无需定义抽象类并对其进行子类化!
您添加到数据中的可配置多态性取决于数据的确切形状。例如,您可以使用约定:
def persist_foo(data):
pass
import persisters
data = {'type':'foo', 'values':{'field1':'a','field2':[1,2]}}
try:
foo_persister = getitem(persisters, 'persist_'+data['type'])
except AttributeError:
或者,如果您需要更进一步的抽象(可能需要添加您无法控制的新模块),您可以使用注册表(只是一个字典)和模块约定:
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)
from registry import register
_REGISTRY = {}
def get_registry():
return _REGISTRY
@register(_REGISTRY, 'extract', 'foo')
def foo_extractor(abc):
print 'extracting_foo'
import extractors, registry
my_registry = {}
registry.add_module_registry(my_registry, extractors)
foo_extracter = registry.get_callable(my_registry, 'extract', 'foo')
如果你想要的话,可以在这个结构之上轻松地构建一个全局注册表(尽管即使有点不太方便,你应该避免使用全局状态)。
如果你正在构建公共框架,并且需要最大程度的可扩展性和形式化,并且愿意付出复杂性的代价,那么你可以看一下类似 zope.interface
这样的东西。(Pyramid 使用了它。)
与其自己编写提取-转换-加载应用程序,你是否考虑过 scrapy?使用 scrapy,你将编写一个“Spider”,它会接收一个字符串并返回 Item 序列(即你的数据)或 Request 序列(即请求更多字符串,例如要获取的 URL)。在可配置的 Item Pipeline 中,Item 会被发送到它接收到的任何 Item 上执行的操作中(例如在数据库中持久化),然后将它们传递下去。
即使你不使用 Scrapy,你也应该采用以数据为中心的流水线式设计,并优先考虑抽象的“可调用”和“可迭代”接口,而不是具体的“类”和“模式”。
dict
的变体”的说法--这并不正确--用户可以定义不是dict
的映射。至于另一点,这当然是个人喜好问题。但我已经看到有些人过分地使用了几次:def square(x): return x*x
。那是一个非常无用的函数,“只是为了以防万一我想改变平方”。将本应该是普通函数的东西制作成类也是同样的道理。当然,我并不是说“内联一切”...只是试图做到实际。 - mgilson