python的命名空间和对象

对象和命名空间是python学习的两大基石。

我们常常听到在python中,一切皆对象。是的,python所操作的数据,都是对象(是对象,就意味着他们有自己独有的方法或属性)。数字、字符串、函数、类,甚至是模块。对,模块也是对象。而在对象中,有一个很重要的角色,那就是命名空间。对象被加载到内存后,需要被调用或者说被引用,就需要有一个指针去指向这个对象,这个指针就是命名空间或者说名称空间。它严格来说只是一个指针或者说对一个对象的引用,你通过查找这个指针,就可以引用这个指针所指向的对象(内存地址中的代码片段)。

上面说数字、字符串、函数、类等都是对象,因此对象是可以被嵌套的,命名空间也是可以被嵌套的。每个被嵌套的命名空间,所引用的就是在这个空间里面的巴拉巴拉一串代码。代码在执行的时候,是以一个命名空间为单位进行执行的。也就是说,你在一个模块中下面的定义了很多类、函数、变量等,但是你在模块这一级别的命名空间中没有调用他们,那么python解释器在解释这个模块的时候,模块中的代码就不会被执行,python解释器只是解释了他们,就是说python解释器只是创建了一堆的命名空间而已;因此,如果被嵌套的命名空间中有错误的代码,也不会被报错(因为他们没被调用,他们只是被命名空间引用了而已),因为当执行到他们下一级嵌套的命名空间时候,python只是把那些一串巴拉巴拉代码赋值给了一个命名空间,而如果这个命名空间没有被python解释器解释的命名空间所调用,那么你可以在这个命名空间中肆意妄为。

 

看下面一段代码:

name = "wang"

def func():

    print(name)

    name = "panda"

 

print(name)

func()

 

上面的代码,如果不是在交互式命令模式下,一定是在某个模块下面定义的,假如是命名空间为__main__的模块中定义的,那么在这个模块级别的命名空间中,又嵌套定义了两个命名空间,name和func。python解释器在解释到func这个命名空间的时候,会把这个命名空间中的代码加载到内存,而不会执行(python解释器还没解释到func())。

接下来python解释器解释print(name),那么在模块级别的这个命名空间内有定义name,就会执行引用name命名空间中的代码片段,而这个代码片段只是一个"wang"这个字符串对象,就会直接把这个对象拿过来,然后打印到屏幕上。学了python面向对象比较深入的时候,你会发现之所以能够使用print打印,是因为这个对象本身有个__str__方法,所以打印出来的只是一串“wang”字符串,而不再是对象。

当python解释器解释到func()的时候,__main__命名空间就会去引用fun命名空间中的代码片段,而python在解释func命名空间的时候:

先解释“print(name)”,python就会搜索name这个命名空间,这里说下命名空间的搜索顺序:这里很重要,因为python代码的执行和调用,就是按照这个顺序执行和调用的

这个可以解释python变量在函数中的搜索顺序的本质,本质就是对命名空间的搜索顺序:

当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:

1、局部命名空间(local):特指当前函数或类的方法。如果函数定义了一个局部变量 x,或一个参数 x,Python 将使用它,然后停止搜索。

2、全局命名空间(global):特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python 将使用它然后停止搜索。

3、内置命名空间(build-in):对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。

4、如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name 'aa' is not defined。

因此,python搜索name这个命名空间,首先搜索func命名空间是否有定义,发现func命名空间中有定义,但是它是在print(name)被解释之前被定义的,因此这里报错。

 

 

变量赋值:

看下面代码:

a = 1

b = a

b = 2

print(a,b)

 

上面代码,定义了两个命名空间a和b,第一行定义了命名空间a,并将它指向数字对象1,第二行是将命名空间a赋值给命名空间b,这样命名空间b也同样指向了数字对象1,第三行,对命名空间b进行了重新赋值。注意:是重新赋值,因为数字1是一个不可变对象,b并没有一种方法可以改变数字1这个对象中的内容,因此这个等于号的意思是重新赋值。

因此,上面的打印结果是1 2

 

再看下面代码:

a = [1,2,3,4]

b = a

b[0]=8

print(a,b)

 

这段代码和上面的代码有个本质的不同,就是a和b所引用的对象是一个可变对象,调用a或b的方法,可以修改引用对象中的内容。因为a和b所引用的是同一个对象,因此不管是调用a或者b去修改这个对象中的内容,都会引起a和b引用对象的改变,因此打印结果是:[8, 2, 3, 4] [8, 2, 3, 4]

 

这样,我们再去理解python的浅拷贝或者深拷贝,就很容易了。如果一个深拷贝的对象里面嵌套了N层命名空间,可以想象会额外消耗更多的内存资源。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值