关于python:创建独立变量字典的简单方法?

Simpler way to create dictionary of separate variables?

我希望能够得到一个字符串形式的变量名,但是我不知道python是否有那么多的自省能力。类似:

1
2
>>> print(my_var.__name__)
'my_var'

我想这样做是因为我有很多变量,我想把它们变成一本字典,比如:

1
2
3
4
5
bar = True
foo = False
>>> my_dict = dict(bar=bar, foo=foo)
>>> print my_dict
{'foo': False, 'bar': True}

但我想要一些更自动的。

python有locals()vars(),所以我想有办法。


As unwind said, this isn't really something you do in Python - variables are actually name mappings to objects.

However, here's one way to try and do it:

1
2
3
4
5
6
7
8
 >>> a = 1
 >>> for k, v in list(locals().iteritems()):
         if v is a:
             a_as_str = k
 >>> a_as_str
 a
 >>> type(a_as_str)
 'str'


我很想这么做。这是一个非常类似于rlotun的建议,但它是一条单行线,这对我很重要:

1
2
blah = 1
blah_name = [ k for k,v in locals().iteritems() if v is blah][0]

Python 3 +

1
2
blah = 1
blah_name = [ k for k,v in locals().items() if v is blah][0]


你想这么做吗?

1
dict( (name,eval(name)) for name in ['some','list','of','vars'] )

例子

1
2
3
4
5
6
>>> some= 1
>>> list= 2
>>> of= 3
>>> vars= 4
>>> dict( (name,eval(name)) for name in ['some','list','of','vars'] )
{'list': 2, 'some': 1, 'vars': 4, 'of': 3}


这是一个黑客。它不适用于所有的python实现发行版(特别是那些没有traceback.extract_stack的发行版)。

1
2
3
4
5
6
7
8
9
10
11
12
13
import traceback

def make_dict(*expr):
    (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
    begin=text.find('make_dict(')+len('make_dict(')
    end=text.find(')',begin)
    text=[name.strip() for name in text[begin:end].split(',')]
    return dict(zip(text,expr))

bar=True
foo=False
print(make_dict(bar,foo))
# {'foo': False, 'bar': True}

注意,这个黑客程序是脆弱的:

1
2
make_dict(bar,
          foo)

(在两条线路上呼叫make_dict)将不起作用。

不是用foobar的值生成dict,从字符串变量名'foo''bar'中生成dict的方法要多得多:

1
dict([(name,locals()[name]) for name in ('foo','bar')])


这在没有"变量"的Python中是不可能的。python有名称,同一对象可以有多个名称。


我认为我的问题将有助于说明为什么这个问题是有用的,并且它可能会对如何回答它提供更多的洞察。我编写了一个小函数来对代码中的各种变量进行快速的内联头检查。基本上,它列出了变量名、数据类型、大小和其他属性,所以我可以很快地发现我犯的任何错误。代码很简单:

1
2
3
4
5
def details(val):
  vn = val.__name__                 #  If such a thing existed
  vs = str(val)
  print("The Value of"+ str(vn) +" is" + vs)
  print("The data type of" + vn +" is" + str(type(val)))

因此,如果您有一些复杂的字典/列表/元组情况,让解释器返回您分配的变量名会非常有帮助。例如,这里有一本奇怪的字典:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
m = 'abracadabra'
mm=[]    
for n in m:
  mm.append(n)
mydic = {'first':(0,1,2,3,4,5,6),'second':mm,'third':np.arange(0.,10)}



details(mydic)

The Value of mydic is {'second': ['a', 'b', 'r', 'a', 'c', 'a', 'd', 'a', 'b', 'r', 'a'], 'third': array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.]), 'first': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
The data type of mydic is <type 'dict'>

details(mydic['first'])
The Value of mydic['first'] is (0, 1, 2, 3, 4, 5, 6)]
The data type of mydic['first'] is <type 'list'>

details(mydic.keys())
The Value of mydic.keys() is ['second', 'third', 'first']
The data type of mydic.keys() is <type 'tuple'>

details(mydic['second'][0])
The Value of mydic['second'][0] is a
The data type of mydic['second'][0] is <type 'str'>

我不确定我是否把这个放在正确的地方,但我认为这可能有帮助。我希望如此。


根据这个问题的答案,我写了一个简洁实用的函数。我把它放在这儿以防有用。

1
2
3
4
5
6
7
8
9
10
def what(obj, callingLocals=locals()):
   """
    quick function to print name of input and value.
    If not for the default-Valued callingLocals, the function would always
    get the name as"obj", which is not what I want.    
   """

    for k, v in list(callingLocals.items()):
         if v is obj:
            name = k
    print(name,"=", obj)

用途:

1
2
3
4
>> a = 4
>> what(a)
a = 4
>>|

