2

In Python, I want to have a method that builds a new function from an old one.

Input: a function like f(**kwargs) together with a tuple of pairs that takes a dictionary of keywords

Output: a new function that has the pairs from the tuple as arguments and default values.

What I have so far:

def f(**kwargs):
    return 'test',kwargs['a'], kwargs['b'] 

def magic(f,argtuple):
    arglist=",".join([str(ke)+" = "+str(va) for ke,va in argtuple])
    keylist=",".join([str(ke)+" = "+str(ke) for ke,va in argtuple])
    exec("def g("+arglist+"): return f("+keylist+")")
    return g

It does what I want:

In [25]: f(b=4,a=3)
Out[25]: ('test', 3, 4)

In [26]: g=magic(f,(['a',1],['b',2]))

In [27]: g()
Out[27]: ('test', 1, 2)

In [28]: g(a=3,b=5)
Out[28]: ('test', 3, 5)

In [29]: import inspect

In [30]: print  inspect.getargspec(f)
ArgSpec(args=[], varargs=None, keywords='kwargs', defaults=None)

In [31]: print  inspect.getargspec(g)
ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=(1, 2))

However I don't want to build the string and don't want to use the exec function. What are other ways of achieving this?

2
  • Does g absolutely have to have that signature or would it be fine if it accepted *args and **kwargs? Commented Mar 4, 2017 at 22:09
  • @Rawing: I would like to have the signature as described Commented Mar 5, 2017 at 10:17

2 Answers 2

3

What you are looking for is functools.partial().

import functools

def f(**kwargs):
    return 'test',kwargs['a'], kwargs['b']

def magic(f, argtuple):
    return functools.partial(f, **dict(argtuple))

g = magic(f, (['a',1],['b',2]))
print g()  # ('test', 1, 2)
Sign up to request clarification or add additional context in comments.

3 Comments

the object g in your solution is not a function
Yeah, but it's a callable. >>> callable(g) True It's perfectly fine to use it in place of a function according to the duck typing principle.
This is not what I was looking for. For example, with this callable thing, tab completion does not work.
2

Here is a solution that also constructs the right signature. It requires a reasonably up-to-date Python since it makes use of relativly recent additions to the inspect module and also the __signature__ special attribute.

import inspect

def build_sig(arg_tuple):
    return inspect.Signature(parameters = [inspect.Parameter(
        name=name, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, 
        default=default) for name, default in arg_tuple])

def wrap(f, arg_tuple):
    sig = build_sig(arg_tuple)
    def g(*args, **kwds):
        bound = sig.bind(*args, **kwds)
        bound.apply_defaults()
        return f(**bound.arguments)
    g.__signature__ = sig
    return g

def f(*args, **kwds):
    return(args, kwds)

g = wrap(f, (['a', 1], ['b', 7]))

print(inspect.getargspec(g))
print(g(3))

# ArgSpec(args=['a', 'b'], varargs=None, keywords=None, defaults=(1, 7))
# ((), {'a': 3, 'b': 7})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.