今天在编程中遇到一个问题,思考半天,原来是因为对LEGB规则没有深入理解。
import pprint
def pprint(dic_things):
ehco = pprint.PrettyPrinter(width = 1 ,indent = 4)
echo.pprint(dic_things)
pprint({'a':'1','b':'2','c':'5'})
AttributeError: 'function' object has no attribute 'PrettyPrinter'
心中一阵懵逼,为啥引用不了呢。难道函数内不能使用import模块。分析一番,原来是要运用LEGB大法,这也是就python编译器的执行规则。
Python 使用 LEGB 的顺序来查找一个符号对应的对象
locals ->enclosing function -> globals -> builtins
那么我们来分析分析,这个错误吧。
Python 是动态语言,def 实际上是执行一条指令,用来创建函数(class 则是创建类的指令),而不仅仅是个语法关键字。函数并不是事先创建好的,而是执行到的时候才创建的。
Python语言函数代码的执行流程,为了保证函数的定义先于其首次调用时执行,我们需要知道中语句的执行顺序。
执行总是从程序的第一行代码开始的,从上到下,从左到右,按顺序依次执行第一条语句。
函数定义并不会改变程序的执行流程,但应该注意函数代码块中的语句并不是立即就执行的,而是等到函数被程序调用时才会执行。
函数调用可以看作程序执行流程中的一个迂回路径,遇到函数调用时,并不会直接继续执行下一条语句,而是跳到函数体的第一行,继续执行完函数代码块的所有语句,再跳回到原来离开的地方。
看似比较简单,一会你会发现,函数代码块中可以调用其他函数,当程序流程运行到一个函数之中时,可能需要执行其他函数中的语句。但当执行那个函数中的语句时,又可能再需要调用执行另一个函数的语句。
幸好Python对于它运行到哪里有很好的记录,所以在每个函数执行结束之后,程序都能跳回到它离开的那个地方,直到执行到整个程序的结尾,才会结束程序。
两个片段解释的很清楚,当我们执行到
pprint({'a':'1','b':'2','c':'5'})
python解释器首先在当前命名空间内寻找变量pprint,非常惊喜他找到了,一个函数名为pprint,
OK,放弃寻找并且执行这个函数(如此我们引入的指向模块的pprint变量就被忽略了),函数执行以后,在函数内部第一句
ehco = pprint.PrettyPrinter(width = 1 ,indent = 4)
我们的变量pprint这时已经指向了函数pprint,肯定就会报错啦,没有这个方法。
那么修改一下,这次执行成功了,分析一下吧。
import pprint
def pprint(dic_things,pprint = pprint):
pprint = pprint.PrettyPrinter(width = 1 ,indent = 4)
pprint.pprint(dic_things)
pprint({'a':'1','b':'2','c':'5'})
老套路,发现
pprint({'a':'1','b':'2','c':'5'})
在自己的命名空间找,发现特殊语句def pprint,很好执行这个函数,这里出现了我们解决问题的关键,pprint = pprint,这里这个给pprint赋值的pprint(好吧,只能这么绕口),为什么就是我们想要的那个身为moudule的pprint.呢?首先从缩进上判断,他缩进为主行缩进(就是没有缩进),也就是说他的命名空间和
pprint({'a':'1','b':'2','c':'5'})
一致,但是他又是与def pprint同行也就是同时创建,那么当解释器准备解释pprint = pprint这句话时,这个命名空间中并没有(function)pprint这个变量!!!怎么办,python解释器就会向外层寻找,这样就找到了我们的(module)pprint。
其实最好的办法还是,引入时就把名字改了
import pprint as pprint_module
这样就避免名字相同,我们还要用人脑去捋顺他的程序执行套路,不过也好,这个坑清晰了两个问题。
1.定义函数时,函数的参数的命名和函数体本身同级,并且他俩同时创建。
2.可以通过缩进来辅助确定一个变量的命名空间(不一定)
PYTHON的作用域由def、class、lambda等语句产生,if、try、for等语句并不会产生新的作用域。变量名引用分为三个作用域进行查找:首先是本地,然后是函数内(如果有的话),之后是全局,最后是内置。