On Tue, 1 Jun 2021 at 13:16, Steven D'Aprano <st...@pearwood.info> 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 -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/NJTZRP64SBWP5SJMKAGPNAYJ7MK6IKQO/ Code of Conduct: http://python.org/psf/codeofconduct/