Say, I have a function that should do some internal stuff and display the provided text:
def display_text(text: str):
...
print(text)
There’s also a class with a convert() method:
class String:
def __init__(self, string: str):
self.string = string
def convert(self):
return self.string
Now, can you type hint text argument in display_text with String, but if the provided parameter will be str, call convert and assign the returned value to text? Like that:
def display_text(text: String):
...
print(text)
It should be done without any additional code in a display_text function, just with type hinting. I’ve seen that in some libs but couldn’t figure out how does it work.
I tried searching through some libraries’ (e.g. discord.py converters) code, searching similar questions on StackOverflow, only found out about typing.Protocol but still no idea how this conversion is done.
>Solution :
Can you type hint
textargument indisplay_textwithString, but if the provided parameter will bestr, callconvertand assign the returned value to text without any additional code in adisplay_textfunction, just with type hinting
No, not just with type hinting, since type annotations are absolutely inert at runtime and do nothing (and with from __future__ import annotations, they’re not even evaluated). If this is a trick question and that "in a display_text function" was the catch, then yes, you could @decorate your functions (or decorate a class holding them, or use a metaclass) to wrap functions using type annotations to cast arguments if needed.
An example of such a decorator:
import dataclasses
import inspect
from functools import wraps
from typing import Any
def convert_args(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
sig = inspect.signature(fn)
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
for name, val in bound_args.arguments.items():
param = sig.parameters[name]
if hasattr(param.annotation, "convert"):
bound_args.arguments[name] = param.annotation.convert(val)
return fn(*bound_args.args, **bound_args.kwargs)
return wrapper
@dataclasses.dataclass
class Shouty:
val: str
@classmethod
def convert(cls, val: Any):
return Shouty(val=str(val).upper())
@dataclasses.dataclass
class Shorten:
val: str
@classmethod
def convert(cls, val: Any):
return Shorten(val=str(val)[::2])
@convert_args
def display_text(arg1: Shouty, arg2: Shorten):
print(locals())
display_text("hello", "world, this is an example")
This prints out
{'arg1': Shouty(val='HELLO'), 'arg2': Shorten(val='wrd hsi neape')}