DEV Community

Super Kai (Kazuya Ito)
Super Kai (Kazuya Ito)

Posted on • Edited on

Type hint in Python (4)

Buy Me a Coffee

*Memo:

When defining a lambda, a lambda cannot be typed so calling an untyped lambda gets error in strict mode as shown below:

*Memo:

  • Calling an untyped lambda doesn't get error in unstrict mode.
  • By defining a typed variable or using cast(), a lambda can be typed.
lambda x: int: x -> int
lambda x: x -> int
lambda x: int: x
# Error

lambda x: x
# No error
Enter fullscreen mode Exit fullscreen mode
from typing import Callable, cast

(lambda x: x)(100)
# Error in strict mode

lam: Callable[[int], int] = lambda x: x

lam(100)
cast(Callable[[int], int], lambda x: x)(100)
# No error in strict mode
Enter fullscreen mode Exit fullscreen mode

Whether strict mode or not, the untyped variable assigned a value except some empty iterable(an empty list, set, frozenset, dictionary, iterator, etc) outside any functions doesn't get error as shown below:

*Memo:

  • The reason why the untyped variable assigned a value except some empty iterable outside any functions doesn't get error is because always needing a typed variable is too strict and too much, needing to write more code, making code complicated:
    • Whether strict mode or not, some empty iterable must be typed.
class Cls: ...
def func(x: int) -> None: ...

a = 100
b = 'Hello'
c = Cls
d = func
e = lambda x: x
f = ()          # Empty tuple
g = ''          # Empty str
h = b''         # Empty bytes
i = bytearray() # Empty bytestring
j = range(0)    # Empty range
# No error

k = []          # Empty list
l = set()       # Empty set
m = frozenset() # Empty frozenset
n = iter([])    # Empty iterator
# Error
Enter fullscreen mode Exit fullscreen mode

Type checkers infer the type of an untyped variable from the assigned value as shown below:

  • Type inference happens checking both sides(variable and value) in an asgstmt whether which side is first checked or not, which is called bidirectional type inference.
  • The doc explains type inference.
# At pre-runtime

from typing import reveal_type

# `v` is `int`
v = 100
reveal_type(v) # builtins.int

v = 200  # No error
v = 3.14 # Error
Enter fullscreen mode Exit fullscreen mode
# At pre-runtime

from typing import reveal_type

v1: int = 100

# `v2` is `int`
v2 = v1
reveal_type(v1) # builtins.int

v2 = 200  # No error
v2 = 3.14 # Error
Enter fullscreen mode Exit fullscreen mode

In strict mode, an untyped function gets error as shown below:

*Memo:

  • In unstrict mode, an untyped function doesn't get error.
  • Whether strict mode or not, error doesn't occur whether self is typed or not in the method which is the function within a class.
def func1(x): ...
def func2(x) -> str: ...
def func3(x: str): ...
# Error
Enter fullscreen mode Exit fullscreen mode
class Cls:
            # ↓↓↓↓↓↓↓↓↓ Typed
    def func1(self: Cls) -> None:
        return None

            # ↓↓↓↓ Untyped
    def func2(self) -> None:
        return None
# No error
Enter fullscreen mode Exit fullscreen mode

Type checkers set Any to an untyped lambda, and an untyped parameter(including *args, **kwargs or self), variable or return value within a function as shown below:

# At pre-runtime

from typing import reveal_type

v = lambda x=100, *args, **kwargs: None

reveal_type(v)
# def (x: Any =, *args: Any, **kwargs: Any)
Enter fullscreen mode Exit fullscreen mode
# At pre-runtime

from typing import reveal_type

# The type of `x`, `*args`, `**kwargs`, `y` and the return value are `Any`.
def func(x=100, *args, **kwargs):
    reveal_type(x)      # Any
    reveal_type(args)   # Any
    reveal_type(kwargs) # Any

    x = 200       # No error
    x = 3.14      # No error
    args = 200    # No error
    args = 3.14   # No error
    kwargs = 200  # No error
    kwargs = 3.14 # No error

    y = 100
    reveal_type(y) # Any

    y = 200  # No error
    y = 3.14 # No error

    return 100

v = func()

reveal_type(v) # Any

v = 200  # No error
v = 3.14 # No error
Enter fullscreen mode Exit fullscreen mode
# At pre-runtime

from typing import reveal_type

# The type of `v`, `self`, `x` and return value are `Any`.
class Cls:
    v = 100
    reveal_type(v) # builtins.int

    v = 200  # No error
    v = 3.14 # Error

    def func(self, x):
        reveal_type(self) # Any
        reveal_type(x)    # Any

        self = 200  # No error
        self = 3.14 # No error
        x = 200     # No error
        x = 3.14    # No error

cls = Cls().func(100)

reveal_type(cls) # Any

cls = 200  # No error
cls = 3.14 # No error
Enter fullscreen mode Exit fullscreen mode

The value None should be used as a type within a type hint because the type NoneType gets error as shown below:

v: None
# No error
Enter fullscreen mode Exit fullscreen mode
from types import NoneType

v: NoneType
# error: NoneType should not be used as a type, please use None instead
Enter fullscreen mode Exit fullscreen mode

With print(), printing the return value from the function which only returns None gets the error as shown below:

*Memo:

  • The error occurs both with and without --strict.
  • The error can be disabled using --disable-error-code with func-returns-value:
    • mypy --strict --disable-error-code func-returns-value test.py.
    • mypy --disable-error-code func-returns-value test.py.
  • Just calling but not printing the function doesn't get the error.
  • I reported the strange behaviour as the issue.
def func() -> None:
    return None

print(func())
# error: "func" does not return a value (it only ever returns None)
Enter fullscreen mode Exit fullscreen mode
def func() -> None:
    return None

func()
# No error
Enter fullscreen mode Exit fullscreen mode

Top comments (0)