|
| 1 | +Literal types and Enums |
| 2 | +======================= |
| 3 | + |
1 | 4 | .. _literal_types:
|
2 | 5 |
|
3 | 6 | Literal types
|
4 |
| -============= |
| 7 | +------------- |
5 | 8 |
|
6 | 9 | Literal types let you indicate that an expression is equal to some specific
|
7 | 10 | primitive value. For example, if we annotate a variable with type ``Literal["foo"]``,
|
@@ -369,3 +372,118 @@ whatever type the parameter has. For example, ``Literal[3]`` is treated as a
|
369 | 372 | subtype of ``int`` and so will inherit all of ``int``'s methods directly. This
|
370 | 373 | means that ``Literal[3].__add__`` accepts the same arguments and has the same
|
371 | 374 | return type as ``int.__add__``.
|
| 375 | + |
| 376 | + |
| 377 | +Enums |
| 378 | +----- |
| 379 | + |
| 380 | +Mypy has special support for :py:class:`enum.Enum` and its subclasses: |
| 381 | +:py:class:`enum.IntEnum`, :py:class:`enum.Flag`, and :py:class:`enum.IntFlag`. |
| 382 | + |
| 383 | +.. code-block:: python |
| 384 | +
|
| 385 | + from enum import Enum |
| 386 | +
|
| 387 | + class Direction(Enum): |
| 388 | + up = 'up' |
| 389 | + down = 'down' |
| 390 | +
|
| 391 | + reveal_type(Direction.up) # Revealed type is "Literal[Direction.up]?" |
| 392 | + reveal_type(Direction.down) # Revealed type is "Literal[Direction.down]?" |
| 393 | +
|
| 394 | +You can use enums to annotate types as you would expect: |
| 395 | + |
| 396 | +.. code-block:: python |
| 397 | +
|
| 398 | + class Movement: |
| 399 | + def __init__(self, direction: Direction, speed: float) -> None: |
| 400 | + self.direction = direction |
| 401 | + self.speed = speed |
| 402 | +
|
| 403 | + Movement(Direction.up, 5.0) # ok |
| 404 | + Movement('up', 5.0) # E: Argument 1 to "Movemement" has incompatible type "str"; expected "Direction" |
| 405 | +
|
| 406 | +Exhaustive checks |
| 407 | +***************** |
| 408 | + |
| 409 | +Similiar to ``Literal`` types ``Enum`` supports exhaustive checks. |
| 410 | +Let's start with a definition: |
| 411 | + |
| 412 | +.. code-block:: python |
| 413 | +
|
| 414 | + from enum import Enum |
| 415 | + from typing import NoReturn |
| 416 | +
|
| 417 | + def assert_never(value: NoReturn) -> NoReturn: |
| 418 | + # This also works in runtime as well: |
| 419 | + assert False, 'This code should never be reached, got: {0}'.format(value) |
| 420 | +
|
| 421 | + class Direction(Enum): |
| 422 | + up = 'up' |
| 423 | + down = 'down' |
| 424 | +
|
| 425 | +Now, let's define an exhaustive check: |
| 426 | + |
| 427 | +.. code-block:: python |
| 428 | +
|
| 429 | + def choose_direction(direction: Direction) -> None: |
| 430 | + if direction is Direction.up: |
| 431 | + reveal_type(direction) # N: Revealed type is "Literal[ex.Direction.up]" |
| 432 | + print('Going up!') |
| 433 | + return |
| 434 | + elif direction is Direction.down: |
| 435 | + print('Down') |
| 436 | + return |
| 437 | + assert_never(direction) |
| 438 | +
|
| 439 | +And then test that it raises an error when some cases are not covered: |
| 440 | + |
| 441 | +.. code-block:: python |
| 442 | +
|
| 443 | + def choose_direction(direction: Direction) -> None: |
| 444 | + if direction == Direction.up: |
| 445 | + print('Going up!') |
| 446 | + return |
| 447 | + assert_never(direction) # E: Argument 1 to "assert_never" has incompatible type "Direction"; expected "NoReturn" |
| 448 | +
|
| 449 | +Extra Enum checks |
| 450 | +***************** |
| 451 | + |
| 452 | +Mypy also tries to support special features of ``Enum`` |
| 453 | +the same way Python's runtime does. |
| 454 | + |
| 455 | +Extra checks: |
| 456 | + |
| 457 | +- Any ``Enum`` class with values is implicitly :ref:`final <final_attrs>`. |
| 458 | + This is what happens in CPython: |
| 459 | + |
| 460 | + .. code-block:: python |
| 461 | +
|
| 462 | + >>> class AllDirection(Direction): |
| 463 | + ... left = 'left' |
| 464 | + ... right = 'right' |
| 465 | + Traceback (most recent call last): |
| 466 | + ... |
| 467 | + TypeError: Other: cannot extend enumeration 'Some' |
| 468 | +
|
| 469 | + We do the same thing: |
| 470 | + |
| 471 | + .. code-block:: python |
| 472 | +
|
| 473 | + class AllDirection(Direction): # E: Cannot inherit from final class "Some" |
| 474 | + left = 'left' |
| 475 | + right = 'right' |
| 476 | +
|
| 477 | +- All ``Enum`` fields are implictly ``final`` as well. |
| 478 | + |
| 479 | + .. code-block:: python |
| 480 | +
|
| 481 | + Direction.up = '^' # E: Cannot assign to final attribute "up" |
| 482 | +
|
| 483 | +- All field names are checked to be unique. |
| 484 | + |
| 485 | + .. code-block:: python |
| 486 | +
|
| 487 | + class Some(Enum): |
| 488 | + x = 1 |
| 489 | + x = 2 # E: Attempted to reuse member name "x" in Enum definition "Some" |
0 commit comments