Python decorator that returns a function with one or more arguments replaced
<p>I would like to create a decorator to a set of functions that replaces one or more of the arguments of the functions. The first thought that came to my mind was to create a decorator that returns a partial of the function with the replaced arguments.
I'm unhappy with the way the decorated function is called, but even when it's being called "properly", i get a TypeError.</p>

<p>Here is some example code:</p>

<pre><code>def decor(func, *args, **kwargs):
    def _new_func(*args, **kwargs):
        return partial(func, *args, **kwargs, v=100)
    return _new_func

@decor
def some_function(a, v, c):
    return a, v, c

some_function(1,2,3)  # functools.partial(&lt;function some_function at 0x7f89a8bed8c8&gt;, 1, 2, 3, v=100)
some_function(1,2,3)(1,2,3)  # TypeError: some_function() got multiple values for argument 'v'
</code></pre>

<p>I'm sure there is an easy way to create a decorator that replaces some of the arguments, but haven't figured it out yet.</p>

<p>You'd have to return the partial as the decoration result:</p>

<pre><code>def decor(func):
    return partial(func, v=100)
</code></pre>

<p>However, this <em>always</em> sets <code>v=100</code>, even if you passed in another value for <code>v</code> by position. You'd still have the same issue.</p>

<p>You'd need to create a decorator that knows what positional argument <code>v</code> is, and look for it there <em>and</em> as a keyword argument:</p>

<pre><code>from inspect import getargspec

def decor(func):
    vpos = getargspec(func).args.index('v')
    def wrapper(*args, **kwargs):
        if len(args) &gt; vpos:
            args = list(args)
            args[vpos] = 100
        else:
            kwargs['v'] = 100
        return func(*args, **kwargs)        
    return wrapper
</code></pre>

<p>The above decorator will set the <code>v</code> argument to 100, <em>always</em>. Wether you tried to set <code>v</code> as a positional argument or as a keyword argument, in the end it'll be set to 100 anyway:</p>

<pre><code>&gt;&gt;&gt; @decor
... def some_function(a, v, c):
...     return a, v, c
...
&gt;&gt;&gt; some_function(1, 2, 3)
(1, 100, 3)
&gt;&gt;&gt; some_function(1, v=2, c=3)
(1, 100, 3)
</code></pre>

<p>If you only wanted to provide a default argument for <code>v</code> if it wasn't explicitly set, you'd have to invert the tests:</p>

<pre><code>def decor(func):
    vpos = getargspec(func).args.index('v')
    def wrapper(*args, **kwargs):
        if len(args) &lt;= vpos and 'v' not in kwargs:
            kwargs['v'] = 100
        return func(*args, **kwargs)        
    return wrapper
</code></pre>

<p>at which point <code>v</code> is only provided if not already set:</p>

<pre><code>&gt;&gt;&gt; @decor
... def some_function(a, v, c):
...     return a, v, c
...
&gt;&gt;&gt; some_function(1, c=3)  # v not set
(1, 100, 3)
&gt;&gt;&gt; some_function(1, 2, c=3)  # v is set
(1, 2, 3)
&gt;&gt;&gt; some_function(1, v=2, c=3)  # v is set as keyword argument
(1, 2, 3)
</code></pre>

python decorator partial python-decorators