关于python:类模板的习惯用法或设计模式?

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
...


为什么不使用关键字参数并将它们全部收集到一个dict中呢?

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)

在这个例子中,并不需要将ReportCustomReport类分开,但最好保持原始类"干净"。

希望这有帮助:)


我自己发现了一些信息。

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))

更好的命名,可以使它方便。也许有更好的设计方案。


我看不出为什么你不能只用一个偏函数。