python是解释性语言!

代码块细述(必看)

代码块可以使得一段python代码作为一个单元、一个整体执行。以下是 官方手册 的描述。

A Python program is constructed from code blocks. A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition. Each command typed interactively is a block. A script file (a file given as standard input to the interpreter or specified as a command line argument to the interpreter) is a code block. A script command (a command specified on the interpreter command line with the ‘-c’ option) is a code block. The string argument passed to the built-in functions eval() and exec() is a code block.

所以,有以下几种类型的代码块:

  1. 模块文件是一个代码块
  2. 函数体是一个代码块
  3. class的定义是一个代码块
  4. 交互式(python idle)的每一个命令行都是一个独立的代码块
  5. 脚本文件是一个代码块
  6. 脚本命令是一个代码块(python -c "xxx")
  7. eval()和exec()中的内容也都有各自的代码块

代码块的作用是组织代码,同时意味着退出代码区块范围就退出了作用域范围。例如退出函数区块,就退出了函数的作用域,使得函数内的本地变量无法被函数的外界访问。

此外,python是解释性语言,读一行解释一行,这意味着每读一行就忘记前一行。但实际上更严格的说法是读一个代码块解释一个代码块,这意味着读代码块中的内容时,是暂时记住属于这个代码块中所读内容的,读完整个代码块后再以统筹的形式解释这个代码块。

先说明读一行解释一行的情况,也就是每一行都属于一个代码块,这个只能通过python的交互式工具idle工具来测试:

>>> x=2000
>>> y=2000
>>> x is y
False
>>> x=2000;y=2000
>>> x is y
True

理论上分号是语句的分隔符,并不会影响结果。但为什么第一个x is y为False,而第二个x is y为True?

首先分析第一个x is y。由于交互式工具idle中每一个命令都是一个单独的语句块,这使得解释完x=2000后立刻就忘记了2000这个数值对象,同时忘记的还有x变量本身。然后再读取解释y=2000,因为不记得刚才解释的x=2000,所以会在内存中重新创建一个数值结构用来保存2000这个数值,然后用y指向它。换句话说,x和y所指向的2000在内存中是不同的数据对象,所以x is y为False。

下面的x is y返回True:

>>> x=2000;y=2000
>>> x is y
True

因为python按行解释,一个命令是一个代码块。对于x=2000;y=2000,python首先读取这一整行,发现x和y的数值对象都是2000,于是做个简单优化,等价于x,y=2000,2000,这意味着它们属于一个代码块内,由于都是2000,所以只会在内存中创建一个数据对象,然后x和y都引用这个数据对象。所以,x is y返回True。

idle工具中每个命令都是独立的代码块,但是py文件却是一个完整的代码块,其内还可以嵌套其它代码块(如函数、exec()等)。所以,如果上面的分行赋值语句放在py文件中,得到的结果将是True。

例如:

扫描二维码关注公众号,回复: 11356392 查看本文章
x = 2000
y = 2000
print(x is y)   # True
def f1():
    z=2000
    z1=2000
    print(x is z)   # False
    print(z is z1)  # True

f1()

python先读取x=2000,并在内存中创建一个属于全局作用域的2000数据对象,再解释y=2000的时候,发现这个全局对象2000已经存在了(因为x和y同处于全局代码块内),所以不会再额外创建新的2000对象。这里反映出来的结果是"同一个代码块内,虽然仍然是读一行解释一行,但在退出这个代码块之前,不会忘记这个代码块中的内容,而且会统筹安排这个代码块"。

同理def f1()内的代码块,因为z是本地作用域的变量,更标准的是处于不同代码块内,所以会在本地作用域内存区创建新的数据对象2000,所以x is z返回False。根据前面的解释,z1 is z返回True。

再回顾前文多次出现的一个异常:

x = 3
def f1():
    print(x)
    x=4
f1()

报错信息:

UnboundLocalError: local variable 'x' referenced before assignment

当执行到def语句的时候,因为def声明函数,函数体是一个代码块,所以按照代码块的方式读取属于这个代码块中的内容。首先读取print(x),但并不会直接解释,而是会记住它,并继续向下读取,于是读取x=4,这意味着x是一个本地变量。然后统筹安排整个代码块,将print(x)的x认为是本地变量而非全局变量。注意,直到def退出的时候都还没有进行x的赋值,而是记录了本地变量x,赋值操作是在函数调用的时候进行的。当调用函数f()的时候,发现print(x)中的x是本地变量,但因为还没有赋值,所以报错。

但是再看下面的,为什么又返回True?

>>> x=256
>>> y=256
>>> x is y
True

因为Python在启动的时候就在内存中预先为常用的较小整数值(-5到256)创建好了对象,因为它们使用的非常频繁(有些在python的内部已经使用了)。所以,对于这个范围内的整数,都是直接引用,不会再在内存中额外创建新的数值对象,所以x is y总是返回true。甚至,这些小值整数可以跨作用域:

x = 3 
def f1():
    y=3
    print(x is y)   # True

f1()

再看前文循环内的函数的问题。

def f1():
    for i in range(5):
        def n():
            print(i)
    return n

f1()()

前面对现象已经解释过,内部函数n()中print(i)的i不会随循环的迭代而改变,而是固定的值i=4。

python首先解释def f1()的代码块,会记录属于这个代码块作用域内的变量i和n,但i和n都不会赋值,也就是说暂时并不知道变量n是一个函数变量。

同理,当需要解释def n()代码块的时候,将记住这个代码块涉及到的变量i,只不过这个变量i是属于外层函数的,但不管如何,这个代码块记住了i,且记住了它是外部函数作用域的。

注意,函数的声明过程中,所有涉及到变量i的作用域内都不会对i进行赋值,仅仅只是保存了这个i变量名,只有在调用函数的时候才会进行赋值操作

当开始调用f1()的时候,开始执行函数体中的代码,于是开始循环迭代,且多次声明函数n(),每一次迭代生成的n()都会让原先已记录的变量n指向这个新声明的函数体(相当于赋值的操作,只不过是变量n引用的对象是函数体结构,而不是一般的数据对象),由于只是在循环中声明函数n(),并没有进行调用,所以不会对n()中的i进行赋值操作。而且,每次循环迭代都会让变量n指向新的函数体,使得先前迭代过程中定义的函数被丢弃(覆盖),所以最终只记住了最后一轮循环时声明的函数n(),并且i=4。

当调用f1()()时,表示调用f1()中返回的函数n(),直到这个时候才会对n()内的i进行赋值,赋值时将搜索它的外层函数f1()作用域,发现这个作用域内的i指向内存中的数值4,于是最终输出4。

再看下面的代码:

def f1():
    for i in range(5):
        def n():
            print(i)
        n()
    return n

f1()

输出结果:

0
1
2
3
4

调用f1()的时候,执行循环的迭代,每次迭代时都会调用n(),意味着每次迭代都要对n()中的i进行赋值。

另外注意,前面说过,函数的默认参数是在函数声明时进行赋值的,所以下面的列表L中每个元素所代表的函数,它们的变量i都指向不同的数值对象。

def f1():
    L = []
    for i in range(5):
        def n(i=i):
            print(i)
        L.append(n)
    return L

f1()[0]()
f1()[1]()
f1()[2]()
f1()[3]()
f1()[4]()

执行结果:

0
1
2
3
4

猜你喜欢

转载自blog.csdn.net/Holden_Liu/article/details/104123661