The simplest way to document the function parameter is using typing.Callable. By doing so the static type checker will verify that the signature (arguments and return type) of the passed function is compatible with the type hint.
from typing import Callable
class ExampleClassTwo:
"""An example class to demonstrate my question.
Args:
func (Callable[[str], bool]): Description of the parameter.
"""
def __init__(self, func: Callable[[str], bool]):
self.func = func
However, this has one potential problem. If you look at the Python Data Model, in the sub-section titled "Callable types" under 3.2 The standard type hierarchy. You'll notice there are several possible types that are callables, and any callable with the specified signature would not cause a warning from the static type checker.
For example, a class instance implementing the __call__() method would not cause a warning:
def str_function(param: str) -> bool:
pass
class OneInstance:
def __init__(self, param):
pass
def __call__(self, param: str) -> bool:
pass
one_instance = OneInstance("test instance")
# neither of the below raise a static type check warning
one = ExampleClassTwo(str_function)
two = ExampleClassTwo(one_instance)
You can type hint the param signature as shown above together with validating that param is of type FunctionType in the __init__ as you would validate other parameters at run-time, and raise a TypeError exception if the parameter is not a function.
from typing import Callable
from types import FunctionType
class ExampleClassTwo:
"""An example class to demonstrate my question
Args:
func (Callable[[str], bool]): Description of the parameter
Raises:
TypeError: Argument `param` is not of type ``FunctionType``.
"""
def __init__(self, func: Callable[[str], bool]):
if type(func) in (FunctionType,):
self.func = func
else:
raise TypeError("param must be initialized as being of ``FunctionType``.")
It may be possible to combine both requirements Callable[[str], bool] and FunctionType as an intersection using structural subtyping, I have not yet tried that approach.
Finally some examples are included that would cause the static type checker to raise warnings:
def int_function(param: int) -> bool:
pass
class ExampleClass:
"""An example class to demonstrate my question
Args:
func (FunctionType): Description of the parameter
"""
def __init__(self, func: FunctionType):
self.func = func
three = ExampleClass(str_function("test string")) # Expected type 'FunctionType', got 'bool' instead
four = ExampleClass(str_function) # Expected type 'FunctionType', got '(param: str) -> bool' instead
five = ExampleClass(type(str_function)) # No warning five.func is {type} <class 'function'>
six = ExampleClassTwo(int_function(2)) # Expected type '(str) -> bool', got 'bool' instead
seven = ExampleClassTwo(str_function("test string")) # Expected type '(str) -> bool', got 'bool' instead
eight = ExampleClassTwo(int_function) # Expected type '(str) -> bool', got '(param: int) -> bool' instead
nine = ExampleClassTwo(str_function) # No warning
