从Python的对象说起

一、Python对象

        学过Python的肯定都听过一句话:“一切皆对象”,那这句话是啥意思呢?

       在Python中,我们都知道数据类型有数字、字符串、元组、列表、字典、集合等,还包括函数、方法、类等,这些都是对象,甚至包括Python模块(py)等等都可以称为对象。

       object的概念在Python中非常常见,比如PyIntObject,PyStringObject,PyFunctionObject等等。那么对象有什么特点呢?

       对象并不一定是像我们理解的称类是对象,即含有属性和方法,Python对象不一定有属性和方法,但Python对象都可以赋值给变量,也可以当做参数传递给函数或类。作为Python对象,它们有三个特点:

  • 值 对象肯定都有一个value,比如2;
  • id 对象都会有一个ID,唯一标识自己,表示了在内存中的位置 id(2)可查看到;
  • 类型  对象都有一个特定的类型,可通过type(2)查看。

每一个对象都会有两个标准的头部信息:

  1. 类型标识符;标志对象的类型;
  2. 引用计数器;代表目前引用该对象的只恨数目,用来决定是否回收这个对象,这个和Python的垃圾回收机制有关,当计数为0时,就会回收。

 

二、赋值和引用

    说完对象,就要说下Python的赋值和引用了。先看一下下面的代码:

a = 3
a = 1.2
a = 'd'
a = [1,32]

    看到了吗?它没有声明变量,而且竟然赋了不同类型的值。这段代码在C语言中或者Java中是行不通的,而python中却可以这样灵活应用。这就和它的一切皆对象有关了。

    对于这句a = 3,Python主要做了以下几步:

  1. 创建一个对象来代表值 3;
  2. 创建一个变量a;
  3. 建立一个指针,从变量a指向对象3;

    变量名a——>对象3。

  看到了吧,在Python中,a = 3并不是像C语言中那样,“a就是代表整数类型,a和3拥有同样的内存地址”,python中的a只是一个变量,它和对象拥有不同的内存地址,当a=3执行时,只是创建了一个从变量到对象的一个连接。在内部,变量就是一个指向对象内存地址的指针。

  这个在Python中叫做“引用”,引用就是自动形成的从变量到对象的指针。而我们在谈论数据类型的时候,并不是针对变量而言,都是针对对象而言的。

再说下Python的共享引用。共享引用就是多个变量引用同一个对象。

a = 3
b = a

上面就是一个共享引用的例子,变量a和b都指向了对象3.你通过id查看,值是一样的。再看一个例子:

a = 3 
b = a

a = 1

print a,b 

>> 1,3

上面当执行a=1后,a指向了新的对象1,而b仍然指向对象3。再看一个:

a = [1,2]
b = a


a[0]=3

print a,b

>> [3,2]  [3,2]



a=[1,4]

print a,b

>> [1,4]  [3,2]

列表[1,2]是一个对象,但里面的元素也是对象,也就是说1也是对象。当我们改变a[0]时,就是改变了第一个元素要引用的对象。那么a指向的对象改变了,b的值也同样会改变;

如果你想改变a时,b的值不变,那么就需要使用拷贝,即创建对象的一个拷贝,和原来的对象拥有不同的内存空间。Python中可以用分片,copy或deepcopy。

再看下面的,当执行a=[1,4]时,实际上是又新创建了一个对象[1,4],这样a就指向了新的对象,b的值就不会改变;

共享引用和相等

先拿代码:

a=[1,2]
b=[1,2]

print a==b 

print a is b

>>True  False


a=1.1
b=1.1

print a is b

>>False




a=1
b=1
 
print a is b

>>True

上面的列表,浮点数是两个对象,is是比较是否引用不同的对象,因此有false;下面的整数却是True,说不通啊。其实这和Python机制有关,Python会缓存并复用小的整数或者字符串。

 

函数参数引用:

a ,b = [1,2], 4

def test(a,b):
    a[0] = 3
    b = 2
    print a,b

test(a,b)
print a,b


>>[3,2] 2

>>[3,2] 4

Python函数的传递只是传递对象的引用,而这种说法和其他语言的传值和传址说法侧重点不同。对象引用可能因为结果的不同表现为传值和传址的特点。

看上面的代码,变量a的值变了,变量b的值没变。这就是传址和传值两种表现形式。那为什么会出现这种情况呢?除了Python的对象和引用之外,还得说另外一个,Python的LEGB准则,函数结束后,生命周期也结束,详细可看Python官网。

a改变了,b没变。这是因为,函数内,改变了a所指的对象(列表),那么即使生命周期结束了,全局变量a还是指向同一个对象啊,所以这里的结果是a变了。

再看b,函数内重新赋值,函数的局部变量b指向了新的对象:2,等函数结束后,局部变量生命周期结束了,全局变量b还是指向变量b。

也就是说,对于可变对象,我们可以理解为传址;对于不可变对象,就是传址。但他们都是对象的引用。再解释一下传址和传值:传值传入的参数是不会改变的,用传址传入就会改变。

上面的例子应该明白了吧。

 

 

 

 

 

 

--------EOF---------
微信分享/微信扫码阅读