[转载] Python的exec

参考链接: Python exec()

目录 

  

exec()简单使用 

动态执行简单的字符串代码 

动态执行较复杂的代码 

执行文件中的Python代码 

在exec中传参 

​ 

使用中遇到的问题 

参考 

 

exec()简单使用 

个人比较喜欢用Python里面的exec(),可以用来动态执行字符串代码,在for循环里面能快速执行大量类似于list1= 1,list2=2,list3=3..这样的语句,使代码显得更加简洁。 

首先简单说一说exec(),exec()是一个十分有趣且实用的内置函数,不同于eval()函数只能执行计算数学表达式的结果的功能,exec()能够动态地执行复杂的Python代码,功能强大但是也有不少小地方容易踩坑的,坑好出但是不太好理解,光影并存吧。 

动态执行简单的字符串代码 

 

动态执行较复杂的代码 

func = "def fact(n):\n\treturn 1 if n==1 else n*fact(n-1)"

exec(func)

a = fact(5)

print(a) 

 

执行文件中的Python代码 

在eg.txt中存储我们想放的Python代码 

def fact(n):

    if n==1:

        return 1

    else:

        return n*fact(n-1)

t = fact(6)

print(t) 

 

在exec中传参 

x = 10

expr = """

z = 30

sum = x + y + z

print(sum)

"""

 

def func():

    y = 20

    exec(expr)

    exec(expr, {'x': 1, 'y': 2})

    exec(expr, {'x': 1, 'y': 2}, {'y': 3, 'z': 4})

 

func() 

 

使用中遇到的问题 

今天在使用时遇到了一些问题简单记录一下。 

def main():

    file_list = [2014, 2045, 2065, 2070, 2080, 2110, 2123, 2133]

    generate_outliers_analysis_log(file_list, "log/outliers.log")

 

    for file in file_list:

        csv_df = csv_file_to_df(r"D:/FTPD/newEnv/" + str(file) + ".csv")

        port_suffix = [33, 35, 36, 37, 39, 40]

        loc = locals()

        for suffix in port_suffix:

            # 通过执行字符串代码来避免反复执行相同语句

            exec("sorted_df_%s =  get_sorted_port_df(csv_df, '25GE1/0/%s')" % (str(suffix), str(suffix)))

        sorted_df_33, sorted_df_35, sorted_df_36 = loc["sorted_df_33"], loc["sorted_df_35"], loc["sorted_df_36"]

        sorted_df_37, sorted_df_39, sorted_df_40 = loc["sorted_df_37"], loc["sorted_df_39"], loc["sorted_df_40"]

        sorted_df_list = [sorted_df_33, sorted_df_35, sorted_df_36, sorted_df_37, sorted_df_39, sorted_df_40]

        name_list = ["25GE1/0/33", "25GE1/0/35", "25GE1/0/36", "25GE1/0/37", "25GE1/0/39", "25GE1/0/40"]

        save_port_figure_and_excel(range(1, 7), sorted_df_list, name_list, str(file), 'port_csv/' + str(file) + '.xlsx') 

这一行代码在运行时遇到了报错: 

sorted_df_33, sorted_df_35, sorted_df_36 = loc["sorted_df_33"], loc["sorted_df_35"], loc["sorted_df_36"] 

报错信息如下: 

sorted_df_33, sorted_df_35, sorted_df_36 = loc["sorted_df_33"], loc["sorted_df_35"], loc["sorted_df_36"]KeyError: 'sorted_df_33'。 

一看就让人觉得奇怪,为了避免出现KeyError的问题,exec常常和locals()连用。 

首先,关于locals,个人认为值得注意的有四点: 

1.locals() 字典是局部命名空间的代理,它会采集局部作用域的变量,代码运行期若动态修改局部变量,只会影响该字典,并不会影响真正的局部作用域的变量。2.当再次调用 locals() 时(即两次调用locals()时),由于重新采集,则动态(exec())修改的内容会被丢弃,locals()会被刷新为不包含之前exec()执行后的kv对的字典。3.运行期的局部命名空间不可改变,这意味着 exec() 函数中的变量赋值不会对它产生影响,但 locals() 字典是可变的,会受到 exec() 函数的影响。4.locals()字典既然是局部命名空间(字典)的代理,会包含在当前局部作用域中的所有的局部变量,那么在把locals()的结果赋给一个变量时,就会产生循环引用。

第4点什么意思呢,举个简单的例子 

def test():

    a = 13

    loc = locals()

    exec('b = a + 1')

    b = loc['b']

    print(b) 

在上面这一小段代码中,当执行到loc = locals()这一行时,loc这一个字典会有一个key为‘loc’,值为loc这个字典本身的键值对。 

而且这个loc是一个循环引用,看一下下面的debug图就知道了。为什么呢,因为locals()会包含在当前局部作用域中的所有的局部变量。由于loc本身也是一个局部变量,所以就造成了循环引用。 

 

exec的常见陷阱 

https://segmentfault.com/a/1190000019217209 

对遇到的问题的分析 

看完上面的链接文章,个人觉得已经讲解的很透彻了。回头来简单看下,就是说对于下面的例一,会报一个KeyError,对于下面的例二,则不会报错。这与locals()的调用位置有关系,locals()是局部变量的字典的copy,运行期的局部命名空间(局部变量字典)不可改变,这意味着 exec() 函数中的变量赋值不会对它产生影响,但 locals() 字典是可变的,会受到 exec() 函数的影响。意味着我们如果希望在后面获取exec中动态执行的值来赋给新的变量的话,需要在exec之前调用locals(),否则无法获取。 

 

好,在此基础上我们来回顾今天遇到的问题,首先简化出现如上问题的业务代码为下图示例3的exec+locals+占位符的example3的使用方式,然后利用example4的方式来做一个简单的验证。咦,这里就出现了一个有趣的问题,按理来说,在example3()函数中,第2行定义了loc,这里的loc在第3行第4行执行完成后是会被exec修改的,即loc中是肯定有“a0”"a1"“a2”“a3”"a4"5个key的,这个在example4中也得到了验证,即只要不用原本的变量名就可以获取。我也不太明白为什么会产生这样的报错,也许这是exec和占位符的设计问题吧,由此也可以得出一个结论,在代码中要获取exec动态执行的变量值,建议还是不要重名,避免定位这些细小琐碎的问题花费较多的时间。: 

 

参考 

https://segmentfault.com/a/1190000014581721 

https://segmentfault.com/a/1190000019217209 

https://python3-cookbook.readthedocs.io/zh_CN/latest/c09/p23_executing_code_with_local_side_effects.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值