def get_var_literal(var):
var_literal=? # 怎么获取到 var 的字面量?
print(var_literal)
s = 'abc'
get_var_literal(s)
#=>s 希望能打印出 s,而不是打印出来 abc
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 |
2
sagaxu 2017-11-07 10:07:15 +08:00
这个 name 不一定存在啊,get_var_literal(foo())
|
3
ipwx 2017-11-07 10:13:25 +08:00
你的这个需求是很别扭的,建议用别的设计。
|
4
est 2017-11-07 10:14:05 +08:00
最喜欢这种扯蛋的问题了
import inspect inspect.getargspec(get_var_literal).args |
5
est 2017-11-07 10:17:44 +08:00
貌似我审题错误。LZ 自己去翻 inspect 吧
|
6
vtoexsir OP @crazycabbage
"if loc[key] == s:" #这里的 s 不是固定不变的 我的本意是:获取函数的实参的字面值。比如,传递 a 这个参数,就返回 a 这个字符串;传递 b 这个参数,就返回 b 这个字符串,而不管 a 和 b 自身是什么类型,也不管是什么具体的值,只获取实参的字面量 |
7
ipwx 2017-11-07 10:24:53 +08:00 1
@vtoexsir 你这事情有方法做,但是你得写很多代码,而且必须源代码 .py 存在,而不是 .pyc 。首先,拿到 stacktrace,找到调用你这个函数的代码的行号。然后,用 ast 去分析那一行代码。最后,你拿到了字面量。
然而何必呢?我看不出来 Python 里面搞这一套的意义。换个设计不是更好? |
8
alber1986 2017-11-07 10:27:14 +08:00
看看 Python 视频教程就懂了
http://www.sucaihuo.com/video/172-2-0 |
9
vtoexsir OP @est inspect.getargspec(get_var_literal).args # 这是获取形参的字面值,不是实参的字面值
不过非常感谢,inspection 可能会有戏解决这个问题! |
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) |
11
lrxiao 2017-11-07 10:51:13 +08:00
|
12
lrxiao 2017-11-07 10:51:52 +08:00
loc = sys._getframe(1).f_locals
|
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') |
14
qsnow6 2017-11-07 11:06:52 +08:00
|
15
jmc891205 2017-11-07 11:11:42 +08:00
请问为什么会有这样的需求啊
|
16
Kilerd 2017-11-07 11:17:03 +08:00
用装饰器,先把抓到的变量读取 __name__ 再传进去
|
17
qsnow6 2017-11-07 11:17:09 +08:00
|
18
Kilerd 2017-11-07 11:20:11 +08:00
emmm, 试了下除非使用 kwargs 的方式穿进去,不然都无解
|
19
SuperMild 2017-11-07 11:26:40 +08:00
楼主是想在没有源码的情况下分析源码?
如果有源码,想不到在程序里分析参数名称的用处。 |
20
SuperMild 2017-11-07 11:27:26 +08:00
还不如直接分析源码
|
21
qsnow6 2017-11-07 11:27:30 +08:00
1 以 kwargs 传进函数里
2 在 locals()里找,找到了打印出来 |
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 有兴趣可以研究研究 |
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 空格全部替换的. 为了缩进。。。。。 |
24
lrxiao 2017-11-07 11:50:45 +08:00
emmm 原来可以 kwargs 23333
|
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()) |
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 |
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) 可以用来完成一样的事情,并且要求在调用的时候指定参数名,语义更明确。 |
28
takeoffyoung 2017-11-07 14:16:19 +08:00 1
这个不是叫变量名么?字面量是什么意思?
https://stackoverflow.com/questions/932818/retrieving-a-variables-name-in-python-at-runtime 这个值只在查看堆栈信息有意义,也只能从中获取。 |
29
shiina 2017-11-07 14:36:57 +08:00
|
30
lolizeppelin 2017-11-08 10:40:40 +08:00 via Android
openstack 的 taskflow 项目里有相关代码可以直接抄
位置就在 atom 这个基类里 |
31
lolizeppelin 2017-11-08 10:44:02 +08:00 via Android
这需求主要用来处理获取函数参数列表
处理可选参数和必选参数 taskflow 里的处理非常不错 还能 rebind 参数名避免冲突 |
32
billgreen1 2017-11-08 11:41:56 +08:00
这在 R 语言里叫做 non standard evaluation, python 也可以实现。参考
http://www.ibis-project.org/design-composability/ |
33
FaiChou 2017-11-09 10:15:53 +08:00
flask 里的 render_templete 函数就有此功能
|