最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Python type hinting for value that could also be a callable - Stack Overflow

programmeradmin1浏览0评论

I was recently trying to design a function that takes an optional default argument -- either a value or a callable that returns a value.

I wrote this method to do so:

T = TypeVar("T")


def default_example(default: T | Callable[[], T] | None) -> T:
    # stuff
    if default is not None:
        return default() if callable(default) else default
    # more stuff

However, my type hinter (pylance) is understandably upset because the type T itself could also be callable and its return type is not bound. Is there a way to modify this function to achieve what I want (value or callable or none) without using overloads or bounding the type variable?

I was recently trying to design a function that takes an optional default argument -- either a value or a callable that returns a value.

I wrote this method to do so:

T = TypeVar("T")


def default_example(default: T | Callable[[], T] | None) -> T:
    # stuff
    if default is not None:
        return default() if callable(default) else default
    # more stuff

However, my type hinter (pylance) is understandably upset because the type T itself could also be callable and its return type is not bound. Is there a way to modify this function to achieve what I want (value or callable or none) without using overloads or bounding the type variable?

Share Improve this question asked Mar 15 at 5:52 Chris LarssonChris Larsson 31 bronze badge 3
  • I don’t think the type variable can be solved without overloading – dROOOze Commented Mar 15 at 6:01
  • Do you have any restrictions on what T can be (e.g. a number or "has attribute foo")? – STerliakov Commented Mar 15 at 12:19
  • Would it really be a problem to require the user to pass a constant-valued function like lambda: 5 instead of an actual constant 5? – chepner Commented Mar 16 at 12:58
Add a comment  | 

1 Answer 1

Reset to default 0

I think the standard way to achieve this is to do like dataclasses.field does:

The parameters to field() are:

  • default: If provided, this will be the default value for this field. This is needed because the field() call itself replaces the normal position of the default value.

  • default_factory: If provided, it must be a zero-argument callable that will be called when a default value is needed for this field. Among other purposes, this can be used to specify fields with mutable default values, as discussed below. It is an error to specify both default and default_factory.

So your function should look something like this:

T = TypeVar("T")


def default_example(
        default: T | None = None,
        *,
        default_factory: Callable[[], T] | None = None
        ) -> T:
    # stuff
    if default is not None and default_factory is None:
        return default
    elif default is None and default_factory is not None:
        return default_factory()
    else:
        raise("must provide exactly one of `default`, `default_factory`")

default_example(3)  # 3
default_example(int)  # the `int` built-in function
default_example(default_factory=int)  # 0, as it is the output of `int()`

Be careful, if you want your default to be able to have the value None, you'll have to work a bit harder, once again you can look what they did in dataclasses (they created a custom MISSING type/value).

Also note that the T = TypeVar("T") is no longer needed in Python 3.12 thanks to the type-parameter syntax, although not all type checkers accept this yet.

def default_example[T](
        *,
        default: T | None = None,
        default_factory: Callable[[], T] | None = None
        ) -> T:
    # your stuff
    ...
发布评论

评论列表(0)

  1. 暂无评论