How to Add Default Parameters to Functions When Using Type Hinting

How do I add default parameters to functions when using type hinting?

Your second way is correct.

def foo(opts: dict = {}):
pass

print(foo.__annotations__)

this outputs

{'opts': <class 'dict'>}

It's true that's it's not listed in PEP 484, but type hints are an application of function annotations, which are documented in PEP 3107. The syntax section makes it clear that keyword arguments works with function annotations in this way.

I strongly advise against using mutable keyword arguments. More information here.

How to type hint function with a callable argument and default value

You could use the @overload for correctly type hinting of function with default argument for your case:

from typing import Callable, List, TypeVar, overload, Set

T = TypeVar("T")

@overload
def transform(data: List[int]) -> Set[int]: ...

@overload
def transform(data: List[int], ret_type: Callable[[List[int]], T]) -> T: ...

# untyped implementation
def transform(data, ret_type = set):
return ret_type(data)

a = [1, 2, 3]
my_set: Set = transform(a)

Type Hinting: Default Parameters

You can't typehint strings, you can only typehint objects and arrays, so this is incorrect:

function setName ( string $name = "happ") {

(The reason you don't get a compile-time error here is because PHP is interpreting "string" as the name of a class.)

The wording in the docs means that if you do this:

function foo(Foo $arg) {

Then the argument passed to foo() must be an instance of object Foo. But if you do this:

function foo(Foo $arg = null) {

Then the argument passed to foo() can either be an instance of object Foo, or null. Note also that if you do this:

function foo(array $foo = array(1, 2, 3))

Then you can't call foo(null). If you want this functionality, you can do something like this:

function foo(array $foo = null) {
if ($foo === null) {
$foo = array(1, 2, 3);
}

[Edit 1] As of PHP 5.4, you can typehint callable:

function foo(callable $callback) {
call_user_func($callback);
}

[Edit 2] As of PHP 7.0, you can typehint bool, float, int, and string. This makes the code in the question valid syntax. As of PHP 7.1, you can typehint iterable.

How to type hint a Callable of a function with default arguments?

Define this:

class Foo(Protocol):
def __call__(self, x: int = ..., /) -> float:
...

then type hint foo as Foo instead of Callable[[int], float]. Callback protocols allow you to:

define flexible callback types that are hard (or even impossible) to express using the Callable[...] syntax

and optional arguments are one of those impossible things to express with a normal Callable. The / at the end of __call__'s signature makes x a positional-only parameter, which allows any passed function to bar to have a parameter name that is not x (your specific example of foo calls it arg instead). If you removed /, then not only would the types have to line up as expected, but the names would have to line up too because you would be implying that Foo could be called with a keyword argument. Because bar doesn't call foo with keyword arguments, opting into that behavior by omitting the / imposes inflexibility on the user of bar (and would make your current example still fail because "arg" != "x").

Type-hinting an overloaded function when parameter default is determined by module level variable

That's not possible. There is a proposal on GitHub to add typeof from TypeScript, but that's a different thing - it doesn't track mutable variables like you've shown.

I don't think such a feature can be implemented, even in theory. It brings a lot of new complexity into type checking. I can think of some problematic scenarios:

  1. Passing a callback
def foo(callback: Callable[[], int]):
some_module.definitely_returns_an_int = callback

option_default = "int"
foo(test) # ok?
option_default = "str"

Now foo has saved a function that returns a string.


  1. Tracking mutations that happen inside functions
def ints_are_the_bomb():
global option_default
option_default = "int"

option_default = "str"
some_other_module.baz()

value = test()

Can you be sure about the type of value? There's no guarantee that some_other_module.baz() didn't call your_module.ints_are_the_bomb(). So now you need to track all changes that can ever happen to your_module.option_default, potentially across modules. And if client code (if this is a library) can change the flag, then it's just impossible.

To generalize, the type of a value (including functions) can't change when you mutate something. That could break distant code that also happens to have a reference to this object.

Type-hinting parameters with a sentinel value as the default

Something I like to do — which is only a slight variation on @Blckknght's answer — is to use a metaclass to give my sentinel class a nicer repr and make it always-falsey.


sentinel.py

from typing import Literal 

class SentinelMeta(type):
def __repr__(cls) -> str:
return f'<{cls.__name__}>'

def __bool__(cls) -> Literal[False]:
return False

class Sentinel(metaclass=SentinelMeta): pass

main.py

from sentinel import Sentinel

class DEFAULT(Sentinel): pass

You use it in type hints exactly in the same way @Blckknght suggests:

def spam(ham: list[str]|None|type[DEFAULT] = DEFAULT): ...

But you have the added advantages that your sentinel value is always falsey and has a nicer repr:

>>> DEFAULT
<DEFAULT>
>>> bool(DEFAULT)
False


Related Topics



Leave a reply



Submit