0

I want to define a function using explicit argument names ff(a,b,c) in the function definition, but I also want to map a function over all arguments to get a list:

ff(a,b,c):
    return list(map(myfunc,[a,b,c]))

However, I don't want to explicitly write parameter names inside function as a,b,c. I want to do it like

ff(a,b,c):
    return list(map(myfunc,getArgValueList()))

getArgValueList() will retrieve the argument values in order and form a list. How to do this? Is there a built-in function like getArgValueList()?

smci
  • 32,567
  • 20
  • 113
  • 146
user15964
  • 2,507
  • 2
  • 31
  • 57
  • Use `*args` as the parameter. – Code-Apprentice May 14 '18 at 03:02
  • You cannot take the arguments with explicit `a, b, c` parameters, but then use them as `args`. You do one or the other. Of course you can always write `args = a, b, c` as the first line of the function, but I'm not sure what that would get you. Maybe if we knew _why_ you wanted this, instead of a meaningless toy problem, we could explain something better. But as is, what you're asking for is impossible. – abarnert May 14 '18 at 03:10
  • Well, not _impossible_, if you don't mind doing some ugly reflection and/or frame hacks. Which I'd be happy to explain if you really want. But that's not a good idea if your goal is to make your code more readable, elegant, efficient, … The only time it might be useful is if you're generating functions dynamically and need more flexibility. – abarnert May 14 '18 at 03:12
  • @abarnert I think what the OP wants is the answer below modified with both of our comments. – Code-Apprentice May 14 '18 at 03:12
  • 1
    @Code-Apprentice The OP said "I want to argument interface to be explict. That is explict write argument names as `ff(a,b,c)` when making function definition", and even edited the question to put that in bold after you made your comments on the answer, so… I think, even if maybe that's what the OP _should_ want, it's not what the OP _does_ want. – abarnert May 14 '18 at 03:14
  • @abarnert @Code-Apprentice Thank you so much for comment. What I want is simple, I just want a function `getArgValueList()` that can be used inside another function(arg name is explicit) to get argvalue as list – user15964 May 14 '18 at 03:15
  • @abarnert Nevermind...I just saw the edited question. – Code-Apprentice May 14 '18 at 03:16
  • @Code-Apprentice Yeah, I think abarnert understand me right : ) – user15964 May 14 '18 at 03:17
  • @user15964 That is difficult to do as abarnert said above. You can pass the arguments explicitly in the function call, but you have to declare the function as `def ff(*args)`. On the other hand, if you use `**kwargs`, you can get very close to what you are asking. I suggest that you read the link above your question for more details. – Code-Apprentice May 14 '18 at 03:18
  • @Code-Apprentice Ok, I will read it. Thank you so much – user15964 May 14 '18 at 03:20
  • Can you explain why you want a `getArgValueList` function, or give a more realistic example of what you intend to do with it? Whatever you're trying to do, there's probably a better way to do it—even if it isn't `*args`. – abarnert May 14 '18 at 03:29
  • Possible duplicate of [Converting list to \*args when calling function](https://stackoverflow.com/questions/3941517/converting-list-to-args-when-calling-function) – smci May 14 '18 at 03:32
  • @smci How could it be a dup of that? The OP doesn't have a list here. – abarnert May 14 '18 at 03:34
  • @abamert: the OP does have a list here if they use *args syntax, which is what they asked for. They're asking how to pass arguments as a list. This is a duplicate. If you still disagree, then post which of the many other SO questions it duplicates. – smci May 14 '18 at 03:37
  • Ah sorry. OP here wants to call `f(a), f(b), ... on each of *args`. The one I linked wants to call `f(*args)`. But still this must be a duplicate, please help identify which one. – smci May 14 '18 at 03:39
  • 1
    @smci I don't think it is a duplicate. What the OP wants to do is to define a function with explicit parameters, but then get the args list anyway. That's an unusual thing to do, so it's not going to match the obvious dups involving varargs or splats, even if it looks like it at first glance. – abarnert May 14 '18 at 03:43
  • FWIW, when writing Python it's a Good Idea to use the usual Python idioms, don't try to write Java in Python. – PM 2Ring May 14 '18 at 04:02
  • **Why do you want to have explicit argument names in the function definition?** If you have a legitimate reason, then what is it? What's wrong with inside the function body, unpacking the positional args into named variables again `a = args[0], b = args[1]...` – smci May 14 '18 at 05:00
  • 2
    @PM2Ring This is even less idiomatic Java than Python. It's… maybe (pre-ES5) JavaScript, but really it feels more Perl than anything else. – abarnert May 14 '18 at 06:19
  • 1
    @abarnert Ok. I was just taking a guess at it being a Java thing, mostly from the camelCase function name (I don't write Java). – PM 2Ring May 14 '18 at 06:29

2 Answers2

2

What you're trying to do is impossible without ugly hacks. You either take *args and get a sequence of parameter values that you can use as args:

def ff(*args):
    return list(map(myfunc, args))

… or you take three explicit parameters and use them by name:

def ff(a, b, c):
    return list(map(myfunc, (a, b, c)))

… but it's one or the other, not both.

Of course you can put those values in a sequence yourself if you want:

def ff(a, b, c):
    args = a, b, c
    return list(map(myfunc, args))

… but I'm not sure what that buys you.


If you really want to know how to write a getArgValueList function anyway, I'll explain how to do it. However, if you're looking to make your code more readable, more efficient, more idiomatic, easier to understand, more concise, or almost anything else, it will have the exact opposite effect. The only reason I could imagine doing something like this is if you had to generate functions dynamically or something—and even then, I can't think of a reason you couldn't just use *args. But, if you insist:

def getArgValueList():
    frame = inspect.currentframe().f_back
    code = frame.f_code
    vars = code.co_varnames[:code.co_argcount]
    return [frame.f_locals[var] for var in vars]

If you want to know how it works, most of it's in the inspect module docs:

  • currentframe() gets the current frame—the frame of getArgValueList.
  • f_back gets the parent frame—the frame of whoever called getArgValueList.
  • f_code gets the code object compiled from the function body of whoever called getArgValueList.
  • co_varnames is a list of all local variables in that body, starting with the parameters.
  • co_argcount is a count of explicit positional-or-keyword parameters.
  • f_locals is a dict with a copy of the locals() environment of the frame.

This of course only works for a function that takes no *args, keyword-only args, or **kwargs, but you can extend it to work for them as well with a bit of work. (See co_kwonlyargcount, co_flags, CO_VARARGS, and CO_VARKEYWORDS for details.)

Also, this only works for CPython, not most other interpreters. and it could break in some future version, because it's pretty blatantly relying on implementation details of the interpreter.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • Hi, @abarnert. Thank you so much for taking so much time answering. I learned a lot. But it seems that `getArgValueList` is not working properly. `getArgValueList(1,2,3)` gives `()` if I defined it as `def getArgValueList(a,b,c):` – user15964 May 14 '18 at 06:11
  • @user15964 Why did you define it as `getArgValueList(a,b,c)`. You wanted a function that you can call as `getArgValueList()` from any function. That's what I gave you. And [it works](https://repl.it/repls/SnowHideousCarriers). If randomly changing it into something different without understanding it doesn't work, that's not surprising. – abarnert May 14 '18 at 06:27
  • Oh, I am so sorry. My brain must ran into chaos previously. It indeed works. Thank you so much, and I decide to accept your answer, because you provide explicit `getArgValueList`. Though jferard's solution also works, it seems that it is not easy to construct `getArgValueList` via his approach. – user15964 May 14 '18 at 06:49
  • @user15964 Yes, Python makes it _possible_ to do all kinds of clever things, but the ones that are usually too clever to be a good idea, it usually makes a bit unpleasant. – abarnert May 14 '18 at 06:55
1

The *args construction will give you the arguments as a list:

>>> def f(*args): return list(map(lambda x:x+1, args))
>>> f(1,2,3)
[2, 3, 4]

If you are bound with the signature of f, you'll have to use the inspect module:

import inspect

def f(a, b,c):
    f_locals = locals()
    values = [f_locals[name] for name in inspect.signature(f).parameters]
    return list(map(lambda x:x+1, values))

inspect.signature(f).parameters gives you the list of arguments in the correct order. The values are in locals().

jferard
  • 7,835
  • 2
  • 22
  • 35