python动态赋值,创建对象,生成变量
in Python with 0 comment
python动态赋值,创建对象,生成变量
in Python with 0 comment

初衷

在使用pytqt5开发车间生产良率分析的脚本UI时,需要按照不同产品呈现不同的良率曲线。
每种产品类型对应UI中的一个QCheckbox,通过QCheckbox勾选状态确定是否显示该产品的良率曲线如果但由于产品类型多,并且随时会添加新类型,每个类型又要添加对应的界面按钮,比较麻烦。
尝试用一个list集合,根据list集合动态生成对应的QCheckbox。

实现

通过查询资料,exec()作为python的内置函数,能够执行储存在字符串或文件中的python语句,动态地执行复杂python代码。eval()函数只能执行计算数学表达式的结果功能。

使用通配符创建类实例并赋值

list1 = ['PMSG066', 'PMSG076', 'PMP110']
for i in list1:
    exec('step1_{} = TESTS({})'.format(i, i))

报错

  File "<string>", line 1, in <module>
NameError: name 'PMSG066' is not defined

报错原理未知,和TESTS(i)动态创建实例有关。

尝试配合locals()局部变量实现

示例:

def f():
    a = 0
    exec("a = 1")
    print(a)

def f2():
    a = 0
    exec("a = 1")
    b = locals()['a']
    print(b)

def f3():
    exec("a = 1")
    b = locals()['a']
    print(b)

f()
f2()
f3()

输出结果

0
0
1

默认情况下,exec() 会在调用者局部和全局范围内执行代码。然而在函数里面, 传递给 exec()的局部范围,__在感知上是拷贝局部变量组成的一个字典__,如果 exec() 如果执行了修改操作,这种修改后的结果对实际局部变量值是没有影响的。
原理:
在CPython的源代码中,变量并非存在一个字典中,而是一个线性类似数组的集合,每一个变量赋予一个index,按顺序放进数组f_localsplus中。调用locals()方式实际上是将这个数组转换为dict并且返回。
在exec()函数的实现中,最终调用的是PyRun_StringFlags()或者PyRun_String(),运行代码时会新建一个frame,也就是exec()内执行的所有代码,都是在另外一个frame中运行的。
比如在f()中,第二行a = 0和第三行exec("a = 1")是在不同的frame里面运行的,虽然共享了一个locals()的dict,但是保存这两个a的局部变量位置不一样。当运行“a = 1”时,并不会修改外面原f函数frame保存的局部变量a。
但在函数外由于变量存在于globals的字典当中,就可以实现。

a = 0
exec("a = 1")
print(a)

按照官方文档说明,我们应新建一个dict当作locals参数传入exec(),来获取对应的局部变量空间。

def f():
    a =0
    result = {}
    exec("a = 1", globals(), result)
    print(result)
    print(a)

输出结果

{'a': 1}
0

最终实现代码:

class TESTS():

    def __init__(self):
        pass

  
class F():

    def __init__(self):
        list1 = ['PMSG066', 'PMSG076', 'PMP110']
        result = {}
        for i in list1:
            exec('%s = TESTS()' %i, globals(), result)
            setattr(self, 'step3_'+i, result[i])


test = F()

参考资料:
[1] 【python】exec改不动局部变量?绝对触及你python知识区的盲点!_哔哩哔哩_bilibili

[2] 【笔记14】python动态创建对象(类实例)、动态赋值、动态生成变量_occamo的博客-CSDN博客

Responses