CPP与Python的作用域规则比较

这篇文章主要意图是借C++作用域规则引出Python的LEGB规则,同时带有一些我对于跨语言学习的想法。

首先提一个问题,作用域是什么?

标准解释为:限定一个名字的可用性代码范围就是这个名字的作用域。简单来说其实就是一个变量从创建到销毁的范围。

那么下一个问题?作用域有哪些?

大部分同学可能会说作用域分局部和全全局啊,局部优先局部,全局只能用全局。是的,从我们大部分人第一个学习的语言——C语言来说,的确就这两个。但是现如今的语言,就拿离C最近的C++来讲,作用域规则都是存在不同的。下面具体说明。

C++作用域

C++的作用域大致可分为下面几种,可能有疏漏,但大体如此:

1.函数原型作用域:

此作用域为c++程序中最小的作用域,生存周期最短。

例:int func(int i) i为参数,作用域类型为函数原型类型。

2.块作用域(局部作用域):与C语言类似;

函数体或某一个模块中变量,从声明开始,到块终止时结束,例如函数,循环内声明的变量

3.静态变量作用域(局部或全局)

4.类的作用域

类中的数据和函数分为公有类(public),保护类(protected),私有类(private)

具体的规则与面向对象设计有关,这里不多提

5.命名空间作用域

namespace 命名空间名{

各种声明(class的声明,函数的声明,变量的声明,常量的声明………)}

这个概念在C中是没有的。名字空间的提出是为了解决C模块间的同名变量冲突的问题。所以里面的变量也属于全局的,但是加上了限定名。引用方式:命名空间名::标识符名

一个小建议

下面在介绍python的LEGB规则的时候,我想先给看到这篇文章的同学一个建议,也是我之前犯过的一个错误。对于程序员来说,通常首先学的C语言然后学的C++,老师上课时可能时常作比较,让我们更好的上手学习。所以我们可能会有这样一个想法:语言都这么相近,多多对比也就学会了。其实我认为这个想法不全对甚至有的时候是很错误的!就拿C和最近最相似的C++来说,C为什么没有命名空间?C为什么没有函数重载?

C/C++这么相近的语言都有各种设计上的不同,那何况是动态比静态,编译比解释,强比弱。。

所以我建议大家不要完美主义。像一些语言设计上的不同最好不要太深入,能够快速切换到另一门语言的逻辑上才是最主要最应该掌握的技能!总结来说就是一句话:可以,但没有必要!

Python的LEGB规则

还记得之前说的C++的命名空间吗?其实命名空间这个概念是脱离于语言的,很多语言都有这个设计。

那么python中命名空间的规则是什么样的呢?

python中一切皆对象,所以python中的变量名是字符串来表示的。

例如:

1
>>> a = 10

表示建立字符串对象aNumber对象10之间的对应关系。由于这是一种映射关系,所以,可以使用键-值的形式来表示,即{name : object}
前面已经说过,命名空间是对变量名的分组划分,所以,Python的命名空间就是对许多键-值对的分组划分,即,键值对的集合,因此:

Python的命名空间是一个字典,字典内保存了变量名称与对象之间的映射关系

Tips:python中有两个内置函数locals()和globals()用于分别打印当前模块的局部和全局的命名空间(就是个字典)

下面列出python的作用域规则,简称为LEGB,很简单

LEGB含义解释:
L-Local(function);函数内的名字空间
E-Enclosing function locals;外部嵌套函数的名字空间(例如closure)
G-Global(module);函数定义所在模块(文件)的名字空间
B-Builtin(Python);Python内置模块的名字空间

LEGB就是用来规定命名空间查找顺序的规则

LEGB规定了查找一个名称的顺序为:local–>enclosing function locals–>global–>builtin

举个栗子来说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python
# encoding: utf-8

x = 1

def foo():
x = 2
def innerfoo():
x = 3
print 'locals ', x
innerfoo()
print 'enclosing function locals ', x

foo()
print 'global ', x

运行结果:

1
2
3
locals  3
enclosing function locals 2
global 1

下面将Python与C++的作用域规则对比理解试一下:

我个人认为python与C++在作用域上的最大区别就是块作用域的区别。python中是没有块作用域的,比如很多人在python中使用的第一个循环语句:

1
2
for i in range(10):
print(i)

大家是不是认为这类比C++或Java等语言的话就是下面这样:

1
2
3
for (int i=0; i<10; i++){
System.out.println(i);
}

的确这两个运行的结果是一样的,但是最大的区别是:java中的变量i在循环结束后是被销毁的,因为超出了自己的作用域。而python的话i还是在它所归属的函数或模块的作用域内,比如将语句做一个更改:

1
2
3
for i in range(10):
print(i)
print(i) # 结果是9

有人可能会问了,C++中有类作用域,命名空间作用域,而python只有模块作用域和内置模块作用域啊?

其实你可以把python中的函数+外部嵌套函数作用域应用到类作用域上是一样的,模块作用域其实可以类比全局作用域。而C++中的命名空间作用域在python中已经实现了,python不允许直接调用其他模块的名字空间,要加上文件名.变量才可以访问到,也就不存在不同模块间重名的问题了。

还有关于内置模块,python可以直接使用print(),help()这样的方法,而C++中并没有内置模块吧,而且C++中的内置变量是不能作为变量名的。

上面的对比除了块作用域的话,其他都有强答的嫌疑 2333… 所以说语言特性间的对比意义不大,只要能熟悉各个语言的特点,快速开发才是根本。

觉得好的话就打赏Ta一瓶冰阔落吧