MyModHeaven 发表于 2024-2-16 21:59

python中exec函数修改局部变量

> 前言:本文信息密度很低,是我边研究`exec`边写的

# 0. 摘要

本文内容如标题,办法在 4.2

# 1. 引子
代码中碰到一个问题,可简化为:
```python
def calculate(calculate_rule):
    account = 0
    for c in calculate_rule:
      exec('account = 2')
      print(account)
```
我以为函数在控制台的输出应该为2,但实际上是0。

# 2. 原因
薅光头发没找到原因,问了一下文心一言,竟然给出了答案:

[!(https://s11.ax1x.com/2024/02/16/pFG6Lzd.png)](https://imgse.com/i/pFG6Lzd)

在代码方面,以前也问过它,但总是没有满意答案,这次倒是让我意外

> 给出的解决办法是错的,后面会提到

# 3. 复现
> 以下部分均为个人推断,如果错误请指出,谢谢


如果我们没有指定`exec`中赋值的变量是全局变量还是局部变量,那么结果总是与我们希望的背道而驰。
## 3.1 exec想给全局变量赋值
### 3.1.1
```python
account = 0

exec(f'account = 2')
print(account)
```
> 输出2,毫无疑问


### 3.1.2
```python
account = 0
def f():
    exec(f'account = 2')
    print(account)
f()
```
> 输出0,因为`exec`语句中的`account`变量是局部变量,`print`语句中的是全局变量

### 3.1.3
```python
account = 0
def f():
    exec(f'account = 2', globals())
    print(account)
f()
```
> 输出2,因为`exec`和`print`语句中的`account`变量都是全局变量


## 3.2 exec想给局部变量赋值
### 3.2.1
```python
account = 0

exec(f'account = 2')
print(account)
```
> 输出2,毫无疑问

### 3.2.2
```python
def f():
    account = 0
    exec(f'account = 2')
    print(account)
f()
```
> 输出0,因为`print`语句中是局部变量,而`exec`语句中的`account`变量在全局变量和局部变量中都没有,不知道去哪了

### 3.2.3
```python
def f():
    account = 0
    exec(f'account = 2', globals(), locals())
    print(account)
f()
```
> 如文心一言所言,这时候输出的应该是局部变量`account`,值为2,但实际上输出的值还是0。所以它说错了


# 4. 纠错
## 4.1 自行修改
https://docs.python.org/zh-cn/3/library/functions.html#exec

看到官方文档,`exec(object, globals=None, locals=None, /, *, closure=None)`,`exec`有关键字参数,所以我改了一下,但是报错:

[!(https://s11.ax1x.com/2024/02/16/pFGRYmn.png)](https://imgse.com/i/pFGRYmn)

向文心一言发送报错内容,回答:

[!(https://s11.ax1x.com/2024/02/16/pFGWkNV.png)](https://imgse.com/i/pFGWkNV)

[!(https://s11.ax1x.com/2024/02/16/pFGWZ3F.png)](https://imgse.com/i/pFGWZ3F)

至此,又碰壁。

## 4.2 询问文心一言

我问`exec`语句中如何修改局部变量,给出如下回答:

[!(https://s11.ax1x.com/2024/02/16/pFGcRk8.png)](https://imgse.com/i/pFGcRk8)

这次给的办法确实有效:

```python
def f():
    locals_dict = {'account': 0}# 创建一个字典来存储局部变量
    exec('locals_dict["account"] = 2')# 修改字典中的值
    account = locals_dict['account']# 更新局部变量
    print(account)# 打印修改后的值

f()# 输出: 2
```


> 虽然其中还有一些疑问,而且也不知道文心一言说的还有没有错误,但问题终归是解决了

刘大富 发表于 2024-2-16 22:08

学习一下

Y0uD1 发表于 2024-2-16 23:13

谢谢博主

Handratty 发表于 2024-2-17 00:36

学习一下,太及时有用了....

sai609 发表于 2024-2-17 06:08

exec函数:是个啥

wmwgi84 发表于 2024-2-17 09:27

感谢分享,学习一下了!

kolt1911 发表于 2024-2-17 10:00

可是新建的字典是不是也算一个局部变量,为什么不会被修改

wapjsx 发表于 2024-2-17 16:35

奇了怪了,既然你已经有了:
locals_dict["account"] = 2

为什么还要exec 一下呢???原因何在?

MyModHeaven 发表于 2024-2-18 09:04

wapjsx 发表于 2024-2-17 16:35
奇了怪了,既然你已经有了:
locals_dict["account"] = 2


开头说了,这只是简化后的,实际的代码是这样的:
def calculate(calculate_rule):
    local_vars = {'account': 0}
    for c in calculate_rule:
      try:
            exec(f'local_vars["account"] {c}= source_data["{c}"]["{c}"]["account"]')
      except Exception as e:
            print(e)
    return local_vars["account"]

wapjsx 发表于 2024-2-18 09:13

MyModHeaven 发表于 2024-2-18 09:04
开头说了,这只是简化后的,实际的代码是这样的:
def calculate(calculate_r ...

{:1_921:}厉害厉害! 膜拜~~~

呵呵,感觉能解决实际问题就好!
页: [1]
查看完整版本: python中exec函数修改局部变量