【问题标题】:Should properties do nontrivial initialization?属性应该进行非平凡的初始化吗?
【发布时间】:2009-09-06 17:35:26
【问题描述】:

我有一个对象,它基本上是 Oracle 序列的 Python 实现。由于各种原因,我们必须获取 Oracle 序列的 nextval,在确定主键时手动计数,然后在插入记录后更新序列。

以下是我的对象执行的步骤:

  1. 构造一个对象,其 key_generator 属性最初设置为 None。
  2. 从数据库中获取第一个值,将其传递给 itertools.count。
  3. 使用属性 next_key 从该生成器返回密钥。

我有点不确定在哪里执行第 2 步。我可以想到三种可能性:

  1. 在构造函数中跳过第 1 步并执行第 2 步。我觉得这很邪恶,因为我倾向于不喜欢在构造函数中进行这种初始化。
  2. 使next_key在第一次调用时从数据库中获取起始键。我觉得这很邪恶,因为属性通常被认为是微不足道的。
  3. next_key 变成get_next_key 方法。我不喜欢这个,因为这里的属性看起来更自然。

三害孰轻孰重?我倾向于 #2,因为只有第一次调用此属性才会导致数据库查询。

【问题讨论】:

    标签: python properties initialization


    【解决方案1】:

    我想你的疑惑来自PEP-8

        Note 3: Avoid using properties for computationally expensive
        operations; the attribute notation makes the caller believe
        that access is (relatively) cheap.
    

    遵守标准行为通常是个好主意;这将是放弃解决方案 #2 的原因。

    但是,如果您感觉接口在属性方面比方法更好,那么我会简单地记录第一次调用更昂贵,然后继续使用(解决方案 #2)。
    归根结底,建议是用来解释的。

    【讨论】:

    • 我想另一个问题是“你如何定义计算成本高”?无论如何,我已经决定了另一条路线。我会将其作为单独的答案发布,但请接受,因为我认为这是这方面的最佳答案。
    【解决方案2】:

    我同意属性访问和所有看起来像它的东西(即 Python 上下文中的属性)应该是相当微不足道的。如果一个属性要执行一个潜在的代价高昂的操作,请使用一种方法来明确这一点。我推荐使用“fetch_XYZ”或“retrieve_XYZ”之类的名称,因为“get_XYZ”在某些语言(例如 Java)中用作简单属性访问的约定,非常通用,而且听起来也不“昂贵”。

    一个好的指导原则是:如果您的属性可能引发不是由于编程错误而引发的异常,那么它应该是一个方法。例如,从属性中抛出(假设的)DatabaseConnectionError 是不好的,而抛出 ObjectStateError 是可以的。

    此外,当我正确理解您时,您希望在访问 next_key 属性时返回下一个键。我强烈建议不要在您的属性中使用副作用(除了缓存、廉价的延迟初始化等)。属性(以及相关的属性)应该是幂等的。

    【讨论】:

    • 好点。 next_key 不是幂等的事实是我没有考虑过的。
    【解决方案3】:

    我已经确定我提出的解决方案中的关键气味是我正在创建的属性中包含“下一个”这个词。因此,我决定将我的 DatabaseIntrospector 类转换为 KeyCounter 类并实现迭代器协议,而不是创建 next_key 属性(即创建一个简单的旧 next 方法来返回下一个键)。

    【讨论】:

      猜你喜欢
      • 2012-11-17
      • 2014-01-12
      • 2011-12-13
      • 2015-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多