类模板的成语或设计模式?

时间:2012-01-16 17:50:23

标签: python class design-patterns

我的代码库是用Python编写的。假设我有一个名为Report的相当通用的类。它需要大量参数

class Report(object):
  def __init__(self, title, data_source, columns, format, ...many more...)

报告中有很多实例化。这些实例化并非完全不相关。许多报告共享相似的参数集,只是稍有不同,例如具有相同的data_source和列但具有不同的标题。

不是复制参数,而是应用一些编程结构来使表达式更容易。我正在努力找到一些帮助,以便为此找出一些成语或设计模式。

如果报告的子类别需要一些额外的处理代码,子类似乎是一个不错的选择。假设我们有一个ExpenseReport的子类别。

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

但在很多情况下,子类别只是修复了一些参数而没有任何额外的处理。可以通过部分功能轻松完成。

ExpenseReport = functools.partial(Report,
                        data_source = EXPENSE_DATA_SOURCE,
                        columns = EXPENSE_COLUMNS,
                        format = EXPENSE_FORMAT,
                )

在某些情况下,甚至没有任何差别。我们只需要在不同的环境中使用同一个对象的2个副本,就像嵌入不同的页面一样。

expense_report = Report("Total Expense", EXPENSE_DATA_SOURCE, ...)
page1.add(expense_report)

...
page2.add(clone(expense_report))

在我的代码库中,使用了一种丑陋的技术。因为我们每个页面需要2个独立的实例,并且因为我们不想使用创建报告的长参数列表复制代码,所以我们只需克隆(Python中的深度复制)第2页的报告。不仅需要克隆不明显,忽略克隆对象而改为共享一个实例会在我们的系统中产生许多隐藏的问题和微妙的错误。

这种情况有什么指导吗?子类,部分功能还是其他成语?我希望这种结构轻巧透明。我对子类化略微警惕,因为它可能会导致子类的丛林。它诱使程序员添加特殊的处理代码,就像我在ExpenseReport中一样。如果有需要,我宁愿分析代码以查看它是否可以推广并推送到报告层。因此,报告变得更具表现力而无需在较低层进行特殊处理。

其他信息

我们使用关键字参数。问题更多的是如何管理和组织实例化。我们有大量具有常见模式的实例化:

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

5 个答案:

答案 0 :(得分:2)

为什么不直接使用关键字参数并将它们全部收集到dict

class Report(object):
  def __init__(self, **params):
      self.params = params
      ...

答案 1 :(得分:0)

我认为你不应该只使用部分功能。

答案 2 :(得分:0)

如果您的主要问题是类构造中的常见参数,可能的解决方案是编写类似:

的内容
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))

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

答案 3 :(得分:0)

我自己找到了一些信息。

予。 curry - 将参数与函数«Python recipes«ActiveState Code

相关联

http://code.activestate.com/recipes/52549-curry-associating-parameters-with-a-function/

看到整个讨论。尼克珀金斯对'轻量级'子类的评论与我所描述的类似。

II。 PEP 309 - 部分功能应用

http://www.python.org/dev/peps/pep-0309/

答案 4 :(得分:0)

这个问题已经过时了,但这可能仍然可以帮助那些偶然发现它的人......

我创建了一个名为classical的小型库来简化像这样的类继承案例(仅限Python 3)。

简单示例:

    visit | CREATE TABLE `visit` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `check_in_date` date DEFAULT NULL,
      `check_out_date` date DEFAULT NULL,
      `patient_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `patient_id2` (`patient_id`),
      CONSTRAINT `patient_id2` FOREIGN KEY (`patient_id`) REFERENCES 'patient` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
    ) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 |

    | p_d  | CREATE TABLE `p_d` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `patient_id` int(11) DEFAULT NULL,
      `disease_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `patient_id_idx` (`patient_id`),
      KEY `disease_id_idx` (`disease_id`),
      CONSTRAINT `disease_id` FOREIGN KEY (`disease_id`) REFERENCES `choroba` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
      CONSTRAINT `patient_id` FOREIGN KEY (`patient_id`) REFERENCES `patient` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
    ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 |

在此示例中,并不一定要将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) Report类分开,但保留原始类&#34;清洁&#34;。< / p>

希望这会有所帮助:)