On 02Aug2019 11:26, bob gailer <bgai...@gmail.com> wrote:
And now for something completely different...
Decorators are not required to return a function!
I use them to create a dictionary that maps function names to the
corresponding function object.
That is an interesting idea! But I want to counter it, briefly.
This is very useful when associating actions with user-entered
commands. Example:
def collect(func=None, d={}):
if not func: return d
d[func.__name__] = func
@collect
def add(a,b):
return a+b
# adds item to dictionary d (key = 'add', value = func)
# repeat for other user-command functions
# finally:
cmd_dict = collect() # returns the dictionary
cmd = input('enter a command>')
func = cmd_dict.get(cmd)
I think you're conflating 2 things: having the decorator have a side
effect outside the bare "do stuff around the call of an inner function",
and returning a callable.
The feature of your remark is the side effect, which is useful. Not
returning a callable is orthognal, and a misfeature.
Let me show you why:
Python 3.7.4 (default, Jul 11 2019, 01:07:48)
[Clang 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> d={}
>>> def collect(f): d[f.__name__]=f
...
>>> def g(x): print(x*2)
...
>>> @collect
... def h(x): print(x*3)
...
>>> g(8)
16
>>> h(8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
The return value of the decorator is bound to the function name in the
current scope,typically the current module for a normal function or the
enclosing class for a method.
Your decorator returns None (I'm ignoring the d={} funkiness for now).
Therefore a side effect of using the decorator is that every function
"foo" you use with this decorator defines the name "foo" in the current
scope as None.
As you can see in the example above, that makes the h() function not
usable on its own. (And litters the namespace with a useless "h" name
whose value is None.)
Had my version of your decorator looked like this:
def collect(f):
d[f.__name__] = f
return f
then h() would have remained independently useful, at no cost to the
functionality of your decorator.
So I'm arguing that while you _can_ return None from a decorator (which
is what is actually happening in your "not returning" phrasing), it
remains _useful_ and _easy_ to return the decorated function itself
unchanged.
I've concerns about your d={} trick too, but we can discuss those in
another subthread if you like.
I'm hoping to convince you that your otherwise nifty @collect decorator
could do with returning the function unchanged after doing its work.
Finally, there is another important reason to return the function (or
another callable): nesting decorators. Look at this piece of code from a
project I'm working on:
@classmethod
@auto_session
@require(lambda console: isinstance(console, Console))
@require(lambda top_node: isinstance(top_node, DirTreeNode))
@require(lambda top_node: not hasattr(top_node, 'can_uuid'))
def from_fstree(cls, console, top_node, *, session):
This is a method, but otherwise the situation is no different. Each of
these decorators does a little task and returns a callable, ready for
further decoration by outer decorators. So every one of them returns a
suitable callable.
If your @collect decorator returned the function, it too could be
happily placed in such a nesting of decorators and everyone is happy.
Because it does not, it does not play well with others, because an outer
decorator would not have a callable to work with; it would get the None
that your @collect returns.
This is the other argument for always returning a callable: to
interoperate with other decorators, or of course anything else which
works with a callable.
Cheers,
Cameron Simpson <c...@cskk.id.au>
_______________________________________________
Tutor maillist - Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor