4 ms·
Very good discussion from my wife's alma mater! One thing I didn't see mentioned that is a particularly annoying false positive in mypy is the enforcement of L
by nonameiguess 6y ago
Very good discussion from my wife's alma mater!
One thing I didn't see mentioned that is a particularly annoying false positive in mypy is the enforcement of Liskov Substitution for equality checks. All types have the common bottom type of object and object itself defines the __eq__ magic method as accepting any other object, which means all custom Python classes that want to define an equality check have to either define the '==' operator as valid accepting anything as a second operand, or tell mypy to ignore the error, which is the obviously saner thing to do since you want a type error, not a return of False, when you check equality against a completely unrelated type. Unfortunately, mypy provides no way to give you that statically.
I'm glad they mentioned the default parameter None to avoid the classic footgun of empty collections, though. That has to be the single most annoying false positive, when you declare a parameter as Optional and the type checker then complains it was set to None in the parameter definition but later set to an actual value, which you have to do because of the totally insane and unintuitive fact that all invocations of a Python function share the same parameter, so filling in the empty list with something else upon calling it means everything else that calls it now gets the changed default value instead of the empty list.
This is probably the single greatest rookie mistake made in Python because of how unexpected it is if you don't deeply understand the object model, but doing the literally recommended by the book solution to get around it results in the official type checker endorsed by Guido himself to report a type error.
- karlicoss 6y agoIt's possible to work around without forcing wrong type on parameter def f(l: List[int]=cast(Any, None)) -> int: if l is None: l = [] ... f() Using casts is useful for all sorts of things like this when sentinel values/objects are involved. People often feel it's somehow 'wrong', but it's fine to trick the type checker as long as it's localized to neighbouring lines of code :)
- nonameiguess 6y agoYou learn something every day! I didn't even know about the cast function.
- rattray 6y ago> you want a type error, not a return of False, when you check equality against a completely unrelated type What? No I don't? foo = if x: Foo else 1 foo == 1 # why would I want this to throw? I just want False! Disclaimer that I haven't done much Python recently (or Java, ever – I wonder if raise-on-== is a java-ism)
- marcosdumay 6y agoThe Python stdlib all seems to disagree with the GP and provide False for comparisons with values of different types. Personally, I didn't expect this, because Python tends to not like mixing types in any way, but it's the one place where it makes sense.
- bobbiechen 6y agoYup, that's true. In Python, generally if you want to only compare equal to the same type, you would use the NotImplemented value to implement: class MyClass: def __eq__(self, other): if not isinstance(other, MyClass): return NotImplemented else: # whatever your comparison is When NotImplemented is returned, there is a fallback sequence described in https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations https://docs.python.org/3/library/numbers.html#implementing-... - where basically, in the expression `A == B`, if `A.__eq__(B)` returns NotImplemented, then `B.__eq__(A)` is checked as well (falling back to an `is` identity comparison if both return NotImplemented). This is neat because it allows you to define a class B that can compare equal to an existing class A with the expression A == B, even though class A has no knowledge of B.
- wk_end 6y agoYou don't want it to raise an exception at run-time, but you probably want it to be a static type error: two things that aren't the same type can never be equal, so if you're bothering to compare them you're at best wasting cycles and at worst making a dumb mistake. In your example, `foo` could be marked (in mypy) a `Union[Foo, int]`, which would be a reasonable value to compare against an `int` and not a type error.
- david2ndaccount 6y agoYou want equality to return False for unrelated types for when you have dictionaries with heterogeneous types as keys.
- coldtea 6y agoOn the other hand, you probably don't want to have dictionaries with heterogeneous types as keys in the first place...
- meowface 6y ago>which you have to do because of the totally insane and unintuitive fact that all invocations of a Python function share the same parameter I think it does make sense if you consider the function signature an expression scoped one level above the function definition. I agree it's initially not intuitive, and it's tripped me up a few times before as a beginner, like probably most other beginners. As a more experienced Python developer, I think I'd actually now find it unintuitive if they changed it, due to everything I've internalized about Python scope, values, assignment, expressions, etc. Same for always requiring "self" as the first method argument. Overall, I'd prefer the changed versions, but I think they (or someone else) would basically need to make a whole new spiritual successor language.