Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I annotate a Python function to hint that it takes the same arguments as another function?

Is there any Python type-hinting syntax to state that a function takes the same parameters (and parameter types) as another function? In particular this is useful for wrapping, e.g.,

async def do_stuff(
        param1: str,
        param2: int,
        param3: int = 14,
):
    ...

def run_async_thing(*args, **kwargs):  # <--- What can I put here to say 'takes args like `do_stuff`'?
    return asyncio.get_event_loop().run_until_complete(do_stuff(*args, **kwargs))

In this case, I would like to add type hinting to the run_async_thing function to identify that it expects the same argument types as the do_stuff function.

Is this possible, and if so, how?

The primary reason for wanting this is so that my tools (in particular PyCharm/IntellliJ IDEA) can figure out what arguments run_async_thing should expect/accept. If it helps with documentation that's a bonus, but this is mainly for tooling.

like image 757
lxop Avatar asked Feb 25 '20 04:02

lxop


People also ask

How to annotate the return value of a function in Python?

The return value is annotated with the type float. Here the ‘->’ syntax for annotating the return value. Above we call func () twice, once with int arguments and once with string arguments. In both cases, func () does the right thing and annotations are simply ignored.

What are annotations in Python and how to use them?

What are Annotations As we just went through in the introduction section, annotations are Python features that hint developers about the data types of the variables or function parameters and return type. They also increase the readability of your Python program.

How do you handle arbitrary arguments in Python?

Python Arbitrary Arguments Sometimes, we do not know in advance the number of arguments that will be passed into a function. Python allows us to handle this kind of situation through function calls with an arbitrary number of arguments. In the function definition, we use an asterisk (*) before the parameter name to denote this kind of argument.

How do annotations affect the execution of the function func ()?

Above we call func () twice, once with int arguments and once with string arguments. In both cases, func () does the right thing and annotations are simply ignored. So, we see the annotations have no effect on the execution of the function func ().


Video Answer


2 Answers

Define the parameters explicitly. You are unnecessarily generalizing the signature for run_async_thing:

def run_async_thing(p1: str, p2: int, p3: int):
    return asyncio.get_event_loop().run_until_complete(do_stuff(p1, p2, p3))

More generally, you can have run_async_thing take a single tuple (or other object) as an argument. For example:

async def do_stuff(t: Tuple[str, int, int]):
    ...

def run_async_thing(args: Tuple[str, int, int]):
   return asyncio.get_event_loop().run_until_complete(do_stuff(args))

The tuple type can be factored out:

StuffDoerArgs = Tuple[str, int, int]

async def do_stuff(t: StuffDoerArgs):
    ...

def run_async_thing(args: StuffDoerArgs):
    ...
like image 141
chepner Avatar answered Oct 14 '22 10:10

chepner


You need to define the parameter types and return types. You can then copy the __annotations__ from one function to the other.

Example:

def abc(num: int, name: str) -> list:
    return [num, name]

print(abc.__annotations__)

Output: {'num': <class 'int'>, 'name': <class 'str'>, 'return': <class 'list'>}

Now we create another function:

def xyz(num, name):
    return [num, name]

print(xyz.__annotations__)

Output: {}

You can just copy over the __annotations__ output from one to the other.

xyz.__annotations__ = abc.__annotations__

So now:

print(xyz.__annotations__)

Output: {'num': <class 'int'>, 'name': <class 'str'>, 'return': <class 'list'>}
like image 24
Rahul P Avatar answered Oct 14 '22 09:10

Rahul P