Python 怎么获取函数参数的字面量? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
vtoexsir
V2EX    Python

Python 怎么获取函数参数的字面量?

  •  
  •   vtoexsir 2017-11-07 09:52:51 +08:00 5845 次点击
    这是一个创建于 2896 天前的主题,其中的信息可能已经有所发展或是发生改变。

    def get_var_literal(var):
    var_literal=? # 怎么获取到 var 的字面量?
    print(var_literal)
    s = 'abc'
    get_var_literal(s)
    #=>s 希望能打印出 s,而不是打印出来 abc

    33 条回复    2017-11-09 10:15:53 +08:00
    crazycabbage
        1
    crazycabbage  
       2017-11-07 10:01:34 +08:00 via Android
    def get_variable_name(s):
    loc = locals()
    print loc
    for key in loc:
    if loc[key] == s:
    return key
    sagaxu
        2
    sagaxu  
       2017-11-07 10:07:15 +08:00
    这个 name 不一定存在啊,get_var_literal(foo())
    ipwx
        3
    ipwx  
       2017-11-07 10:13:25 +08:00
    你的这个需求是很别扭的,建议用别的设计。
    est
        4
    est  
       2017-11-07 10:14:05 +08:00
    最喜欢这种扯蛋的问题了

    import inspect
    inspect.getargspec(get_var_literal).args
    est
        5
    est  
       2017-11-07 10:17:44 +08:00
    貌似我审题错误。LZ 自己去翻 inspect 吧
    vtoexsir
        6
    vtoexsir  
    OP
       2017-11-07 10:22:32 +08:00
    @crazycabbage
    "if loc[key] == s:" #这里的 s 不是固定不变的
    我的本意是:获取函数的实参的字面值。比如,传递 a 这个参数,就返回 a 这个字符串;传递 b 这个参数,就返回 b 这个字符串,而不管 a 和 b 自身是什么类型,也不管是什么具体的值,只获取实参的字面量
    ipwx
        7
    ipwx  
       2017-11-07 10:24:53 +08:00   1
    @vtoexsir 你这事情有方法做,但是你得写很多代码,而且必须源代码 .py 存在,而不是 .pyc 。首先,拿到 stacktrace,找到调用你这个函数的代码的行号。然后,用 ast 去分析那一行代码。最后,你拿到了字面量。

    然而何必呢?我看不出来 Python 里面搞这一套的意义。换个设计不是更好?
    alber1986
        8
    alber1986  
       2017-11-07 10:27:14 +08:00
    看看 Python 视频教程就懂了
    http://www.sucaihuo.com/video/172-2-0
    vtoexsir
        9
    vtoexsir  
    OP
       2017-11-07 10:33:31 +08:00
    @est inspect.getargspec(get_var_literal).args # 这是获取形参的字面值,不是实参的字面值
    不过非常感谢,inspection 可能会有戏解决这个问题!
    crazycabbage
        10
    crazycabbage  
       2017-11-07 10:38:57 +08:00 via Android
    a=123
    loc = locals()

    def get_variable_name(s):
    ...print loc
    ...for key in loc:
    ......if loc[key] == s:
    .........return key

    print get_variable_name(a)
    lrxiao
        11
    lrxiao  
       2017-11-07 10:51:13 +08:00
    @crazycabbage
    用 locals 不太好
    不如 loc = sys._getframe(1)
    lrxiao
        12
    lrxiao  
       2017-11-07 10:51:52 +08:00
    loc = sys._getframe(1).f_locals
    qsnow6
        13
    qsnow6  
       2017-11-07 10:54:55 +08:00
    >>> def foo(**kwargs):
    for arg_name in kwargs:
    return kwargs[arg_name], arg_name


    >>> foo(fib=1)
    (1, 'fib')
    qsnow6
        14
    qsnow6  
       2017-11-07 11:06:52 +08:00
    jmc891205
        15
    jmc891205  
       2017-11-07 11:11:42 +08:00
    请问为什么会有这样的需求啊
    Kilerd
        16
    Kilerd  
       2017-11-07 11:17:03 +08:00
    用装饰器,先把抓到的变量读取 __name__ 再传进去
    qsnow6
        17
    qsnow6  
       2017-11-07 11:17:09 +08:00
    Kilerd
        18
    Kilerd  
       2017-11-07 11:20:11 +08:00
    emmm, 试了下除非使用 kwargs 的方式穿进去,不然都无解
    SuperMild
        19
    SuperMild  
       2017-11-07 11:26:40 +08:00
    楼主是想在没有源码的情况下分析源码?

    如果有源码,想不到在程序里分析参数名称的用处。
    SuperMild
        20
    SuperMild  
       2017-11-07 11:27:26 +08:00
    还不如直接分析源码
    qsnow6
        21
    qsnow6  
       2017-11-07 11:27:30 +08:00
    1 以 kwargs 传进函数里
    2 在 locals()里找,找到了打印出来
    wcsjtu
        22
    wcsjtu  
       2017-11-07 11:46:14 +08:00
    难道是逆向 pyc?
    lz 你可能需要这个 https://docs.python.org/2/library/dis.html python 字节码分析

    比如说, 你在 m.py 中定义

    def func(x, y):
    a = 2
    return x+y+a

    然后在 main.py 中导入这个模块

    import dis
    import m
    print dis.dis(m.func)

    打印出来的结果是
    2 0 LOAD_CONST 1 (2)
    3 STORE_FAST 2 (a)

    3 6 LOAD_FAST 2 (a)
    9 LOAD_FAST 0 (x)
    12 BINARY_ADD
    13 LOAD_FAST 1 (y)
    16 BINARY_ADD
    17 PRINT_ITEM
    18 PRINT_NEWLINE
    19 LOAD_CONST 0 (None)
    22 RETURN_VALUE

    中间那一列就是字节码, 后面的就是变量名和对应的值了。
    这个函数的所有信息都在这了。 如果有闭包的话,情况稍微复杂些
    lz 有兴趣可以研究研究
    lrxiao
        23
    lrxiao  
       2017-11-07 11:49:51 +08:00
    好的 我又丧病的 hack 了

    import.sys
    import.dis
    import.types
    from.opcode.import.*

    #.ref:.https://nedbatchelder.com/blog/200804/wicked_hack_python_bytecode_tracing.html
    def.hack_line_numbers(f):
    ....""".Replace.a.code.object's.line.number.information.to.claim.that.every
    ........byte.of.the.bytecode.is.a.new.line...Returns.a.new.code.object.
    ........Also.recurses.to.hack.the.line.numbers.in.nested.code.objects.
    ...."""
    ....code.=.f.__code__
    ....n_bytes.=.len(code.co_code)
    ....new_lnotab.=."\x01\x01".*.(n_bytes-1)
    ....new_consts.=.[]
    ....for.const.in.code.co_consts:
    ........if.type(const).==.types.CodeType:
    ............new_consts.append(hack_line_numbers(const))
    ........else:
    ............new_consts.append(const)
    ....new_code.=.types.CodeType(
    ........code.co_argcount,.code.co_kwonlyargcount,.code.co_nlocals,.code.co_stacksize,.code.co_flags,
    ........code.co_code,.tuple(new_consts),.code.co_names,.code.co_varnames,
    ........code.co_filename,.code.co_name,.0,.str.encode(new_lnotab),.code.co_freevars,.code.co_cellvars
    ........)..
    ....f.__code__.=.new_code
    ....return.f

    def.get_variable_name_simple(var):
    ....loc.=.sys._getframe(1).f_locals
    ....names.=.[]
    ....for.k,.v.in.loc.items():
    ........if.v.==.var:
    ............names.append(k)
    ....return.name

    #.don't.work.with.REPL
    #.the.caller.function.should.hack.line.number.to.get.accurate.lineno
    def.get_variable_name(var):
    ....last_frame.=.sys._getframe(1)
    ....frame.=.sys._getframe(0)
    ....last_code.=.sys._getframe(1).f_code
    ....last_code_arr.=.bytearray(last_code.co_code)
    ....call_lineno.=.last_frame.f_lineno
    ....#.last_code_arr[last_frame.f_lineno].=.opmap['CALL_FUNCTION']
    ....load_var_op.=.last_code_arr[call_lineno.-.2]
    ....load_var_pos.=.last_code_arr[call_lineno.-.1]
    ....if.load_var_op.==.opmap['LOAD_NAME'].or.load_var_op.==.opmap['LOAD_FAST']:
    ........return.last_code.co_varnames[load_var_pos]
    ....if.load_var_op.==.opmap['LOAD_GLOBAL']:
    ........return.last_code.co_names[load_var_pos]
    ....print("I.don't.know,.maybe.just.consts")
    ....return.None

    def.do_nothing():
    ....pass

    @hack_line_numbers
    def.g():
    ....ar.=.1
    ....arrrrrgggghhhhhh.=.1
    ....do_nothing()
    ....do_nothing()
    ....do_nothing()
    ....do_nothing()
    ....n.=.get_variable_name(arrrrrgggghhhhhh)
    ....print(n)

    > arrrrrgggghhhhhh

    空格全部替换的. 为了缩进。。。。。
    lrxiao
        24
    lrxiao  
       2017-11-07 11:50:45 +08:00
    emmm 原来可以 kwargs 23333
    lrxiao
        25
    lrxiao  
       2017-11-07 13:00:41 +08:00
    emmm 解决了传入 attr 访问

    import sys
    import dis
    import types
    from opcode import *
    import inspect

    # ref: https://nedbatchelder.com/blog/200804/wicked_hack_python_bytecode_tracing.html
    def hack_line_numbers(f):
    ....""" Replace a code object's line number information to claim that every
    ........byte of the bytecode is a new line. Returns a new code object.
    ........Also recurses to hack the line numbers in nested code objects.
    ...."""
    ....code = f.__code__
    ....n_bytes = len(code.co_code)
    ....new_lnotab = "\x01\x01" * (n_bytes-1)
    ....new_cOnsts= []
    ....for const in code.co_consts:
    ........if type(const) == types.CodeType:
    ............new_consts.append(hack_line_numbers(const))
    ........else:
    ............new_consts.append(const)
    ....new_code = types.CodeType(
    ........code.co_argcount, code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize, code.co_flags,
    ........code.co_code, tuple(new_consts), code.co_names, code.co_varnames,
    ........code.co_filename, code.co_name, 0, str.encode(new_lnotab), code.co_freevars, code.co_cellvars
    ........)
    ....f.__code__ = new_code
    ....f.__is_lineno_hacked__ = True
    ....return f

    def get_variable_name_easy(**kwargs):
    ....for arg_name in kwargs:
    ........return kwargs[arg_name], arg_name

    def get_variable_name_simple(var):
    ....loc = sys._getframe(1).f_locals
    ....names = []
    ....for k, v in loc.items():
    ........if v == var:
    ............names.append(k)
    ....return names

    # Don't work with REPL, nothing named after f_globals['<module>']
    # Need to redirect a frame
    # If not handled with hacked lineno,
    # We must use 1-level nested no-argument function
    # which directly ref to ordered variable
    def get_variable_name(var):
    ....last_frame = sys._getframe(1)
    ....last_code = last_frame.f_code
    ....last_func_name = last_code.co_name
    ....last_func = None
    ....if last_func_name in last_frame.f_globals.keys():
    ........last_func = last_frame.f_globals[last_func_name]
    ....elif last_func_name in last_frame.f_locals.keys():
    ........last_func = last_frame.f_globals[last_func_name]
    ....else:
    ........# nested support
    ........if last_func_name in last_frame.f_back.f_globals.keys():
    ............last_func = last_frame.f_back.f_globals[last_func_name]
    ........elif last_func_name in last_frame.f_back.f_locals.keys():
    ............last_func = last_frame.f_back.f_locals[last_func_name]
    ....is_lineno_hacked = False;
    ....if not last_func:
    ........print("Holy crap. Assume we have hacked our lineno")
    ........is_lineno_hacked = True
    ....elif '__is_lineno_hacked__' in last_func.__dict__.keys():
    ........is_lineno_hacked = True
    ....if is_lineno_hacked:
    ........last_code_arr = bytearray(last_code.co_code)
    ........call_lineno = last_frame.f_lineno
    ........# last_code_arr[last_frame.f_lineno] = opmap['CALL_FUNCTION']
    ........attr_name = []
    ........pos_code = 2
    ........pos_off = 1
    ........load_var_op = last_code_arr[call_lineno - pos_code]
    ........load_var_pos = last_code_arr[call_lineno - pos_off]
    ........while load_var_op == opmap['LOAD_ATTR']:
    ............attr_name.append(last_code.co_names[load_var_pos])
    ............load_var_op = last_code_arr[call_lineno - pos_code]
    ............load_var_pos = last_code_arr[call_lineno - pos_off]
    ............pos_code += 2
    ............pos_off += 2
    ........if load_var_op == opmap['LOAD_FAST']:
    ............attr_name.append(last_code.co_varnames[load_var_pos])
    ............return '.'.join(attr_name)
    ........elif load_var_op == opmap['LOAD_GLOBAL'] or load_var_op == opmap['LOAD_NAME']:
    ............attr_name.append(last_code.co_names[load_var_pos])
    ............return '.'.join(attr_name)
    ........elif load_var_op == opmap['LOAD_DEREF']:
    ............attr_name.append(last_code.co_freevars[load_var_pos])
    ............return '.'.join(attr_name)

    ........print("I don't know, maybe just consts")
    ........return None
    ....else:
    ........last_func = hack_line_numbers(last_func)
    ........sys._getframe(0).f_locals[last_func_name] = last_func
    ........return last_func()
    ........# sys._getframe(0).f_back = last_frame.f_back
    ........# last_frame.clear()

    def do_nothing():
    ....pass

    @hack_line_numbers
    def f():
    ....a_var = 'str'
    ....print(get_variable_name(a_var)) # a_var


    def g():
    ....ar = 1
    ....arrrrrgggghhhhhh = 1
    ....do_nothing()
    ....do_nothing()
    ....do_nothing()
    ....do_nothing()
    ....name = None
    ....def nested_get_varname():
    ........return get_variable_name(arrrrrgggghhhhhh)
    ....name = nested_get_varname()
    ....print(name) # arrrrrgggghhhhh

    class A:
    ....pass

    def h():
    ....test = A()
    ....test.t = A()
    ....test.t.t = 1
    ....return get_variable_name(test.t.t) # test.t.t

    def u():
    ....return get_variable_name(1)

    def test_all():
    ....f()
    ....g()
    ....print(h())
    ....print(u())
    Xiaobaixiao
        26
    Xiaobaixiao  
       2017-11-07 13:23:33 +08:00
    用 global 声明?

    >>> def get_var_literal(var):
    ... global var_literal
    ... var_literal=666666
    ... print(var_literal)
    ...
    >>> s='abc'
    >>> get_var_literal(s)
    666666
    wizardoz
        27
    wizardoz  
       2017-11-07 14:00:12 +08:00
    我觉得这个需求在 Python 中属于伪需求。楼主是不是用其它语言中的经验来考虑 Python 了?

    在 Python 中
    def fun(**kwargs):
    print(kwargs)

    fun(a=1, b=2, c=3)

    可以用来完成一样的事情,并且要求在调用的时候指定参数名,语义更明确。
    takeoffyoung
        28
    takeoffyoung  
       2017-11-07 14:16:19 +08:00   1
    这个不是叫变量名么?字面量是什么意思?
    https://stackoverflow.com/questions/932818/retrieving-a-variables-name-in-python-at-runtime

    这个值只在查看堆栈信息有意义,也只能从中获取。
    shiina
        29
    shiina  
       2017-11-07 14:36:57 +08:00
    虽然不知道怎么解, 不过好像大部分回复都误解了题主的意思
    s, var, abc 这三个里题主想要的是 s

    @lrxiao 解法刁钻, 菜 b 一个表示看不懂
    lolizeppelin
        30
    lolizeppelin  
       2017-11-08 10:40:40 +08:00 via Android
    openstack 的 taskflow 项目里有相关代码可以直接抄

    位置就在 atom 这个基类里
    lolizeppelin
        31
    lolizeppelin  
       2017-11-08 10:44:02 +08:00 via Android
    这需求主要用来处理获取函数参数列表
    处理可选参数和必选参数

    taskflow 里的处理非常不错 还能 rebind 参数名避免冲突
    billgreen1
        32
    billgreen1  
       2017-11-08 11:41:56 +08:00
    这在 R 语言里叫做 non standard evaluation, python 也可以实现。参考
    http://www.ibis-project.org/design-composability/
    ethusdt
        33
    ethusdt  
       2017-11-09 10:15:53 +08:00
    flask 里的 render_templete 函数就有此功能
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2574 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 04:55 PVG 12:55 LAX 21:55 JFK 00:55
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86