On Tue, 1 Jun 2021 at 13:16, Steven D'Aprano <[email protected]> wrote:
> We can distinguish the two contexts by using different signatures. The
> signature used depends entirely on the call site, not the decorator, so
> it is easy for the interpreter to deal with.
>
> If the decorator is called on a function or class statement, a single
> argument is always passed, no exceptions:
>
> # always calls decorate with one argument
> @decorate
> def func(): # or class
> pass
>
> # --> func = decorate(func)
>
> If called on a variable, the number of arguments depends on whether it
> is a bare name, or a value and annotation are provided. There are
> exactly four cases:
>
> # bare name
> @decorate
> var
> # --> var = decorate('var')
>
> # name with annotation
> @decorate
> var: annot
> # --> var = decorate('var', annotation=annot)
>
> # name bound to value
> @decorate
> var = x
> # --> var = decorate('var', value=x)
>
> # name with annotation and value
> @decorate
> var: annot = x
> # --> var = decorate('var', annotation=annot, value=x)
>
>
> Keyword arguments are used because one or both of the value and the
> annotation may be completely missing. The decorator can either provide
> default values or collect keyword arguments with `**kwargs`.
I've yet to be convinced that variable annotations are sufficiently
useful to be worth all of this complexity (and by "this" I mean any of
the proposals being made I'm not singling out Steven's suggestion
here).
But if we do need this, I quite like the idea of making the
distinction based on signature.
> The only slightly awkward case is the bare variable case. Most of the
> time there will be no overlap between the function/class decorators and
> the bare variable decorator, but in the rare case that we need to use a
> single function in both cases, we can easily distinguish the two cases:
>
> def mydecorator(arg, **kwargs):
> if isinstance(arg, str):
> # must be decorating a variable
> ...
> else:
> # decorating a function or class
> assert kwarg == {}
>
> So it is easy to handle both uses in a single function, but I emphasise
> that this would be rare. Normally a single decorator would be used in
> the function/class case, or the variable case, but not both.
You don't need to do this. Just add another keyword argument "name":
# bare name
@decorate
var
# --> var = decorate(name='var')
# name with annotation
@decorate
var: annot
# --> var = decorate(name='var', annotation=annot)
# name bound to value
@decorate
var = x
# --> var = decorate(name='var', value=x)
# name with annotation and value
@decorate
var: annot = x
# --> var = decorate(name='var', annotation=annot, value=x)
The single positional argument is reserved for function/class
annotations, and will always be None for variable annotations.
Paul
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/NJTZRP64SBWP5SJMKAGPNAYJ7MK6IKQO/
Code of Conduct: http://python.org/psf/codeofconduct/