Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I like adding | as an alias for Union, and the PEP has lots of examples of where typing.Union gets gnarly really quick, but that doesn't seem like a great example in the changelog:

    def square(number: int | float) -> int | float:
        return number ** 2
A TypeVar constrained to int and float, T = TypeVar('T', int, float), should be used instead to indicate that the return type is the same as the argument type, no?


This example demonstrates why I don't understand Python's type annotations. They seem to be incompatible with the language's core principle of duck typing.

Why would you constrain a function like `square` to only accept `int`s and `float`s? What if I want to square, say, a `fractions.Fraction`?


Interface typing > duck typing. With any sort of code completion, it's 1000x easier and more productive to figure out what goes where when the computer has some idea of the types of a given varaible, and hence what operations you can do to it.

It's like duck typing with a built-in Audubon society taxonomy chart.

You can always do whatever you want at runtime, it's still python.

IMHO, duck typing is strictly inferior to interface typing. You'd define a `Number` interface which `int`, `float`, ect all implement.


> Interface typing > duck typing.

Duck typing has been fundamental to Python for a long time. Do you think we're seeing a shift away from it? In the future, do you think "Pythonic" code will include significant use of explicit interfaces?


> Duck typing has been fundamental to Python for a long time. Do you think we're seeing a shift away from it?

We have been since at least the introduction of abstract base classes; even in purely dynamic python, having the ability to more explicitly declare and interrogate intent than pure duck typing is frequently useful.

> do you think "Pythonic" code will include significant use of explicit interfaces?

Sure, as it already does via abcs. But it will also still use lots of duck typing, though over time more of it will be “static duck typing” by way of protocols.

https://mypy.readthedocs.io/en/stable/protocols.html