在python 3中,这很容易

1
2
3
4
myVariable = 5
for v in locals():
  if id(v) == id("myVariable"):
    print(v, locals()[v])

这将打印:

myVariable 5


我发现,如果您已经有了一个特定的值列表,那么@s.lotts描述的方法是最好的;但是,下面描述的方法可以很好地使代码中添加的所有变量和类都不需要提供变量名,尽管您可以根据需要指定它们。代码可以扩展到排除类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import types
import math  # mainly showing that you could import what you will before d

# Everything after this counts
d = dict(globals())

def kv_test(k,v):
    return (k not in d and
            k not in ['d','args'] and
            type(v) is not types.FunctionType)

def magic_print(*args):
    if len(args) == 0:
        return {k:v for k,v in globals().iteritems() if kv_test(k,v)}
    else:
        return {k:v for k,v in magic_print().iteritems() if k in args}

if __name__ == '__main__':
    foo = 1
    bar = 2
    baz = 3
    print magic_print()
    print magic_print('foo')
    print magic_print('foo','bar')

输出:

1
2
3
{'baz': 3, 'foo': 1, 'bar': 2}
{'foo': 1}
{'foo': 1, 'bar': 2}


Python 3。使用inspect捕获调用的本地名称空间,然后使用这里介绍的思想。可以返回多个已指出的答案。

1
2
3
4
5
6
7
8
9
10
def varname(var):
  import inspect
  frame = inspect.currentframe()
  var_id = id(var)
  for name in frame.f_back.f_locals.keys():
    try:
      if id(eval(name)) == var_id:
        return(name)
    except:
      pass


这是我创建的用来读取变量名的函数。它更通用,可用于不同的应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
def get_variable_name(*variable):
    '''gets string of variable name
    inputs
        variable (str)
    returns
        string
    '''

    if len(variable) != 1:
        raise Exception('len of variables inputed must be 1')
    try:
        return [k for k, v in locals().items() if v is variable[0]][0]
    except:
        return [k for k, v in globals().items() if v is variable[0]][0]

在指定的问题中使用它:

1
2
3
4
5
6
>>> foo = False
>>> bar = True
>>> my_dict = {get_variable_name(foo):foo,
               get_variable_name(bar):bar}
>>> my_dict
{'bar': True, 'foo': False}

在看书的时候,我看到了很大的摩擦。很容易给予一个错误的答案,然后让别人给出正确的答案。不管怎样,这是我发现的。

发件人:【effbot.org】(http://effbot.org/zone/python objects.htm name)

名称有点不同-它们不是对象的真正属性,对象本身不知道它叫什么。

对象可以有任意数量的名称,或者根本没有名称。

名称位于命名空间中(例如模块命名空间、实例命名空间、函数的本地命名空间)。

注意:它说物体本身不知道它叫什么,所以这就是线索。python对象不是自引用的。然后它会说,名字存在于名称空间中。我们有TCL/TK的。所以也许我的回答会有帮助(但它确实帮助了我)

1
2
3
    jj = 123
    print eval("'" + str(id(jj)) +"'")
    print dir()

1
2
166707048
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'jj']

所以在列表的末尾有"jj"。

将代码重写为:

1
2
3
4
    jj = 123
    print eval("'" + str(id(jj)) +"'")
    for x in dir():
        print id(eval(x))
1
2
3
4
5
6
7
8
161922920
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'jj']
3077447796
136515736
3077408320
3077656800
136515736
161922920

这个讨厌的代码ID是变量/对象/任何你学究所说的名称。

就是这样。"jj"的内存地址在我们直接查找时与在全局名称空间中查找字典时相同。我相信你可以做一个功能来完成这个。只需记住您的变量/对象/wypci所在的名称空间。

QED。


我写了一个巫术包来做这种魔法。你可以写:

1
2
3
from sorcery import dict_of

my_dict = dict_of(foo, bar)

1
2
3
4
5
6
7
8
9
10
11
12
13
import re
import traceback

pattren = re.compile(r'[\W+\w+]*get_variable_name\((\w+)\)')
def get_variable_name(x):
    return pattren.match( traceback.extract_stack(limit=2)[0][3]) .group(1)

a = 1
b = a
c = b
print get_variable_name(a)
print get_variable_name(b)
print get_variable_name(c)


我上传了一个解决方案给Pypi。它是一个定义C的nameof函数等价物的模块。

它迭代所调用帧的字节码指令,获取传递给它的变量/属性的名称。这些名称位于函数名称后面的LOAD指令的.argrepr中。


大多数对象没有名称属性。(类、函数和模块是这样的;还有其他内置类型吗?)

除了print("my_var"),您还希望对print(my_var.__name__)有什么其他要求?您可以直接使用字符串吗?

