13

I want to type the parameter of a method to be one of a finite set of valid values. So basically, I would like to have the typing equivalent of the following minimal example:

valid_parameters = ["value", "other value"]

def typed_method(parameter):
    if not parameter in valid_parameters:
        raise ValueError("invalid parameter")

I checked typing already, but I didn't manage to find a solution. Maybe I was just not able to fully understand the documentation. Is there such a solution? Can it be created?

Jonathan Scholbach
  • 4,925
  • 3
  • 23
  • 44
  • FYI, this spawned that: https://stackoverflow.com/q/58522093/476 – deceze Oct 23 '19 at 11:42
  • In terms of coding style, I think type checking should be used to detect _syntactic_ errors but not _semantic_ errors. Usually syntactic errors can be avoided entirely by checking for type. Semantic errors -- passing values that 'do not make sense', like `parameter = "a different value"` -- should be handled separately and precisely in the way your minimal example does it (possibly with a more expressive exception type). Using type checking to check for semantic errors moves business logic (which can change frequently) to a place where a reader would not expect it. – Elias Strehle Oct 23 '19 at 11:58
  • 1
    @EliasStrehle I agree. My minimum example was just to illustrate what I want for typing. I do not want to replace the semantic error checking with syntactic typing. – Jonathan Scholbach Oct 23 '19 at 12:18
  • Possible duplicate of [Type hint for a function that returns only a specific set of values](https://stackoverflow.com/questions/39398138/type-hint-for-a-function-that-returns-only-a-specific-set-of-values) – Georgy Oct 23 '19 at 13:59

2 Answers2

20

This feature has just been introduced in Python 3.8: typing.Literal. See PEP 586 for details.

Example:

def typed_method(parameter: Literal["value", "other value"]):
    pass
Evan Porter
  • 2,987
  • 3
  • 32
  • 44
deceze
  • 510,633
  • 85
  • 743
  • 889
  • I have a list: `my_list = ["a", "b", "c"]`, and I want to use this list both as a value and a literal type. However, Literal complains when I do `Literal[my_list]`. Do you know how I can have a Literal of a predefined list? – Nihir Jun 12 '21 at 16:50
  • @Nihir: This is answered here: https://stackoverflow.com/questions/64522040/typing-dynamically-create-literal-alias-from-list-of-valid-values – Jonathan Scholbach Feb 19 '22 at 08:18
4

I want to type the parameter of a method to be one of a finite set of valid values

Use Enum

from enum import Enum


class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3


def handle_color(color):
    if not isinstance(color, Color):
        raise ValueError('Not a color')
    print(color)


handle_color(Color.GREEN)
handle_color('something')
balderman
  • 22,927
  • 7
  • 34
  • 52
  • 1
    This is not really an answer to the question, because the question asks for a solution with `typing`. – Jonathan Scholbach Oct 23 '19 at 11:29
  • 1
    +1 for the design pattern: even if the question is about type hinting for a set of values, for which there are valid use cases, actually using concrete types is an interesting alternative. Of course, this is a step away from Python idioms and towards static typing, said otherwise a trade between simplicity and robustness. – Joël Feb 17 '22 at 10:41