Reputation: 346
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).
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
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 int
s or str
s, 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