4

I have some boiler plate logic that I want to wrap several functions that have the same optional keyword. Right now it looks like the code below. However, this only handles the case that opt_key is passed as a keyword, as opposed to being passed positionally. One way to solve this would be to know how the argument assignment would be resolved.

Is there some meta function that take a function, args, and kwargs, and returns a dictionary that gives the map from variable names to values? So for below,

meta_function(g,1,2,3) would return {"x" : 1, "y" : 2, "opt_key" : 3} and

meta_function(g,1,2,opt_key=3) would also return {"x" : 1, "y" : 2, "opt_key" : 3}

def g(x,y,opt_key = None):
  return something

def h(z,opt_key = None):
  return something else

def wrap(f,*args,**kwargs):
  if kwargs["opt_key"] is None:

    #Do some stuff to get default opt_key

    kwargs["opt_key"] = default_opt_key
    v = f(args,kwargs)

    #Do some stuff to dispose default opt_key

    return v
  else:
    return f(args,kwargs)

wrap(g,x,y,opt_key = None)
wrap(h,x,opt_key = None)
  • 3
    [PEP 318](https://www.python.org/dev/peps/pep-0318/). Also you can check [my answer](https://stackoverflow.com/a/62353563/10824407) in similar question. – Olvin Roght Jan 26 '21 at 17:13
  • You can unwrap args and kargs: `return f(*args, **kwargs)` so in this way you don't have to manage if the user uses the postional argument or the keyword one. – DPD- Jan 26 '21 at 17:13
  • I don't understand your question. *"Can you determine from a function, args, and kwargs, how variables will be assigned?"* - yes: `args` are assigned by position, and `kwargs` are assigned by their name. That's their definition – Tomerikoo Jan 26 '21 at 17:23
  • @juanpa.arrivillaga, they? I thought that I respond to a single person. Anyway, I don't clearly understand meaning of your comment? I pointed to *PEP* where described syntax of decorators. I understand that theoretically there's no difference to use syntax "sugar" or chain function calls. – Olvin Roght Jan 26 '21 at 17:25
  • @juanpa.arrivillaga, in those answer initially argument names weren't printed, I've added it to code by request and copied from other answer. If you'll check comment under the question, I've [suggested](https://stackoverflow.com/posts/comments/110277611) to use `inspect` as well ;) – Olvin Roght Jan 27 '21 at 12:24
  • Under the assumption that __code__.co_varnames starts with the set of parameters for the function in order, and that the function is called with proper set of parameters (i.e. not too many parameters, no duplicate keys, etc), then I think @OlvinRoght's solution works. The solution of juanpa.arrivillaga is a bit better, in my opinion, since it throws an error when the inputs don't match the function signature. – Josh Brown Kramer Jan 31 '21 at 22:44
  • 1
    @JoshBrownKramer, I have never said that answer I've sent is complete solution, it's just one of examples which could be upgraded to fit your requirements. – Olvin Roght Jan 31 '21 at 22:59

1 Answers1

-1

Yes, you want to use inspect.signature and work with the Signature object, which will allow you to .bind *args and **kwargs:

In [1]: import inspect

In [2]: args = 'a', 'b', 'c'

In [3]: kwargs = {}

In [4]: def g(x,y,opt_key = None):
   ...:   return something
   ...:
   ...: def h(z,opt_key = None):
   ...:   return something_else
   ...:

In [5]: g_signature = inspect.signature(g)

In [6]: g_signature.bind(*args, **kwargs)
Out[6]: <BoundArguments (x='a', y='b', opt_key='c')>

Note, this BoundArguments object acts as a mapping:

In [7]: bound_arguments = g_signature.bind(*args, **kwargs)

In [8]: bound_arguments.arguments['opt_key']
Out[8]: 'c'
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172