请考虑以下代码:

from sklearn.model_selection import ParameterGrid

def conf1():
    return {"p1": [1,2], "p2": [4,5,6]}

def f(aa, bb, cc):
    c = locals()
    print("CONFIG, locals():")
    for k, v, in c.items():
        print(f"{k}: {v}")
    
    #>
    params = eval("conf1()")
    grid = ParameterGrid(params)
    #<

    d = {**c}
    
    print("CONF after eval")
    for k, v, in d.items():
        print(f"{k}: {v}")
    

f(1,2,3)

其输出为:

CONFIG, locals():
aa: 1
bb: 2
cc: 3
CONF after eval
aa: 1
bb: 2
cc: 3
c: {'aa': 1, 'bb': 2, 'cc': 3, 'c': {...}, 'k': 'cc', 'v': 3}
k: cc
v: 3

函数f使用其局部名称,特别是其接收的参数,创建字典c.然后由eval("conf1()")创建一个新词典.然后,创建一个d = {**c}.

似乎当执行c = {**d}时,d = locals()被重新判断.因为在这一点上,本地环境也包含kvd,它们最终会变成d.如果我删除eval(#>#<之间的行),我会得到我所期望的,即cd包含相同的项.

可能不是因为eval,但我真的无法理解发生了什么.我找不到任何解释.有什么帮助吗?


因为我在 comments 中读到config是可变"locals()"的名称,所以我做了这个测试:

def f(param1, param2):
    config = locals()
    print("1. config:")
    for k, v in config.items():
        print(f"- {k}: {v}")
        
    name =1 
    
    print("2. config:")
    for k, v in config.items():
        print(f"- {k}: {v}")
    
f(1, 2)

带输出:

1. config:
- param2: 2
- param1: 1
2. config:
- param2: 2
- param1: 1

所以它似乎不那么易变.

如果加上eval:

def f(param1, param2):
    config = locals()
    print("1. config:")
    for k, v in config.items():
        print(f"- {k}: {v}")
        
    name = 1 
    eval("print()")
    
    print("2. config:")
    for k, v in config.items():
        print(f"- {k}: {v}")
    
f(1, 2)

再一次:

1. config:
- param2: 2
- param1: 1

2. config:
- param2: 2
- param1: 1
- name: 1
- v: 1
- k: param1
- config: {'param2': 2, 'param1': 1, 'name': 1, 'v': 1, 'k': 'param1', 'config': {...}}

在@juanpa的 comments 之后.arrivillaga,我得到了它,并且能够在没有eval()的情况下复制:

def f(param1, param2):
    config = locals()
    print("1. config:")
    for k, v in config.items():
        print(f"- {k}: {v}")
        
    name = "hello"
    locals()
    
    print("2. config:")
    for k, v in config.items():
        print(f"- {k}: {v}")
    
f(1, 2)

带输出:

1. config:
- param2: 2
- param1: 1
2. config:
- param2: 2
- param1: 1
- name: hello
- v: 1
- k: param1
- config: {'param2': 2, 'param1': 1, 'name': 'hello', 'v': 1, 'k': 'param1', 'config': {...}}

文件中说:

locals()更新并返回表示当前本地

我会用"…还字典…"而不是"a",但无论如何...

谢谢大家.


对于将来访问此页面的用户.在阅读了权威来源的示例后,我开始使用config=locals()来保存一个类似配置的字典,但该赋值并不"正确",并导致这些奇怪的行为,在复杂的代码中很难发现.

使用locals()保存配置字典的更好方法是config = {**locals()}:

def f(param1, param2):
    config = {**locals()}
    print("1. config:")
    for k, v in config.items():
        print(f"- {k}: {v}")
        
    name = "hello"
    locals()
    
    print("2. config:")
    for k, v in config.items():
        print(f"- {k}: {v}")
    
f(1, 2)

对于预期的正常行为,尤其是对于习惯于使用变量而不是名称的语言的人:

1. config:
- param2: 2
- param1: 1
2. config:
- param2: 2
- param1: 1

我同意公认答案的作者:文档应该更接近实际语义.

推荐答案

eval将隐式检索locals().从docs for eval:

如果省略locals字典,则默认为globals

然后从locals的文档中:

更新并返回表示当前本地符号表的字典.

因此,精确的行为没有得到很好的定义.但它似乎在重复使用同一个字典,并在每次调用时从实际的局部符号表中更新它.

因此:

>>> def foo():
...     l = locals()
...     print(l)
...     a = 42
...     print(l is locals())
...     print(l)
...     b = 11
...     print(l)
...
>>> foo()
{}
True
{'l': {...}, 'a': 42}
{'l': {...}, 'a': 42}
>>>

Python相关问答推荐

ruamel.yaml dump:如何阻止map标量值被移动到一个新的缩进行?

巨 Python :逆向猜谜游戏

一个telegram 机器人应该发送一个测验如何做?""

Seaborn散点图使用多个不同的标记而不是点

Django抛出重复的键值违反唯一约束错误

BeatuifulSoup从欧洲志愿者服务中获取数据和解析:一个从EU-Site收集机会的小铲子

高效地计算数字数组中三行上三个点之间的Angular

是否将Pandas 数据帧标题/标题以纯文本格式转换为字符串输出?

将标签与山脊线图对齐

使用Django标签显示信息

如何从具有完整层次数据的Pandas框架生成图形?

如何使用aiohttp获取图像并直接处理它而不保存它?

在Matplotlib中通过特定的Y值而不是 colored颜色 来改变alpha/opacity

Raspberry Pi Pico W、WebSockets和从中获取数据

新进程不会在运行FastApi的Docker中启动

[]和Expression API的区别是什么?

如何在Pandas 分组处理中执行多个功能

获取文本文件并创建CSV文件

通过np.interp的辅助x轴:如何更改xlims

将DF转换为特定的对象 struct