diff options
| author | Michele Simionato <michele.simionato@gmail.com> | 2015-12-09 10:08:23 +0100 |
|---|---|---|
| committer | Michele Simionato <michele.simionato@gmail.com> | 2015-12-09 10:08:23 +0100 |
| commit | a26c55c0d527e1b09b996e29f9d9d816d085addd (patch) | |
| tree | 9c0cc430c59de018e39511df8ab13d35ec31690f /src/tests/documentation.py | |
| parent | 1ed48fd60939e752c5a7ffb2b1b17744d73239ee (diff) | |
| download | python-decorator-git-a26c55c0d527e1b09b996e29f9d9d816d085addd.tar.gz | |
Documented the quirk when decorating functions with keyword arguments
Diffstat (limited to 'src/tests/documentation.py')
| -rw-r--r-- | src/tests/documentation.py | 47 |
1 files changed, 43 insertions, 4 deletions
diff --git a/src/tests/documentation.py b/src/tests/documentation.py index 04d62eb..69e9f4b 100644 --- a/src/tests/documentation.py +++ b/src/tests/documentation.py @@ -526,7 +526,6 @@ be added to the generated function: >>> print(f1.__source__) def f1(a, b): f(a, b) - <BLANKLINE> ``FunctionMaker.create`` can take as first argument a string, as in the examples before, or a function. This is the most common @@ -1007,9 +1006,49 @@ callable objects, nor on built-in functions, due to limitations of the ``inspect`` module in the standard library, especially for Python 2.X (in Python 3.5 a lot of such limitations have been removed). -There is a restriction on the names of the arguments: for instance, -if try to call an argument ``_call_`` or ``_func_`` -you will get a ``NameError``: +There is a strange quirk when decorating functions that take keyword +arguments, if one of such arguments has the same name used in the +caller function for the first argument. The quirk was reported by +David Goldstein and here is an example where it is manifest: + +.. code-block: python + + >>> @memoize + ... def getkeys(**kw): + ... return kw.keys() + >>> getkeys(func='a') + Traceback (most recent call last): + ... + TypeError: _memoize() got multiple values for argument 'func' + +The error message looks really strange until you realize that +the caller function `_memoize` uses `func` as first argument, +so there is a confusion between the positional argument and the +keywork arguments. The solution is to change the name of the +first argument in `_memoize`, or to change the implementation as +follows: + +.. code-block: python + + def _memoize(*all_args, **kw): + func = all_args[0] + args = all_args[1:] + if kw: # frozenset is used to ensure hashability + key = args, frozenset(kw.items()) + else: + key = args + cache = func.cache # attribute added by memoize + if key not in cache: + cache[key] = func(*args, **kw) + return cache[key] + +We have avoided the need to name the first argument, so the problem +simply disappear. This is a technique that you should keep in mind +when writing decorator for functions with keyword arguments. + +On a similar tone, there is a restriction on the names of the +arguments: for instance, if try to call an argument ``_call_`` or +``_func_`` you will get a ``NameError``: .. code-block:: python |
