user3608078
user3608078

Reputation: 346

How to use a function parameter as a type hint?

I have a check_value function which expects the parameter value and value_type, compares type(value) with value_type and depending on the outcome returns the value or raises an Exception. Now I want to use type hints to annotate both parameters and the return type.

def check_value(value: "value_type", value_type: type) -> "value_type":
    if type(value) is value_type:
        return value
    else:
        raise Exception(f"Value '{value}' should be of type '{value_type}' instead of type '{type(value)}'.")


if __name__ == '__main__':
    print(check_value(2, int)) # works, returns
    print(check_value(2, str)) # works, raises Exception

The annotation of the parameter value_type works fine, but forward referencing value_type as a type hint (since the type of value/the returned value are of type value_type) causes an warning in Pycharm_2018.1.4 (see picture below).

enter image description here

Is it a bug in Pycharm? Did I do something wrong? Is it not possible to use type hints this way?

Thanks!

Upvotes: 3

Views: 2654

Answers (1)

Blckknght
Blckknght

Reputation: 104802

I don't think Python's static type checking system can support a function like the one you've shown. That's because the value of value_type is not static at all, but determined at runtime. I think, the best you can do is something like this:

T = typing.TypeName("T")

def check_value(value: T, value_type: type) -> T:
    ...

The fact that the value_type argument should be bound to the same type that T represents is not something that can be represented with type annotations.

But if you're doing static type checking, this kind of function shouldn't be necessary. If you've properly annotated the places where value comes from and the places it will be used, the static type checker should already know if it is of an appropriate type, without needing there to be a function to check it at runtime. It won't really work in your example, since print accepts Any arguments, but for functions that specifically expect either ints or strs, you should just pass the value in and let the type checker spot the issue:

def source_of_ints() -> int:
    return 2

def function_that_takes_an_int(value: int) -> None:
    print(value) # or whatever

def function_that_takes_a_str(value: str) -> None:
    print(value) # or whatever

if __name__ == '__main__':
    value = source_of_ints()           # value's type can be inferred by static type checker
    function_that_takes_an_int(value)  # works and passes type check too
    function_that_takes_a_str(value)   # works at runtime, but type checker will see an error

Upvotes: 2

Related Questions