-
-
Notifications
You must be signed in to change notification settings - Fork 385
attrs-decorated classes cannot participate in cooperative inheritance #640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Generally speaking we're not super fond of subclassing (although the biggest chunk of work for the next release was fixing attrs behavior in diamond shaped hierarchies 😱) so attrs behavior is optimized for the case where you don't do subclassing at all or where you use attrs throughout (which gives you an optimized This will never change since our opinion on subclassing will not change. :) (I'll spare you the preaching here why ;)) Currently you can run super from a |
I feel you. I'm all for composition over inheritance. I avoid inheritance where possible. However, it is difficult to avoid inheritance. If a class expects a
So, if I understand correctly, the reason attrs doesn't call super is because the super call is too expensive? But you call
That would be fine except, unless I missed something, you're not able to forward If optimization is the reason that you don't unconditionally delegate to super, could I humbly request a flag on If cooperative is False, you should probably check to make sure that the decorated class does not inherit from anything. Also, the decorated class should raise in |
You might be able to use |
@euresti Thanks, that's true. |
We don't call
How would you expect it to work? Have you seen https://fuhm.net/super-harmful/? It's a bit more complicated than it sounds in practice. I promised not to preach, but I can't help myself: have you seen Brandon's brilliant post https://python-patterns.guide/gang-of-four/composition-over-inheritance/? Not telling you to convince you – telling you because I have the urge to tell everybody. :) |
I haven't seen this, but this looks very good! Great presentation of design patterns and yes to preferring composition over inheritance! I mentioned the principle earlier in this issue ("I'm all for composition over inheritance.") But like I said, composition is not always the best solution. Sometimes, you really do want inheritance. That's just a reality of design.
I don't really like the tone of frustration of this article, but I mainly agree with its conclusions:
Yes.
Yes.
Close: You don't need args.
Close. It's okay to have positional arguments like this:
Callers to
I contend that The way I see it, there are three choices for every attrs-decorated class
I understand your desire to “hyper-optimize”, so maybe the best option is the flag. My personal choice would be delegation since the kinds of programs I write are not affected by such minor computational optimizations. I mainly want attrs for the rapid prototyping. What makes me so uncomfortable though is that if I forget which classes are attrs-decorated, then I end up with subtle bugs when constructors aren't called. It's too much mental overhead to remember. What do you think? |
In the end, it was easier for me to just add cooperative_dataclasses. |
Uh oh!
There was an error while loading. Please reload this page.
Dear attrs team, great project!
Summary: Because attrs-decorated classes don't call super in
__init__
, they cannot participate in cooperative multiple inheritance.I might be alone in this interpretation, but I imagine that there are three fundamental kinds of inheritance patterns for methods, which I defined in my ipromise package: implementing an abstract method, overriding, and augmenting. If I had to choose, I would say that
__init__
should be an augmenting pattern.If that interpretation is correct, then
__init__
should call super. Even if a user wantsY
to override some behavior inX
, what happens ifZ
inherits fromY
andW
? Now,Y.__init__
's decision not to call super would mean thatW.__init__
would not be called. That seems like a bug. Instead, I would rather put the behavior thatY
wants to override in a separate method, sayX.f
, which is called inX.__init__
. Now, ifY.f
overridesX.f
, everything is okay. Even ifZ
inherits fromY
andW
, the override still works, andW.__init__
still gets called, angels sing, etc.Long story short, am I wrong to interpret
__init__
as an augmenting method and ensure that it always callsuper().__init__(**kwargs)
? Are there any downsides to attrs-generated__init__
providing this behavior?The text was updated successfully, but these errors were encountered: