3

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

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

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

从中派生出三个类,UnivariateGraphics,BivariateGraphics并分别MultivariateGraphics使用一、两或更多列data。让我展示一个:

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

单变量情况仅具有x和多变量情况作为单个columns属性。这失败了,因为在 MROxy之后height,并且width但是xy是强制性的,而height不是width。确切的错误是

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

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

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

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

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

4

2 回答 2

1

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

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

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

于 2018-10-15T07:21:50.473 回答
0

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

于 2018-09-24T20:36:18.467 回答