• 第6.6节 Python动态执行小结


    一、    Python动态执行支持通过输入数据流或文件传入Python源代码串,进行编译后执行,可以通过这种方式扩展Python程序的功能;
    二、    动态执行方法可能导致恶意攻击,因此使用时需要限定使用范围,注意安全风险;
    三、    如果采用先编译后动态执行的方式,注意编译模式必须与执行模式对应;
    四、    动态执行可以指定代码的执行空间,动态执行最好在指定的全局名字空间和局部名字空间中执行:
    1.    如果省略了可选参数,代码将在当前范围内执行;
    2.    如果提供了 globals 参数,就必须是字典类型,而且会被用作全局和局部变量的名字空间;
    3.    如果同时提供了 globals 和 locals 参数,它们分别被用作全局和局部变量的名字空间;
    4.    动态执行查找变量时的顺序依次为:局部名字空间->全局名字空间->调用exec或eval的代码执行环境的名字空间;
    5.    建议所有动态执行都指定单独的执行空间,以保证动态执行不会无意中损害调用方的环境。
    五、    在模块层级(通常是动态执行的调用方),全局和局部变量是相同的字典,内置 globals() 和 locals() 函数各自返回当前的全局和局部字典,因此可以将它们传递给 exec() 的第二个和第三个实参,但实际上与这两个参数不传参效果是一样的,因为不传值用的就是在当前调用方所在模块的空间执行,而这两个函数也是返回当前模块的空间,而在模块层级这两个函数返回的值是一样的。
    六、    补充知识
    1.    动态执行时,如果执行时指定了全局名字空间或局部名字空间,则可以在执行前通过字典键值访问值的模式给对应变量赋值;
    举例:
     
    这个例子中,要编译的表达式字符串为‘x+y’,但在编译代码中没有给x和y赋值,通过全局名字空间给x和y赋值的,计算时就取到了对应变量值。
    2.    在启动Python解释器之后,即使没有创建任何的变量或者函数,还是会有许多函数可以使用, 我们把这些函数称为内建常量和内建函数,是因为它们不需要我们程序员作任何定义,在启动Python解释器的时候,会首先加载内建名字空间,内建名字空间有许多名字到对象之间映射,而这些名字其实就是内建函数和常量的名称,对象就是这些内建函数和常量本身。这些名字空间由__builtins__模块中的名字构成;
    3.    __builtins__:对于动态执行时的全局名字空间字典,如果不包含 __builtins__ 键值,则将为该键插入对内建 builtins 模块字典的引用。因此,在将执行的代码传递给 exec() 之前,可以通过将自己的 __builtins__ 字典插入到 globals 中来控制可以使用哪些内置代码。本点老猿没有深入研究,暂时不展开说明。

    七、    例子
    1.    动态执行全局空间使用调用方的例子

    s='''
    person=['张三','李四']
    for p in person:
       print('name=',p)
    while(True):
        s=input("I will exit,are you ready(y/n)?")
        if s=='Y': break;
        if s=='y': break;
    '''
    exec(s,globals(),locals()) 
    


    此时执行的语句执行效果与exec(s)的执行效果完全相同,执行后s的值被输入改变。
    2.    用eval动态执行实现的cal函数
    第五章介绍的的cal函数的例子,如果使用动态执行方法的实现代码会简单许多,具体代码如下:

    def cal(number1,number2,*numbers,calmethod='+'):
       print('number1=',number1,',number2=',number2,',numbers=',numbers,', calmethod=',calmethod)
       calmethod=calmethod.strip()
       if len(calmethod)!=1: raise ValueError('运算符必须是+-*/中的一个,分别对应加、减、乘、除') 
       if '+-*/'.find(calmethod)==-1 :raise ValueError('运算符必须是+-*/中的一个,分别对应加、减、乘、除') 
       if not isinstance(number1,int): raise TypeError('第1个参数必须为整数') 
       if not isinstance(number2,int): raise TypeError('第2个参数必须为整数') 
       s=str(number1)+calmethod+str(number2)
       for i,n in enumerate(numbers):
           if  isinstance(n,int): #判断n是否为整数
               s+=calmethod+str(n)
           else:raise TypeError('第'+str(i+3)+'个参数必须为整数') 
       return eval(s)
    


    代码及执行样例截屏如下:
     
    上述代码的实现细节在此不再赘述,相信大家都能明白。
    3.    以exec定义的cal函数,所有代码全部存储在字符串codebuff中

    >>> codebuff='''
    def cal(number1,number2,*numbers,calmethod='+'):
       print('number1=',number1,',number2=',number2,',numbers=',numbers,', calmethod=',calmethod)
       calmethod=calmethod.strip()
       if len(calmethod)!=1: raise ValueError('运算符必须是+-*/中的一个,分别对应加、减、乘、除')
       if '+-*/'.find(calmethod)==-1 :raise ValueError('运算符必须是+-*/中的一个,分别对应加、减、乘、除')
       if not isinstance(number1,int): raise TypeError('第1个参数必须为整数')
       if not isinstance(number2,int): raise TypeError('第2个参数必须为整数')
       s=str(number1)+calmethod+str(number2)
       for i,n in enumerate(numbers):
           if  isinstance(n,int): #判断n是否为整数
               s+=calmethod+str(n)
           else:raise TypeError('第'+str(i+3)+'个参数必须为整数')
       return eval(s)
    '''
    >>> ns={}
    >>> exec(codebuff,ns)
    >>> ns['cal'](1,2,3,calmethod='+')
    number1= 1 ,number2= 2 ,numbers= (3,) , calmethod= +
    6
    >>> add=ns['cal']
    >>> add(1,2,3,calmethod='+')


    执行截图:
     
    上面这段代码是将第2个例子全部改成动态代码来实现,第一步将所有函数定义放到字符串codebuff缓冲区中,然后定义一个函数执行的名字空间ns,并通过exec执行函数定义的语句,在ns中就生成了函数定义,该函数可以通过名字空间ns以函数名作为键值获取,获取后可以直接执行函数调用,也可以赋值给一个变量add,再通过变量进行调用。

    本节对动态执行方法相关的内容进行了总结,并补充说明了传递的全局名字空间和本地名字空间相关的内容。
    老猿Python(https://blog.csdn.net/LaoYuanPython)系列文章用于逐步介绍老猿学习Python后总结的学习经验,这些经验有助于没有接触过Python的程序员可以很容易地进入Python的世界。
    欢迎大家批评指正,谢谢大家关注!

  • 相关阅读:
    envoy部分二: envoy的配置组件
    envoy部分一:enovy基础
    envoy部分四:envoy的静态配置
    envoy部分七:envoy的http流量管理基础
    envoy部分六:envoy的集群管理
    十七、Kubernetes的网络管理模型
    SQL 日期时间函数
    JSON 和 JavaScript eval
    Ajax 读取.ashx 返回404
    Repeat 数据为空时的处理
  • 原文地址:https://www.cnblogs.com/LaoYuanPython/p/11087713.html
Copyright © 2020-2023  润新知