On 24.05.20 18:34, Alex Hall wrote:


OK, let's forget the colon. The point is just to have some kind of 'modifier' on the default value to say 'this is evaluated on each function call', while still having something that looks like `arg=<default>`. Maybe something like:

     def func(options=from {}):

It looks like the most common use case for this is to deal with mutable defaults, so what is needed is some way to specify a default factory, similar to `collections.defaultdict(list)` or `dataclasses.field(default_factory=list)`. This can be handled by a decorator, e.g. by manually supplying the factories or perhaps inferring them from type annotations:

    @supply_defaults
    def foo(x: list = None, y: dict = None):
        print(x, y)  # [], {}


    @supply_defaults(x=list, y=dict)
    def bar(x=None, y=None):
        print(x, y)  # [], {}


This doesn't require any change to the syntax and should serve most purposes. A rough implementation of such a decorator:


    import functools
    import inspect


    def supply_defaults(*args, **defaults):
        def decorator(func):
            signature = inspect.signature(func)
            defaults.update(
                (name, param.annotation) for name, param in signature.parameters.items()
                if param.default is None and param.annotation is not param.empty
            )

            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                bound = signature.bind(*args, **kwargs)
                kwargs.update(
                    (name, defaults[name]())
                    for name in defaults.keys() - bound.arguments.keys()
                )
                return func(*args, **kwargs)

            return wrapper

        if args:
            return decorator(args[0])
        return decorator