首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >用Python初始化复合类

用Python初始化复合类
EN

Software Engineering用户
提问于 2016-02-19 14:38:18
回答 1查看 1.2K关注 0票数 2

我正在编写一个Python,其中我有一个主要类(PlayerModel),它有另外两个类作为成员(StateModelActionModel)。我想知道,初始化这种复合类的正确方法是什么?

目前。我将分别创建成员实例,类的__init__方法如下所示:

代码语言:javascript
运行
复制
def __init__(self, state_model, action_model):
    self.state_model = state_model
    self.action_model = action_model

但是,这意味着类在编译时不知道成员的类型(例如,我丢失了自动完成,这很烦人)。

当然,我可以实例化主类中的成员,但这意味着我必须始终将他们的参数传递给主类构造函数,将其转换为具有5-6参数的方法,我不喜欢这些参数(Bob叔叔也不喜欢)。

对于这个问题,有比将成员类的参数聚合为命名元组更优雅的解决方案吗?

EN

回答 1

Software Engineering用户

回答已采纳

发布于 2016-02-19 15:42:45

首先,请注意Python是一种动态语言。不管您使用什么解决方案,“成员的类型在编译时都不为类所知”,因为Python没有为变量或类成员分配类型的静态类型系统。好的自动完成是有价值的,但是你不应该把你的设计押在你的编辑器的局限性上。

在类内部或外部实例化字段时,有几个重要的权衡。

  • 如果在外部实例化,那么实例化我们类的人需要了解类的内部细节,以便创建这两个依赖关系。这本质上是对德米特定律的违反。另一方面,这些依赖项可能在类之外发挥有意义的作用。
  • 如果在外部实例化,我们不能确定我们的类拥有对内部对象的唯一引用。这会导致其他代码的错误,从而改变我们内部对象的状态。其他对象可以安全地共享,甚至应该共享(例如,一些外部连接)。
  • 如果在内部实例化,我们就不能轻易地提供一个模拟对象来测试我们的类。

对我来说,决定性的问题是,内部对象是可能在运行时被选中的依赖项,还是太大,需要进行隔离测试。在你的例子中,答案可能是“是”。在外部实例化这些类要合理得多。

代码语言:javascript
运行
复制
player_model = PlayerModel(
    state_model=StateModel(...),
    action_model=ActionModel(...))

而不是将其他模型的实例化添加到它已经拥有的所有职责中。特别是,实例化构造函数中的所有内容的策略会导致更大、更臃肿的构造函数,从而进一步查找使用链。这不仅是计算参数的问题,也是有关分离关注的问题。

如果调用方负责实例化所有依赖项是不合理的,我有时会创建一个工厂来执行此操作:

代码语言:javascript
运行
复制
class Dependencies(object):
  def __init__(self, global_configuration):
    ...

  def new_player_model(self, ...):
    return PlayerModel(
      state_model=self.new_state_model(...),
      action_model=self.new_action_model(...))

  def new_state_model(self, ...): return StateModel(...)
  def new_action_model(self, ...): return ActionModel(...)

然后:

代码语言:javascript
运行
复制
deps = Dependencies(...)
player_model = deps.new_player_model(...)

通常,许多构造函数参数只是必须满足的各种依赖关系。Dependencies对象中的工厂方法只需转发其他参数,因为依赖项可以在内部管理。在这种情况下,集中依赖管理可能非常有用。

如果大量的争论是不可避免的,那么有几种策略可以解决这一问题。最重要的是内置Python:命名参数。在声明函数def foo(a, b, c): ...时,可以以foo("a", c="c", b="b")的形式调用它。注意,这些标签是可选的,顺序是任意的,如果忘记了任何必需的参数,Python将抛出一个错误。这使得您的参数的变量名非常重要,因为它们可能用于命名参数。使用命名参数,管理大量的参数是合理的。在这里,针对“Bob叔叔”和其他参数的建议只适用于局限性,因为它们的建议主要针对Java等缺乏命名参数的语言。

但是,命名参数并不能解决所有问题(matplotlib.pyplot是我最喜欢的反例)。通常情况下,会有自然的参数组。某些参数只能一起使用(例如,xy坐标)。然后,我们可以引入包含这些参数的小对象,而不执行其他操作。应用于您的情况,我们可能有StateModelArgsActionModelArgs或其他分组。然后调用者提供这些参数,但是实际的对象是从类中的这些参数实例化的。这种策略有一些优点,例如在参数对象中可以更容易地进行验证,但它们也可能提供的值太少,因此更容易直接实例化实际对象。

所以使用哪种策略在很大程度上取决于具体的情况,而这两种选择都是有意义的。

票数 2
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/310538

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档