【问题标题】:Controlling order of arguments in init of derived class defined with attrs控制用 attrs 定义的派生类的 init 中的参数顺序
【发布时间】:2019-02-25 01:27:10
【问题描述】:

attrs 中,生成的init 方法的参数顺序由类中的属性定义顺序+ MRO 确定(一种基于多重继承关系定义总顺序的标准方法)。这对我的用例不利,但似乎没有任何灵活性。这是用例:

我正在使用attrs 来定义一些建模图形基元的类。这些原语是相关的,因为它们都需要数据来处理并生成具有给定高度和宽度的图形,这些图形具有默认值。所以在顶层有一个类

@attr.s
class BaseGraphics:
    data = attr.ib()
    height = attr.ib(default=300)
    width = attr.ib(default=400)

由此派生出三个类,UnivariateGraphicsBivariateGraphicsMultivariateGraphics,它们分别在 data 中使用一、两或更多列。让我展示一个:

@attr.s
class BivariateGraphics(BaseGraphics):
    x = attr.ib()
    y = attr.ib()

单变量案例只有x,多变量案例只有一个columns 属性。这失败了,因为在 MRO 中 xyheightwidth 之后,但 xy 是强制性的,而 heightwidth 不是强制性的。确切的错误是

ValueError: No mandatory attributes allowed after an attribute with 
a default value or factory.  Attribute in question: 
Attribute(name='x', default=NOTHING, validator=None, repr=True, 
cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=None, 
converter=None, kw_only=False)

我可以为xy 设置默认值,就像第一列和第二列一样,但顺序仍然是错误的。例如,如果想写类似的东西

BivariateGraphics(iris_data, "petalWidth", "sepalWidth")

第二个和第三个参数将被解释为heightwidth,而不是xy。我可以通过将所有属性设置为仅data 关键字来防止此错误,但我不支持此语法。通过阅读几个相关问题,例如#38,看来这是推荐的方法。关闭但没有雪茄。

另一种解决方法是将heightwidth 分别添加到每个派生类。这将违反 DRY 原则,并且无法表达和执行类之间的这种共性。有超过三个类,它会变得非常讨厌。

这不仅仅是一个“学术”问题。我在包autosig 中使用attrs,以帮助以一致的方式定义API。这又在统计图形包altair_recipes 中使用,实际发生上述情况的地方(嗯,在下一个版本中,当我需要将heightwidth 添加到所有图形基元时)。

我可以向开发人员提出问题,但由于主要开发人员开玩笑地 (?) 威胁使用电击子类的人,我觉得这将是徒劳的。我会对不需要 DRY 违规或样板的非继承解决方案感兴趣。谢谢

【问题讨论】:

    标签: python-attrs


    【解决方案1】:

    在这种情况下,您希望能够依赖稳定的 API(尤其是在子类化时顺序并不总是一目了然),我们强烈建议使用类方法工厂(这也是我倾向于在我自己的代码中使用)。每当您开始在类中进行更改时,事情都会变得一团糟,尤其是在您必须浏览多个层次结构时。因此,最好按照自己的方式构建 API,并将 attrs 仅用于管道。

    但是由于主要开发者开玩笑地(?)威胁了用电击子类的人

    虽然我确实对子类化持相当强硬的立场,但我很难想象用电击威胁除我自己以外的任何人的记录,除非这是在志同道合的朋友下开玩笑。如果我确实有这样的判断失误,我很抱歉。

    【讨论】:

    • 我找不到电击报价,所以可能是一个错误,轮到我道歉了。但如果我记得的话,那很有趣,一点也不威胁。我会考虑 classmethod 方法以及它如何应用于我的用例,谢谢。
    • @hynek - 您能否添加一个使用“类方法工厂”的示例
    • 初始化文档有一个例子:attrs.org/en/stable/init.html
    【解决方案2】:

    我决定避免使用继承并将其替换为在生成类之前起作用的组合器。为此,我使用了attr.make_class 而不是类语法,它接受属性作为OrderdDict 并遵守顺序。因此,上述组合器只是将OrderedDicts 与一些约定组合以定义最终顺序的一种方式。不确定这是否通过了解决方法级别以获得答案状态,但它让我感动。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-24
      • 1970-01-01
      • 1970-01-01
      • 2012-03-11
      • 2016-11-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多