Skip to content

Attributes with defaults not playing well with inheritance #93

@Tinche

Description

@Tinche

The problem is simple.

@attr.s class A: a = attr.ib() b = attr.ib(default=1) @attr.s class B(A): c = attr.ib() ValueError: No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: Attribute(name='c', default=NOTHING, validator=None, repr=True, cmp=True, hash=True, init=True, convert=None) 

Since, in the case of inheritance, attrs will order the attributes so:

  1. a
  2. b (default=1)
  3. c

and this is illegal (presumably because it would result in __init__(a, b=1, c), which is illegal Python).

The current approach has the benefit of the order of attributes being very clear, and it fits well with positional parameters (i.e. A(a=1, b=2) === A(1, 2)). This is a neat symmetry.

First of all, I don't see a way to make this work naturally (i.e. to make the example work as is), simply because of Python.

I can see several solutions to this problem (maybe there are more?).

The first solution: leave it as is, say it's unsupported.

The second solution: when processing an inheriting class, group non-default fields together first, then fields with defaults. So the order would instead be:

  1. a
  2. c
  3. b (default=1)

I think this would preserve backward compatibility. It's at odds with the point of the attribute order being significant that I mentioned earlier.

The third solution: this works only on Python 3. Introduce keyword-only parameters (https://www.python.org/dev/peps/pep-3102/) somehow. I would need to think about a nice API but let's pretend it'd just be:

@attr.s class A: a = attr.ib() b = attr.ib(default=1, kwonly=True) @attr.s class B(A): c = attr.ib() 

Then we'd basically do the same as for the second proposal, only for kwonly parameters, but now it's explicit. The generated __init__s would be:

class A: def __init__(self, a, *, b=1) class B(A): def __init__(self, a, c, *, b=1) 

I think this is nice since we're basically just exposing existing Python functionality.

If you like any of the proposals, I will put together a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions