atupal
atupal

Reputation: 17210

How to get name of function's actual parameters in Python?

For example:

def func(a):
    # how to get the name "x"

x = 1
func(x)

If I use inspect module I can get the stack frame object:

import inspect
def func(a):
    print inspect.stack()

out:

[
(<frame object at 0x7fb973c7a988>, 'ts.py', 9, 'func', ['  stack = inspect.stack()\n'], 0)

(<frame object at 0x7fb973d74c20>, 'ts.py', 18, '<module>', ['func(x)\n'], 0)
]

or use inspect.currentframe() I can get the current frame.

But I can only get the name "a" by inspect the function object.

Use inspect.stack I can get the call stack:"['func(x)\n']",

How can I get the name of actual parameters("x" here) when call the func by parsing the "['func(x)\n']"?

If I call func(x) then I get "x"

if I call func(y) then I get "y"

Edit:

A example:

def func(a):

    # Add 1 to acttual parameter
    ...

x = 1
func(x)
print x # x expected to 2

y = 2
func(y)
print y # y expected to 3

Upvotes: 1

Views: 1056

Answers (4)

jsbueno
jsbueno

Reputation: 110301

That is feasible, up to a point - but nonetheless,useless in any kind of "real code". You can use it for backyard magic tricks:

Just retrieve the calling stack_frame like you are doing, then loop linearly through the variables available in the calling frame for one that references the same object you got as a parameter. The variables are in the f_locals and f_globals dictionaries which are attributes of the frame object.

Of course, the parameter passed may not be in a variable name to start with: it might be a constant in the caller, or it might be inside a dict or a list.

Either way, as @Nasser put it in the answer: it is not the way Python works. Objects exist in memory, and variable names, in each scope, just point to those objects. The names themselves are meaningless otherwise.

import inspect
from itertools import chain

def magic(x):
    # note that in Python2, 'currentframe' could get a parameter
    # to indicate which frame you want on the stack. Not so in Python3
    fr = inspect.currentframe().f_back
    for var_name, value in chain(fr.f_locals.items(),  fr.f_globals.items()):
        if value is x:
            print ("The variable name in the caller context is {}".format(var_name))


def caler():
   y = "My constant"
   magic(y)

Upvotes: 2

atupal
atupal

Reputation: 17210

This is my final solution. Use ast to parse the function statement and get the args.:

import inspect
import re
import ast

def func(a,b,c):
  stack = inspect.stack()
  stack_pre = stack[1]
  function_call_string = ';'.join( stack_pre[4] ).strip('\n')
  ret = re.search(r'(%s[ ]*\(.*\))' % func.func_name, function_call_string)
  striped_function_call_string = ret.group()
  parser = ast.parse(striped_function_call_string)

  frame = inspect.currentframe()
  for arg in parser.body[0].value.args:
    iter_frame = frame.f_back
    while iter_frame:
      if arg.id in iter_frame.f_locals:
        iter_frame.f_locals[arg.id] += 1
        break
      iter_frame = iter_frame.f_back

x=1;y=2;z=3;func(x,y,z)
print x,y,z

Out: 2 3 4

Upvotes: -1

Nasser Al-Shawwa
Nasser Al-Shawwa

Reputation: 3653

Looking at your comment explanations, the reason you are trying to do this is:

Because I want to impelment Reference Parameters like in C++

You can't do that. In python, everything is passed by value, but that value is a reference to the object you pass. Now, if that object is mutable (like a list), you can modify it, but if it's immutable, a new object is created and set. In your code example, x is an int which is immutable, so if you want to change the value of x it, just return its new value from the function you are invoking:

def func(a):
    b = 5 * a
    # ... some math
    return b


x = 1
x = func(x)

So what if you want to return multiple values? Use a tuple!

def func(a, b):
    a, b = 5 * b, 6 * a
    # ...
    return b, a

a, b = 2, 3
a, b = func(a, b)

Upvotes: 4

markcial
markcial

Reputation: 9323

use lists as references

def func(a):
   a[0] = a[0] + 1

x = [1]
func(x)
print x[0] # x expected to 2

y = [2]
func(y)
print y[0] # y expected to 3

Upvotes: 0

Related Questions