How to get name of function's actual parameter

2019-03-01 08:21发布

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

4条回答
爷、活的狠高调
2楼-- · 2019-03-01 09:19

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
查看更多
爷的心禁止访问
3楼-- · 2019-03-01 09:19

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

查看更多
甜甜的少女心
4楼-- · 2019-03-01 09:20

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)
查看更多
太酷不给撩
5楼-- · 2019-03-01 09:23

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)
查看更多
登录 后发表回答