Are "protocols" the python term for what Go (and I guess java but I'm way rusty at java) calls "interfaces"? eg the set of methods exposed by an object.


Yes.


Yes but if you want Interface Typing use Java.

But of course give the slightest whiff of types and the type police will come and want to shove types down your throat all the way


The "proper" way to annotate this function would be to use the numbers abstract base classes.

https://docs.python.org/3/library/numbers.html


But I don't think this will work on numpy matrix.


Should it?

  >>> import numpy as np
  >>> np.array("asdgh")**2
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: ufunc 'square' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''


Matrix, not array.

    >>> import numpy as np
    >>> np.mat(((1,0),(0,1)))**2
    matrix([[1, 0],
            [0, 1]])


That's a particularly confusing example to choose, because matrix squaring produces the same result as array (element-wise) squaring:

  >>> np.array([[1, 0], [0, 1]])**2
  array([[1, 0],
         [0, 1]])
Here:

  >>> np.array([[1, 2], [3, 4]])**2
  array([[ 1,  4],
         [ 9, 16]])
  >>> np.mat([[1, 2], [3, 4]])**2
  matrix([[ 7, 10],
          [15, 22]])
The real problem with the

  np.array("asdgh")**2
example is squaring doesn't make sense for character data.

Matrix vs. array squaring has other issues too, as matrix squaring only works with square matrices:

  >>> np.mat([1, 2])**2
  [...]
  LinAlgError: Last 2 dimensions of the array must be square
  >>> np.array([1, 2])**2
  array([1, 4])


There is a numbers.Number for that - but yeah, that's another way in which it's just not a good example..

I think typing_extensions.Protocol helps support the general duck typing "any object that has method x", though there are some implementation issues w/ dataclasses and such..


> There is a numbers.Number for that

That won't help if I want to square a matrix, or a polynomial.

> typing_extensions.Protocol helps support the general duck typing "any object that has method x"

Even if there's a `powerable` protocol that could be used to annotate `square` and `cube`, how would you annotate a slightly more complex function like `poly`?

    def square(x): return x**2
    
    def cube(x): return x**3

    def poly(x): return 3 * x**2 + 2 * x


It wouldn’t be a “squareable”

What’s you want is something like “commutative multiplication”


While duck typing solves this problem on paper, how is a user to find out whether square really accepts a matrix?

From comments? They are again limited by the authors imagination. From reading the source?


> how is a user to find out whether square really accepts a matrix?

How do you find out that a function exists, what it accepts, and what it does normally?

> From comments? They are again limited by the authors imagination.

Sure, but an author which much experience with duck typing will typically write documentation that specified that expected methods (informal structural “types”) rather than nominal types.

With the static equivalent (protocols), it’s conceivable that this is also discoverable via tooling.


Using maths to either oppose or support generic duck typing is just pointless, because it's easy to come up with counterexamples where a mathematical operation would or would not behave as expected.

In quite a few cases where one would overload a mathematical operation to, say, sum two objects of certain type, it'd be better to explicitly type out the operation as a functor/lambda.


Yes, read the comments or the source code. Or the documentation. Or `help(square)`. Or just try `square(Matrix([1, 2], [3, 4]))` in the REPL.

How would you use type annotations to show that `square` accepts a matrix (as well as an int, float, rational number etc.)?


> How would you use type annotations to show that `square` accepts a matrix (as well as an int, float, rational number etc.)?

Semi-seriously: by specifying the input type is a Monoid.


Maybe something like `numpy.typing.ArrayLike|numbers.Number`. Not incredibly precise. But if this covers also pandas.Series (not entirely sure), then I think it would cover 99.9% of my practical needs. numpy arrays are effectively the standard library datatype for matrices in Python land.


Type annotations are just hints and aren't enforced at runtime. You can pass whatever you want into that function, and you can also create a union type with the Fraction if you want the annotations to match your application.


> You can pass whatever you want into that function

Not really. Doing so won't cause an error at runtime, but it will produce an error when you run the type checker.


You seem to have removed the previous sentence which qualified the statement as "at runtime."


How can I pass whatever I want into that function at runtime, while still satisfying the type checker?


> How can I pass whatever I want into that function at runtime, while still satisfying the type checker?

Tell the type-checker to buzz-off by any number of means, including # type: ignore


You can use the Any type, or you can not annotate at all, though I'm not sure if the latter is something that passes some stricter modes of e.g. mypy.


Type hints don't help performance either because the interpreter discards them. The hints are not enforced by CPython, so you now need a build tool instead of just running the script. The whole point of PEP 484 seems to be to enable autocomplete in Visual Studio Code.


> The whole point of PEP 484 seems to be to enable autocomplete in Visual Studio Code.

It's to enable tooling, but not just autocomplete. The main purpose of typing is preventing type-related bugs, though making editor autocompletion and other editor information more robust is a not-insignificant additional benefit.

PEP 484 (created 29-Sep-2014, accepted 22-May-2015) wasn't motivated by VS Code (announced April 29, 2015).


PyCharm understands type hints for years now. Without using mypy btw. VSCode should be able to that, there should be one or more (either finished or half baked) plugins for it.

Edit: Microsoft Python Language Server has it (i read)


> Type hints don't help performance either because the interpreter discards them.

With CPython they don't. Cython actually uses annotations to optimize the generated code.


That's literally a different language though, but I get your point. CPython is the worst implementation.


It has nothing to do with any particular editor, it is for autocomplete and build time type checking in any editor and type checking in people's build chains.


Your code should get type checked whenever you run tests and can help reveal subtle bugs (for example, allowing Optional but not handling None in all code paths)


Another great value of running mypy on your code is that you ensure it can be compiled as a whole!


Define a protocol where __pow__ returns the type of self, and use that for the argument and return type of this function.


The word you are looking for is structural typing. (as opposed to nominal typing)

It's essentially type-safe duck typing. In the case of square, you say i want x and y to support the multiply-operator, but i don't care what type they are otherwise.

Available since 3.8: https://www.python.org/dev/peps/pep-0544/


You need an interface to code something. Otherwise how can your square function work?


I was just thinking that squaring an imaginary number results in a real number - that's a pretty crazy thing for a type system to handle, no? I guess that means the correct annotations would be more along the lines of:

    def square(number: numbers.Complex) -> numbers.Real: ...

    T = typing.TypeVar('T', numbers.Real)
    def square(number: T) -> T:
        return number ** 2
What a rabbit hole!


> squaring an imaginary number results in a real number

However, in general, squaring a complex number results in another complex number.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: