ChatGPT解决这个技术问题 Extra ChatGPT

动态设置局部变量

这个问题在这里已经有了答案:如何动态创建变量? [重复] (8个回答) 7年前关闭。

如何在 Python 中动态设置局部变量(变量名是动态的)?

如果变量名是动态的,那你就错了。
我曾经编程从输入文件中读取化学物种名称,然后根据我从文本文件中读取的内容为这些物种名称创建对象。这样我就可以说 H2O.mwt 之类的东西。恕我直言,这样做可能有正当理由。
我知道这是不好的做法。但在某些情况下,这可能是一条肮脏的捷径。此外,这很有趣,它显示了语言的动态性。反馈在这里也很好,因此读者不会轻易使用它,但这不是拒绝投票的正当理由。
快速本地数组与代码对象(即 co_varnames、co_nlocals)耦合。它是固定的。 locals() 只是调用 PyFrame_FastToLocals 来创建当前值的 dict 视图。如果您使用 ctypes 在当前帧 (sys._getframe(0)) 上调用 PyFrame_LocalsToFast,您可以基于此 dict 动态更新快速本地。
@eryksun - 这听起来像是一个完整而有见地的答案的开始 - 如果你将它编译成一个,我会接受它

u
user2357112

与已经发布的其他答案相反,您不能直接修改 locals() 并期望它能够工作。

>>> def foo():
    lcl = locals()
    lcl['xyz'] = 42
    print(xyz)


>>> foo()

Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    foo()
  File "<pyshell#5>", line 4, in foo
    print(xyz)
NameError: global name 'xyz' is not defined

修改 locals() 未定义。在 locals()globals() 相同的函数之外,它会起作用;在函数内部,它通常不起作用。

使用字典,或在对象上设置属性:

d = {}
d['xyz'] = 42
print(d['xyz'])

或者,如果您愿意,可以使用一个类:

class C: pass

obj = C()
setattr(obj, 'xyz', 42)
print(obj.xyz)

编辑:访问不是函数的命名空间中的变量(因此模块、类定义、实例)通常通过字典查找来完成(正如 Sven 在评论中指出的那样,存在例外情况,例如定义 __slots__)。函数局部变量可以针对速度进行优化,因为编译器(通常)提前知道所有名称,因此在调用 locals() 之前没有字典。

在 Python locals() 的 C 实现中(从函数内部调用)创建一个从局部变量的当前值初始化的普通字典。在每个函数中,对 locals() 的任意数量的调用都将返回相同的字典,但对 locals() 的每次调用都将使用局部变量的当前值更新它。这可能给人的印象是对字典元素的分配被忽略了(我最初写的是这种情况)。因此,对从 locals() 返回的字典中现有键的修改只会持续到下一次调用同一范围内的 locals()

在 IronPython 中,事情的工作方式有点不同。任何在其内部调用 locals() 的函数都使用字典作为其局部变量,因此对局部变量的赋值会更改字典,对字典的赋值会更改变量,只有当您在下面显式调用 locals() 时那个名字。如果您将不同的名称绑定到 IronPython 中的 locals 函数,则调用它会为您提供绑定名称的范围的局部变量,并且无法通过它访问函数局部变量:

>>> def foo():
...     abc = 123
...     lcl = zzz()
...     lcl['abc'] = 456
...     deF = 789
...     print(abc)
...     print(zzz())
...     print(lcl)
...
>>> zzz =locals
>>> foo()
123
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
>>>

这一切都可能随时改变。唯一可以保证的是,您不能依赖分配给 locals() 返回的字典的结果。


两个小的更正: 1. locals() 的返回值是 CPython 2.x 和 3.x 中的标准字典,可以照常更改(但更改传播回本地范围)。 2. 访问类和实例名称空间并不总是涉及字典查找。有几个例外,包括定义 __slots__ 的类的实例。
谢谢@SvenMarnach,我已经在你的第二点上更新了我的答案。我在第一点上写的内容非常具体,我确信我必须已经测试过它,所以要么我完全混淆了自己,要么它可能因 Python 版本而异。我会再次检查并稍后更新答案。
@SvenMarnach,我想通了:在同一个函数中多次调用 locals() 会返回同一个字典并在每次调用它时覆盖现有的键,所以因为我混合了对 locals() 的多次调用并打印了它最初返回的字典对我来说,就好像它没有改变一样。我已经在答案中解释了这个可能的陷阱。谢谢。
@CharlieParker 更好,因为如果您事先不知道本地人的名称,则除了通过 locals() 字典外无法访问它,并且无法确保您没有覆盖或隐藏另一个本地人或甚至是一个全球名称。使用 dict 可以安全地将任何字符串存储为键。
@Duncan 我一直认为 python 是一种成年人同意的语言。如果一个人使用 exec 或覆盖 locals() 一个人将承担您提到的事情的风险(例如覆盖事情)。如果可以确定将变量加载到局部变量以及名称是什么,则可以稍后使用这些变量,如果它们没有预先定义,编译器就会出现问题。我个人认为没什么大不了的,总是可以重写像 for = 'breaking for keyword' 这样的 gobals,而无需覆盖本地人的帮助。我想我不欣赏真正的问题或什么...
k
kindall

其他人建议分配给 locals()。这在函数内部不起作用,在函数中使用 LOAD_FAST 操作码访问局部变量,除非您在函数的某处有 exec 语句。为了支持这种可能创建在编译时未知的新变量的语句,Python 被迫在函数内按名称访问局部变量,因此写入 locals() 有效。 exec 可以在执行的代码路径之外。

def func(varname):
    locals()[varname] = 42
    return answer           # only works if we passed in "answer" for varname
    exec ""                 # never executed

func("answer")
>>> 42

注意:这只适用于 Python 2.x。他们在 Python 3 中消除了这种愚蠢,其他实现(Jython、IronPython 等)可能也不支持它。

不过,这是个坏主意。如果您不知道变量的名称,您将如何访问这些变量?可能是 locals()[xxx]。那么,为什么不直接使用您自己的字典而不是污染 locals()(并借此机会覆盖您的函数实际需要的变量)?


C
Community

(只是给其他人谷歌搜索的快速说明)

好的,所以修改 locals() is not the way to go(同时修改 globals() is supposed to work)。与此同时,exec 可能是,但它的速度非常慢,因此,与正则表达式一样,我们可能希望先compile()

# var0 = 0; var1 = 1; var2 = 2
code_text = '\n'.join( "var%d = %d" % (n, n) for n in xrange(3) )

filename = ''
code_chunk = compile( code_text, filename, 'exec' )

# now later we can use exec:
exec code_chunk # executes in the current context

...如果您可以使用字典,为什么还要这样做?
“我知道这不是一个好的做法,并且这些言论是合法的,但这并不使它成为一个坏问题,只是一个更具理论性的问题”(q)TS - 需要相当长的时间来解释。这是一个不同的问题。假设在大多数情况下可以安全地忽略这个答案)ps。如果您决定需要它,它就在那里,这不是很好吗?
p
poke

您可以直接修改 locals()

locals()['foo'] = 'bar'

但更好的方法是拥有一些将所有动态变量名称保存为字典键的字典:

d = {}
for some in thing:
    d[some] = 'whatever'

-1 建议修改 locals()
@Duncan:如果您仔细阅读,我不建议这样做。我只是说可以做到,但有更好的方法。
说你可以这样做是我反对的。来自文档:Note The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.
-1 修改 locals() 不起作用。
locals()['foo'] = 'bar' 适用于 python 2.7 和 3,这是否在某个时候改变了?
R
Redsplinter

我想我已经花了最后......几个小时试图解决缺少函数闭包的问题,我想出了这个,这可能会有所帮助:

common_data = ...stuff...
def process(record):
    ...logic...

def op():
    for fing in op.func_dict: # Key line number 1
        exec(fing + " = op.func_dict[fing]") # Key line number 2

    while common_data.still_recieving:
        with common_data._lock:
            if common_data.record_available:
                process(common_data.oldest_record)
        time.sleep(1.0)

op.func_dict.update(locals()) # Key line number 3
threading.Thread(target = op).start()

...

这是一个相当笨拙/人为的例子,但如果有很多当地人或者你仍在原型制作过程中,这个模式就会变得有用。大多数情况下,我只是对复制或移动所有数据存储以处理回调委托等感到痛苦。


A
Adam Zalcman

您可以使用本地字典并将所有动态绑定作为项目放入字典中。然后知道了这样一个“动态变量”的名称,您可以使用名称作为键来获取它的值。


0
0x90

假设我们有以下字典:

DictionaryA = {'No Rating': ['Hobbit', 'Movie C', 'Movie G'],
               'Forget It': ['Avenger', 'Movie B'], 
               'Must See': ['Children of Men', 'Skyfall', 'Movie F'], 
               '3': ['X-Men', 'Movie D'],
               '2': ['Captain America', 'Movie E'], 
               '4': ['Transformers', 'Movie A']} 

我想创建如下新字典:

NewDictionary1 = {'No Rating': ['Hobbit', 'Movie C', 'Movie G']} 
NewDictionary2 = {'Forget It': ['Avenger', 'Movie B']} 
NewDictionary3 = {'Must See': ['Children of Men', 'Skyfall', 'Movie F']}

单线:

dics = [{k:v} for k,v in DictionaryA.iteritems()]

将输出到:

[{'Must See': ['Children of Men', 'Skyfall', 'Movie F']}, {'Forget It': ['Avenger', 'Movie B']}, {'No Rating': ['Hobbit', 'Movie C', 'Movie G']}, {'3': ['X-Men', 'Movie D']}, {'2': ['Captain America', 'Movie E']}, {'4': ['Transformers', 'Movie A']}]

但是要精确声明变量,我们可以使用:

>>> i=0
>>> lcl = locals()
>>> for key,val in DictionaryA.iteritems():
        lcl["Dict" + str(i)] = {key:val}
        i += 1

可以看出前 3 个 Dict 变量:

>>> Dict0
{'Must See': ['Children of Men', 'Skyfall', 'Movie F']}
>>> Dict1
{'Forget It': ['Avenger', 'Movie B']}
>>> Dict2
{'No Rating': ['Hobbit', 'Movie C', 'Movie G']}

正如其他人所提到的,如果要将其放入函数中,则应将其添加到 globals()

>>> glb = globals()
>>> for key,val in DictionaryA.iteritems():
        glb["Dict" + str(i)] = {key:val}
        i += 1