Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Type conversion using type hinting

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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 text argument in display_text with String, but if the provided parameter will be str, call convert and assign the returned value to text without any additional code in a display_text function, 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')}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading