On Wed, 4 May 2022 at 17:04, Christopher Barker <python...@gmail.com> wrote: > > Bringing this back on list -- I hope that was an accident.
If it was, it was me not spotting that your reply to me was on-list, I thought you're replied offlist so I followed suit. Not a problem though, I'm fine with this being on-list (although we're quite a lot off topic now, so others may want me to shut up ;-)) >> The problem with __post_init__ (I think) is >> that it confuses the type checkers (beware, untested code below!): > > Does the attrs approach work any better for this? Not notably. But I haven't explored it in detail, I'll be honest - there's a lot of options, and I may have missed a good one. >> @dataclass >> class Metadata: >> version: str # Actually a Version, but that's not the type we >> want the constructor to accept. >> >> def __post_init__(self): >> self.version = Version(self.version) >> >> And the reality is worse. We want the constructor to take a Version, >> or anything that can be converted to a Version > >> (which, if I check the >> source of the library, *is* just str - but I don't want to necessarily >> copy that declaration as it might change). And yet we want the >> typechecker to know that metadata.version is always a Version. All of >> that information is available statically (we call Version on the input >> data, and self.version is always the result of that call) but I can't >> see how to explain that to the type checker. > > > Frankly, I see that as a limitation of static typing -- how does one describe > "anything that can be turned into a Version? -- sure, the Version __init__ > can take either a Version or a str -- but only certain strs will make any > sense -- so what's the point? Well, while I do agree it's a limitation of static typing, if a value is passed to Version(), the type checker can infer that it must have a type that matches the argument declaration. The problem is that there's no way in Python to *describe* that type so that I can use it in the attribute declaration. So I have to copy the definition. And anyway, what I want is to say that the *attribute* has type Version, but the *constructor argument* has a different type. And I don't think type checkers can handle that, because of how dataclasses work (auto-generating the init function). > Honestly, I think if you want your code to be statically "safe" then you > should have this metadata class require a Version object, which would require > its users to do the conversion first. Nope, I really shouldn't. The reasons are very specific to the application logic, but a class that required the user to convert in advance would be useless to me. (Sure, a complete redesign of the app might make it possible, but that's not the point). > Which is why I like the Dynamic nature of Python, and haven't gotten excited > about static typing. I know, and I agree. But having autocompletion in VS Code is nice, and mypy checks do sometimes pick up on errors. But too much of my time is taken up trying to work around cases where type checkers get confused or upset about very dynamic code. And they don't typically degrade gracefully (by which I mean red "you got this wrong" error bars in my editor, or errors I don't know how to suppress in mypy without adding "I know what I'm doing" comments in my code. So I'm mostly uninterested in adding types, but when I do, I'm obsessive about getting them right :-( > I suppose the way to solve this without dataclasses or auto-assignment is to > type the class attribute and the __init__ parameter separately, yes? Possibly, I haven't experimented much. Although it's a PITA if, in def f(v): v1 = Version(v) reveal_type(v1) reveal_type(v) type checkers can't infer that v1 is of type Version, and v is of a type compatible with the argument of Version. Annoyingly, VS Code appears to get the type of v1, but not of v. And mypy says: ❯ mypy .\tyex.py tyex.py:5: note: Revealed type is "Any" tyex.py:5: note: 'reveal_type' always outputs 'Any' in unchecked functions tyex.py:6: note: Revealed type is "Any" tyex.py:6: note: 'reveal_type' always outputs 'Any' in unchecked functions > (which auto-assignment wouldn't help with, either) Indeed. [...] > What this tells me is that if you want dataclasses to be more > typing-friendly, then there should be a way to specify the __init__ type > separately, which could be done with a parameter to the Field object. Maybe. But really what I want is a way to say "this is what type the attribute is, but don't assume the init argument is the same type". Or just not bother with all this at all. This is where it starts to just become not worth trying to make dataclasses do what I want, I might as well do it myself via init=False. > Bringing it back OT: > > As much as I hate to say it: For folks advocating an auto-assignment feature > -- you should probably consider how it would play with static typing :-( That's probably a good point. Paul _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/WXM5CAI563ULEBTHO2JW6KB56P3O7QV5/ Code of Conduct: http://python.org/psf/codeofconduct/