Live data from Hacker News

Python types have an expectations problem

medium.com

11–20 of 115 posts

Re: Python types have an expectations problem

#11
In Python it's common to have functions whose return types depend on the run-time arguments.

For example:

    def fooify(x):
      if isinstance(x, list):
        map(fooify, x)
      else:
        x * 2
I guess this is to make scripting more forgiving.

In typical typed languages, you would have two functions instead:

    fooify : number -> number

    fooifyMany : [number] -> [number]
But in the Python community, it's common to have a big function with many behaviors. However, then the type annotations cannot be so precise:

    fooify : any -> any
Not an experienced Python dev, so curious how this works in practice.

Re: Python types have an expectations problem

#12
post #6

A note; typescript does nothing to ensure types are correct at runtime. Especially in the browser. You need to do runtime checking even if you're using typescript. In Python, yeah, they're called Type _Hints_ for a reason. Don't count on them at runtime here either. Both are dynamic languages, it's hard doing anything meta/schema driven with rigid types. If you really want a hard type system, just move to GO or Rust…

C's type checker is the MMU - when you mess up types, it give you a segfault. If you're lucky.

Re: Python types have an expectations problem

#13

In Python it's common to have functions whose return types depend on the run-time arguments. For example: def fooify(x): if isinstance(x, list): map(fooify, x) else: x * 2 I guess this is to make scripting more forgiving. In typical typed languages, you would have two functions instead: fooify : number -> number fooifyMany : [number] -> [number] But in the Python community, it's common to have a big function with man…

You can annotate OR types in python so in this case you could do

def fooify(x: int | list[int])

Re: Python types have an expectations problem

#14

In Python it's common to have functions whose return types depend on the run-time arguments. For example: def fooify(x): if isinstance(x, list): map(fooify, x) else: x * 2 I guess this is to make scripting more forgiving. In typical typed languages, you would have two functions instead: fooify : number -> number fooifyMany : [number] -> [number] But in the Python community, it's common to have a big function with man…

That could be a Union[float, list[float]]. Union types are very common!

In fact, I think TypeScript will, for your given example, with the 'else' clause accurately identify the type of x to be a float if it's a union like the one I wrote down above.

Re: Python types have an expectations problem

#15

In Python it's common to have functions whose return types depend on the run-time arguments. For example: def fooify(x): if isinstance(x, list): map(fooify, x) else: x * 2 I guess this is to make scripting more forgiving. In typical typed languages, you would have two functions instead: fooify : number -> number fooifyMany : [number] -> [number] But in the Python community, it's common to have a big function with man…

This is function overloading, you can do the same thing in C++. In Python you type this using the overload decorator from the typing module.

Re: Python types have an expectations problem

#16
I don't really like types on the function declaration line.

Instead of this:

    def check_permission(user: User, perm: str, obj: BaseModel | None) -> bool:
I find it much nicer to read something this:

    """
    Check if user is allowed to drive a car
    :user: User             # The application's User model
    :perm: str              # Our magic permission string. Tab seperated.
    :obj : Basemodel | None # Base model of all our objects since 2017
    :returns bool           # True if the user is allowed to drive
    """
    def check_permission(user, perm, obj):
This way I can grok the code much faster and only have to look into the type declaration when I want to. Due to syntax highlighting, it will look really nice. Because the whole type part can be styled in a dimmer color which puts it into the background. And I can define a key combo to show/hide the whole type part.

Re: Python types have an expectations problem

#17

In Python it's common to have functions whose return types depend on the run-time arguments. For example: def fooify(x): if isinstance(x, list): map(fooify, x) else: x * 2 I guess this is to make scripting more forgiving. In typical typed languages, you would have two functions instead: fooify : number -> number fooifyMany : [number] -> [number] But in the Python community, it's common to have a big function with man…

In practice, this would be solved with `typing.overload`[0].

Using you example:

    from typing import overload
 
    @overload
    def fooify(x: int) -> int:
      ...
 
    @overload
    def fooify(x: list[int]) -> list[int]:
      ...
 
 
    def fooify(x: list[int] | int) -> list[int] | int:
      if isinstance(x, list):
        return [fooify(_x) for _x in x]
      return x * 2
 

[0] https://docs.python.org/3/library/typing.html#overload

Re: Python types have an expectations problem

#18
post #9

"I’d have to set up some CI with the type checking step and make it impossible to deploy the code that doesn’t pass it, maybe." Yes. That's how people successfully use type checking in Python.

I did that for Python, and now I do it for PHP as well.

Trivial to set up but ends up being a huge time saver, especially when reviewing junior colleagues code - don't even ping me to review your code until you've managed to convince the static analyzer it will work!

Re: Python types have an expectations problem

#19
post #3

Summarizing quote: “ Python type hints are a core part of the language, they even have standard library modules (typing), and yet they don’t do anything when used in that language without some external tooling. That, to me, is a bit of an expectations mismatch. ” I.e. they’re complaining that a type checker like mypy isn’t run by default.

I think it's a reasonable complaint and I think that there should be some "strict" annotation that forces python to do that type checking before running the code, which should be completely backwards compatible.

Let's think of this in a deploy environment like kubernetes -- if you don't have such a check, then you could deploy code that fails _at runtime_, causing an outage, because someone made a mistake with types.

If you fail _at startup_, then the deploy will never go healthy and will fail, leaving the old pods still running, causing no interruption in service.

And yes you _should_ have this check in CI, but there's no reason not to have defense in depth.

Re: Python types have an expectations problem

#20

In Python it's common to have functions whose return types depend on the run-time arguments. For example: def fooify(x): if isinstance(x, list): map(fooify, x) else: x * 2 I guess this is to make scripting more forgiving. In typical typed languages, you would have two functions instead: fooify : number -> number fooifyMany : [number] -> [number] But in the Python community, it's common to have a big function with man…

That could be a Union[float, list[float]]. Union types are very common! In fact, I think TypeScript will, for your given example, with the 'else' clause accurately identify the type of x to be a float if it's a union like the one I wrote down above.

Sorta? The function does return a union type, in isolation. At most callsites, you would know which of the two you are getting. This is much closer to generic invocation, if I remember the name correctly. Was very common in a lot of older dynamic languages.

In fact, in a lot of languages, you can't tell this is a float, statically. It would work with whatever type was passed in that supports multiplication. And return the appropriate type. Right?

Post reply on HN