A idiom or design pattern for class template?
我的代码库是用Python编写的。假设我有一个相当通用的类,叫做report。它需要大量的参数
1 2 | class Report(object): def __init__(self, title, data_source, columns, format, ...many more...) |
报告有很多实例。这些实例并非完全无关。许多报表共享相同的参数集,只因细微的变化而有所不同,例如具有相同的数据源和列,但标题不同。
为了使表达式这种结构更容易,应用了一些编程构造,而不是重复参数。我正试图找到一些帮助来整理我的头脑,为这个找出一些习语或设计模式。
如果报表的子类别需要一些额外的处理代码,那么子类似乎是一个不错的选择。假设我们有支出报告的子类别。
1 2 3 4 5 6 7 8 9 10 11 12 13 | class ExpenseReport(Report): def __init__(self, title, ... a small number of parameters ...) # some parameters are fixed, while others are specific to this instance super(ExpenseReport,self).__init__( title, EXPENSE_DATA_SOURCE, EXPENSE_COLUMNS, EXPENSE_FORMAT, ... a small number of parameters...) def processing(self): ... extra processing specific to ExpenseReport ... |
但在许多情况下,子类别只会修复一些参数,而不需要任何额外的处理。它可以很容易地用偏函数来完成。
1 2 3 4 5 | ExpenseReport = functools.partial(Report, data_source = EXPENSE_DATA_SOURCE, columns = EXPENSE_COLUMNS, format = EXPENSE_FORMAT, ) |
在某些情况下,甚至没有什么区别。我们只需要在不同的环境中使用同一对象的两个副本,比如嵌入到不同的页面中。
1 2 3 4 5 | expense_report = Report("Total Expense", EXPENSE_DATA_SOURCE, ...) page1.add(expense_report) ... page2.add(clone(expense_report)) |
在我的代码库中,使用了一种丑陋的技术。因为每个页面需要两个单独的实例,而且我们不想用创建报表的长参数列表复制代码,所以我们只克隆(python中的deepcopy)第2页的报表。克隆的需求不仅不明显,忽略了克隆对象,而是共享一个实例,这在我们的系统中造成了许多隐藏的问题和微妙的错误。
在这种情况下有什么指导吗?子类、部分函数或其他习语?我的愿望是让这座建筑变得明亮和透明。我有点担心子类化,因为它可能导致子类的丛林。它诱导程序员添加特殊的处理代码,就像我在ExpenseSreport中所做的那样。如果有需要,我宁愿分析代码,看看它是否可以被通用化并推送到报告层。这样,在不需要在较低层进行特殊处理的情况下,报表就变得更具表现力。
附加信息
我们确实使用关键字参数。问题更多的是如何管理和组织实例化。我们有大量使用通用模式的实例化:
1 2 3 4 5 6 7 8 9 10 11 | expense_report = Report("Expense", data_source=EXPENSE, ..other common pattern..) expense_report_usd = Report("USD Expense", data_source=EXPENSE, format=USD, ..other common pattern..) expense_report_euro = Report("Euro Expense", data_source=EXPENSE, format=EURO, ..other common pattern..) ... lot more reports ... page1.add(expense_report_usd) page2.add(expense_report_usd) # oops, page1 and page2 shared the same instance?! ... lots of pages ... |
为什么不使用关键字参数并将它们全部收集到一个
1 2 3 4 | class Report(object): def __init__(self, **params): self.params = params ... |
这个问题已经很古老了,但这可能仍然有助于那些偶然发现它的人……
我创建了一个名为Classic的小库,以简化类继承案例(仅限Python3)。
简单例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 | from classical.descriptors import ArgumentedSubclass class CustomReport(Report): Expense = ArgumentedSubclass(data_source=EXPENSE, **OTHER_EXPENSE_KWARGS) Usd = ArgumentedSubclass(format=USD) Euro = ArgumentedSubclass(format=EURO) PatternN = ArgumentedSubclass(**PATTERN_N_KWARGS) PatternM = ArgumentedSubclass(**PATTERN_M_KWARGS) # Now you can chain these in any combination (and with additional arguments): my_report_1 = CustomReport.Expense.Usd(**kwargs) my_report_2 = CustomReport.Expense.Euro(**kwargs) my_report_3 = CustomReport.Expense.PatternM.PatternN(**kwargs) |
在这个例子中,并不需要将
希望这有帮助:)
我自己发现了一些信息。
i.curry——将参数与函数关联?Python食谱?活动房地产代码
http://code.activestate.com/recipes/52549-curry-associating-parameters-with-a-函数/
看看整个讨论。尼克·帕金斯对"轻量级"子类的评论与我所描述的类似。
二。PEP 309——部分函数应用
http://www.python.org/dev/peps/pep-0309/
如果您的主要问题是类构造函数中的常见参数,则可能的解决方案是编写如下内容:
1 2 3 4 5 6 7 8 9 | common_arguments = dict(arg=value, another_arg=anoter_value, ...) expense_report = Report("Expense", data_source=EXPENSE, **common_arguments) args_for_shared_usd_instance = dict(title="USD Expense", data_source=EXPENSE, format=USD) args_for_shared_usd_instance.update(common_arguments) expense_report_usd = Report(**args_for_shared_usd_instance) page1.add(Report(**args_for_shared_usd_instance)) page2.add(Report(**args_for_shared_usd_instance)) |
更好的命名,可以使它方便。也许有更好的设计方案。
我看不出为什么你不能只用一个偏函数。