你可以"切"口述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def dict_slice(D, keys, default=None):
  return dict((k, D.get(k, default)) for k in keys)

print dict_slice(locals(), ["foo","bar"])
# or use set literal syntax if you have a recent enough version:
print dict_slice(locals(), {"foo","bar
<div class="
suo-content">[collapse title=""]<ul><li>+但是我知道这个名字不存在,为什么每个人都把这个"类似"的东西当作垃圾?你的解决方案不能解决这个问题,因为我不想对名字进行编码,我会按照我在问题中给出的dict解决方案进行改写。</li><li>@e-satis:如果简单地使用locals()中的所有内容就能解决您的问题,我不知道您在问什么。我猜您可以调用<wyn>some_func(var)</wyn>,所以我试图指出,离<wyn>some_func("var")</wyn>不远,dictslice允许您一次获得多个变量的名称-值映射。</li></ul>[/collapse]</div><hr><P>我在研究一个类似的问题。@洛特说:"如果你有变量列表,那么"发现"它们的名字有什么意义?"我的答案只是想看看是否可以这样做,如果出于某种原因你想把变量按类型排序到列表中。总之,在我的研究中,我遇到了这个问题,我的解决方案有点扩展,并且基于@rlotun解决方案。还有一件事,@unutbu说,"这个想法很有价值,但请注意,如果两个变量名引用相同的值(例如true),那么可能会返回一个意料之外的变量名。"在这个练习中,这是真的,所以我对每种可能性都使用类似的列表理解来处理它:<wyn>isClass = [i for i in isClass if i != 'item']</wyn>。没有它,"项目"会出现在每个列表中。</P>[cc lang="python"]__metaclass__ = type

from types import *

class Class_1: pass
class Class_2: pass
list_1 = [1, 2, 3]
list_2 = ['dog', 'cat', 'bird']
tuple_1 = ('one', 'two', 'three')
tuple_2 = (1000, 2000, 3000)
dict_1 = {'one': 1, 'two': 2, 'three': 3}
dict_2 = {'dog': 'collie', 'cat': 'calico', 'bird': 'robin'}
x = 23
y = 29
pie = 3.14159
eee = 2.71828
house = 'single story'
cabin = 'cozy'

isClass = []; isList = []; isTuple = []; isDict = []; isInt = []; isFloat = []; isString = []; other = []

mixedDataTypes = [Class_1, list_1, tuple_1, dict_1, x, pie, house, Class_2, list_2, tuple_2, dict_2, y, eee, cabin]

print '
MIXED_DATA_TYPES total count:', len(mixedDataTypes)

for item in mixedDataTypes:
    try:
        # if isinstance(item, ClassType): # use this for old class types (before 3.0)
        if isinstance(item, type):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isClass.append(mapping_as_str)
            isClass = [i for i in isClass if i != 'item']

        elif isinstance(item, ListType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isList.append(mapping_as_str)
            isList = [i for i in isList if i != 'item']

        elif isinstance(item, TupleType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isTuple.append(mapping_as_str)
            isTuple = [i for i in isTuple if i != 'item']

        elif isinstance(item, DictType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isDict.append(mapping_as_str)
            isDict = [i for i in isDict if i != 'item']

        elif isinstance(item, IntType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isInt.append(mapping_as_str)
            isInt = [i for i in isInt if i != 'item']

        elif isinstance(item, FloatType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isFloat.append(mapping_as_str)
            isFloat = [i for i in isFloat if i != 'item']

        elif isinstance(item, StringType):
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    isString.append(mapping_as_str)
            isString = [i for i in isString if i != 'item']

        else:
            for k, v in list(locals().iteritems()):
                if v is item:
                    mapping_as_str = k
                    other.append(mapping_as_str)
            other = [i for i in other if i != 'item']

    except (TypeError, AttributeError), e:
        print e

print '
 isClass:', len(isClass), isClass
print '  isList:', len(isList), isList
print ' isTuple:', len(isTuple), isTuple
print '  isDict:', len(isDict), isDict
print '   isInt:', len(isInt), isInt
print ' isFloat:', len(isFloat), isFloat
print 'isString:', len(isString), isString
print '   other:', len(other), other

# my output and the output I wanted
'''
MIXED_DATA_TYPES total count: 14

 isClass: 2 ['Class_1', 'Class_2']
  isList: 2 ['list_1', 'list_2']
 isTuple: 2 ['tuple_1', 'tuple_2']
  isDict: 2 ['dict_1', 'dict_2']
   isInt: 2 ['x', 'y']
 isFloat: 2 ['pie', 'eee']
isString: 2 ['house', 'cabin']
   other: 0 []
'''


对于python 2.7和更新版本,也有字典理解,这使得它更短一些。如果可能的话,我会用getattr代替eval(eval是邪恶的),就像上面的答案一样。自我可以是任何有你所关注的上下文的对象。它可以是一个对象或locals=locals()等。

1
{name: getattr(self, name) for name in ['some', 'vars', 'here]}

你可以用easydict

1
2
3
4
5
6
7
8
9
>>> from easydict import EasyDict as edict
>>> d = edict({'foo':3, 'bar':{'x':1, 'y':2}})
>>> d.foo
3
>>> d.bar.x
1
>>> d = edict(foo=3)
>>> d.foo
3

另一个例子:

1
2
3
4
>>> d = EasyDict(log=False)
>>> d.debug = True
>>> d.items()
[('debug', True), ('log', False)]


也许我想得太多了,但是……

1
2
3
4
5
6
7
8
9
10
11
12
str_l = next((k for k,v in locals().items() if id(l) == id(v)))


>>> bar = True
>>> foo = False
>>> my_dict=dict(bar=bar, foo=foo)
>>> next((k for k,v in locals().items() if id(bar) == id(v)))
'bar'
>>> next((k for k,v in locals().items() if id(foo) == id(v)))
'foo'
>>> next((k for k,v in locals().items() if id(my_dict) == id(v)))
'my_dict'


在python3上,此函数将获取堆栈中最外部的名称:

1
2
3
4
5
6
7
8
9
10
11
12
13
import inspect


def retrieve_name(var):
       """
        Gets the name of var. Does it from the out most frame inner-wards.
        :param var: variable to get name from.
        :return: string
       """

        for fi in reversed(inspect.stack()):
            names = [var_name for var_name, var_val in fi.frame.f_locals.items() if var_val is var]
            if len(names) > 0:
                return names[0]

它在代码的任何地方都很有用。遍历反向堆栈以查找第一个匹配项。


好吧,几天前我遇到了同样的需求,我不得不得到一个变量的名称,它指向对象本身。

为什么这么必要?

简而言之,我正在为玛雅开发一个插件。核心插件是用C++构建的,但是GUI是通过Python绘制的(因为它不是处理器密集型的)。因为到目前为止,我还不知道如何从插件中获取return多个值,除了默认的MStatus,所以为了在python中更新字典,我必须将变量的名称传递给实现GUI的对象,该对象包含字典本身,然后使用MGlobal::executePythonCommand()更新来自maya全局范围的字典。

要做到这一点,我所做的是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time

class foo(bar):

    def __init__(self):
        super(foo, self).__init__()
        self.time = time.time() #almost guaranteed to be unique on a single computer

    def name(self):
        g = globals()
        for x in g:
            if isinstance(g[x], type(self)):
                if g[x].time == self.time:
                    return x
                    #or you could:
                    #return filter(None,[x if g[x].time == self.time else None for x in g if isinstance(g[x], type(self))])
                    #and return all keys pointing to object itself

我知道在globals中这不是完美的解决方案,许多键可能指向同一对象,例如:

1
2
3
4
5
6
a = foo()
b = a
b.name()
>>>b
or
>>>a

而且这种方法不是线程安全的。如果我错了就纠正我。

至少这种方法解决了我的问题,它获取全局范围内指向对象本身的任何变量的名称,并将其作为参数传递给插件,供其内部使用。

我在int上尝试过这个方法,但问题是这些原始类不能被绕过(如果错误的话,请更正使用的技术术语)。您可以重新实现int,然后执行int = foo,但a = 3永远不会是foo的对象,而是原始对象。为了克服这一点,你必须让a = foo(3)工作。


虽然这可能是一个糟糕的想法,但它与Rlotun的答案是一致的,但它会更频繁地返回正确的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import inspect
def getVarName(getvar):
  frame = inspect.currentframe()
  callerLocals = frame.f_back.f_locals
  for k, v in list(callerLocals.items()):
    if v is getvar():
      callerLocals.pop(k)
      try:
        getvar()
        callerLocals[k] = v
      except NameError:
        callerLocals[k] = v
        del frame
        return k
  del frame

你这样称呼它:

1
2
3
4
5
bar = True
foo = False
bean = False
fooName = getVarName(lambda: foo)
print(fooName) # prints"foo"

应该得到清单然后返回

1
2
3
4
5
6
7
def get_var_name(**kwargs):
   """get variable name
        get_var_name(var = var)
    Returns:
        [str] -- var name
   """

    return list(kwargs.keys())[0]


1
2
3
4
5
6
7
8
9
10
>>> a = 1
>>> b = 1
>>> id(a)
34120408
>>> id(b)
34120408
>>> a is b
True
>>> id(a) == id(b)
True

这样就可以得到a或者a或者b的varname。