zoukankan      html  css  js  c++  java
  • Python全栈开发-执行字符串形式的语句和字符串形式的表达式方法(即exec和eval方法)

        Python有时需要动态的创造Python代码,然后将其作为语句执行  或  作为表达式计算。

                  exec用于执行存储在字符串中的Python代码。

                 1、 语句与表达式的区别:表达式是 某事,语句是 做某事(即告诉计算机做什么)。

                  比如2*2是4,而print 2*2是打印4。上述两句代码在交互式解释器中执行的结果是一样的,是因为解释器总是把所有表达式的值打印出来而已。而在程序中编写类似2*2这样的表达式并不会打印显示什么,编写print 2*2则会打印4。

                  语句与表达式的区别在赋值时更明显,因为语句不是表达式,所以没有值。如在交互式解释器中输入 x=2则不会打印任何东西,立刻出现新的提示符。虽然什么也没显现,但是有些东西已经发生变化如x的值现在变为3.这也是语句特性的一般定义:它们改变了事物。比如,赋值语句改变了变量,print语句改变了屏幕显示的内容。

                 2、 命名空间(作用域) 全局变量和局部变量

                 除了全局作用域外,每个函数会都会创建一个新的作用域。变量分为全局变量和局部变量,函数内的变量称为局部变量只在局部命名空间中起作用。

                在函数内部读取全局变量一般来说不是问题,直接访问即可。但是,如果局部变量名或者参数的名字与全局变量名相同的话,就不能直接访问了,因为全局变量被局部变量给屏蔽了。如果确实需要的话,可以使用globals函数获取被屏蔽的全局变量值。(globals返回全局变量的字典,locals返回局部变量的值)。例如:有一个名为parameter的全局变量,那么在combine(parameter)函数内部访问全局变量时,因为与参数重名,必须使用globals()['parameter']获取。代码如下:

      

    def combine(parameter):
        print parameter+globals()['parameter']
    
    #函数调用
    parameter="hello"
    combine("berry")

               上面讲的是再函数内部读取全局变量的方法,不包括修改。如果要在函数内部修改全局变量,需要告知修改的值是全局变量,因为在函数内部将值赋予一个变量那么变量自动成为局部变量。通过global关键字来告诉Python函数内一个需要修改的变量是一个全局变量。代码如下:

    x=1
    def change_global(n):
        global x
        x=x+1

                   3、执行字符串的语句  exec

                   如输入exec "print  'hello'"会打印出hello。(注意:Python 3.0中,exec是一个函数不是一个语句了,因此使用exec('字符串语句')的方式来调用)。exec执行字符串语句存在安全风险,因为exec可能会干扰命名空间,即改变不应该变的变量。例如:

                   从上面的例子可以看出,exec干扰了命名空间,改变了sqrt的值,使其不是一个函数而变成1了。由此可见,如果对exec不加限制就会存在安全风险。下面是改进措施。

                   措施:通过增加 in <scope>来实现,其中的<scope>是一个字典,该字典起到放置代码字符串命名空间的作用。这样exec执行的代码只会在<scope>代表的命名空间中起作用。如:

                     从上面代码中可以看到,exec语句在scope命名空间中执行,不会影响到现在命名空间的sqrt。scope虽然充当命名空间的作用,但实质仍是一个字典,所以如果想知道scope命名空间中有多少变量时,可通过len(scope)获得,可通过scope.key()获得scope命名空间的所有变量。

                   4、eval 会计算字符串形式的Python表达式,并返回结果的值。

                   exec语句不会返回任何对象。而eval会返回表达式的值。下面的代码可以创建一个Python计算器:

    #Python计算器
    print eval(raw_input("Please input an arithmetic expression:"))

               上面代码解释,上面代码中eval内部现在还不是字符串,首先执行raw_input()函数,raw_input()返回你输入的求值字符串,现在eval函数内部就是求值字符串了,就可以用eval进行字符串的求值了。如输入:4*5+6,那么raw_input就会返回“4*5+6”,eval求值后为26.

            要注意上面代码与下面代码的区别:

    print eval('raw_input("Please input an arithmetic expression:")')

    在这个代码中,与Python计算器代码不同的是,eval函数内直接就是字符串,那么直接对字符串求值,但是字符串中是raw_input表达式,raw_input表达式将用户的输入转换为字符串,所以如果输入4+5的话会返回"4+5"。注意:raw_input('xxxxx')是一个表达式,表达式的值就是用户输入。  可能疑惑的是代码:exec('raw_input("Please input an arithmetic expression:")')不会报错,因为ecec也可以用于表达式,只是什么效果也没达到而已(既不返回值,也没干事情)。

             跟exec一样,eval也可以使用命名空间。因为尽管表达式一般不会给变量重新赋值,但是表达式可以通过调用函数来达到给全局变量赋值的目的。例如执行下面代码后,全局变量x的值会被重新赋值为2:

    x=1
    def inc_x():
        global x
        x=x+1
    eval("inc_x()")
    print x

           从上面的代码可以看出eval函数也是不安全的,必须使用命名空间。事实上,可以为eval提供两个命名空间,一个是全局的,另一个是局部的。全局的必须是字典,局部的可以是任何形式的映射。

    exec和eval的命名空间使用代码(命名空间可以不是空的字典,可以提前为命名空间提供一些值):

    复制代码
    scope={}
    scope['x']=1
    scope['y']=2
    print eval('x+y',scope)
     
    
    scope={}
    exec "x=2" in scope
    eval("x*x",scope)
    复制代码
  • 相关阅读:
    二叉树学习随笔
    结构体初始化中的数组赋值
    C和FORTRAN的快速傅里叶/余弦/正弦变换(Fast Fourier/Cosine/Sine Transform)开源库分享
    Java简单的数组用法尝试,和C语言很不一样
    中位数( 优先队列较优处理 )
    单调队列 数组写法qwq
    P1886 滑动窗口
    Sicily 2014. Dairy Queen
    P3385 【模板】负环
    [USACO06DEC]虫洞Wormholes (负环模板)
  • 原文地址:https://www.cnblogs.com/GavinSimons/p/9076037.html
Copyright © 2011-2022 走看看