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/

Reply via email to