python对象,变量,可变类型,不可变类型,函数传参

1:对象

python对象有三个属性:身份,类型和值

身份是唯一标识自己的东西,就像身份证号一样,不过这里可以理解成对象的内存地址;类型决定了对象可以保存什么类型的值,可以进行哪些操作等;值就是对象的数据项

2:变量

变量储存的是内存对象的引用,可以形象理解成指向某种类型对象的一个引用,指向对象,或者理解成一个对象的标签,名字;变量无类型

关于对象和变量

(1)a = 1

一个整型对象被创建,值为1;一个对象引用被创建,名为a;然后这个对象引用a引用的就是这个整型对象,可以理解为变量a指向了这个值为1的整型对象

这里的=也就是将对象引用和内存中的对象进行绑定

(2)a = 1 b = a

一个值为1的整型对象被创建,变量a指向了这个对象;创建一个新的对象引用,变量b,然后对象引用进行传递,变量a和b同时指向了这个整型对象

3:id,对象值

先来两个对比小例子

>>> a = 1
>>> b = 1
>>> id(a), id(b)
(24855352, 24855352)
>>> a is b
True
>>> a == b
True
>>> 
>>> a = 1000
>>> b = 1000
>>> id(a), id(b)
(25130920, 25130800)
>>> a is b
False
>>> a == b
True

id:获取对象的内存地址,而不是变量的id

a is b:变量a和b是否指向的是同一个对象

a == b:变量a和b指向的对象的内容是否相同

根据定义,显然

无论是1还是1000,a和b指向的内容都是相等的,也就是a == b为True

而两次创建的值为1的整型对象是同一个对象;但是值为1000的整型对象却是不同的对象,原因是python会缓存经常会用到的简单整型,我记得区间是[-1, 256],所以在这个区间内取值看上去好像会重新创建对象,实际上并没有重新创建,新的变量b还是指向了原来的值为1的对象

4:可变类型和不可变类型

继续小例子

>>> a = 1
>>> id(a)
36307768
>>> a = 2
>>> id(a)
36307744
>>> 
>>> b = [1]
>>> id(b)
140064897305848
>>> b[0] = 2
>>> b
[2]
>>> id(b)
140064897305848

变量a指向了值为1的整型对象,然后又指向了值为2的整型对象,但是显然他们不是同一个对象,也就是说当执行a=2时,变量丢掉了值为1的对象,而指向了值为2的对象

变量b指向了只有一个整型元素1的列表对象,然后修改了b指向的列表对象的第一个值,但是从id可以看出来b指向的列表对象并没有变化,还是之前那个列表

原因就是整型是不可变类型,对于a,内存中值为1的整型对象不可改变,因此才会重新创建一个值为2的整型对象,a重新指向;对于b,列表是可变类型,修改列表的元素值,而列表本身就是可变,所以根本不做改编,不需要重新创建列表对象

一般int,float,tuple,string都是不可变类型;list,dict是可变类型

5:函数调用,参数传递

LiHui@GodLike /cygdrive/e/py
$ cat foo.py
#!/usr/bin/env python

def foo(a):
    a = 1

b = 2
foo(b)
print b

LiHui@GodLike /cygdrive/e/py
$ ./foo.py
2

变量b指向值为2的整型对象,函数调用后,变量a也指向值为2的同一个整型对象,当执行函数内容a = 1的时候,由于整型为不可变类型,因此变量a抛弃了值为2的整型对象,重新指向了新的值为1的整型对象,而变量b依旧指向值为2的整型对象,因此最后print的结果为2

 

LiHui@GodLike /cygdrive/e/py
$ cat foo.py
#!/usr/bin/env python

def foo(a):
    a[0] = 1

b = [2]
foo(b)
print b

LiHui@GodLike /cygdrive/e/py
$ ./foo.py
[1]

变量b指向只有一个元素2的列表类型的对象,函数调用后a也指向这个对象,执行a[0] = 1时,由于列表是可变类型,因此修改列表元素,并没有重新创建一个列表对象,而是依旧在原来b和a同时指向的列表对象上进行操作,将元素2改成了1,所以最终b指向的列表对象元素也变成了1

 

LiHui@GodLike /cygdrive/e/py
$ cat foo.py
#!/usr/bin/env python

def foo(a):
    a = 3,4

b = 1,2
foo(b)
print b

LiHui@GodLike /cygdrive/e/py
$ ./foo.py
(1, 2)

这里用的是元组,跟整型是一个意思,不可变类型

6:例子

前面写的题目

LiHui@GodLike /cygdrive/e/py
$ cat for.py
#!/usr/bin/env python
# coding = utf-8

class Solution:
    # @param nums, a list of integer
    # @param k, num of steps
    # @return nothing, please modify the nums list in-place.
    def rotate(self, nums, k):
        k = k % len(nums)
        print id(nums)
        for i in range(0, k):
            nums[i] = nums[i] + len(nums) - k
        for i in range(k, len(nums)):
            nums[i] = nums[i] - k
        print id(nums)

if __name__ == '__main__':
    alist = [1, 2]
    print id(alist)
    lihui = Solution()
    lihui.rotate(alist, 1)
    print id(alist)
    print alist

LiHui@GodLike /cygdrive/e/py
$ ./for.py
7696579340408
7696579340408
7696579340408
7696579340408
[2, 1]

显然,这里两层for循环,是分别对列表的元素进行更改数据,对于可变类型来说,并没有重新创建列表对象,因此是实实际际进行了修改,所以alist进行了更改

 

LiHui@GodLike /cygdrive/e/py
$ cat hello.py
#!/usr/bin/env python
# coding = utf-8

class Solution:
    # @param nums, a list of integer
    # @param k, num of steps
    # @return nothing, please modify the nums list in-place.
    def rotate(self, nums, k):
        length = len(nums)
        k = k % length
        print id(nums)
        nums = nums[(length - k):] + nums[:(length - k)]
        print id(nums)

if __name__ == '__main__':
    alist = [1, 2]
    lihui = Solution()
    print id(alist)
    lihui.rotate(alist, 1)
    print id(alist)
    print alist
LiHui@GodLike /cygdrive/e/py
$ ./hello.py
7696579340408
7696579340408
7696579397680
7696579340408
[1, 2]

而将列表切片进行相加,此刻nums指向了一个新创建的列表对象,而alist还是指向原始的列表对象,因此alist的结果并没有改编

不信?看看下面就明白了

LiHui@GodLike /cygdrive/e/py
$ python
Python 2.7.8 (default, Jul 25 2014, 14:04:36)
[GCC 4.8.3] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [1, 2, 3, 4, 5]
>>> id(a)
7696579602552
>>> a = a[3:] + a[:3]
>>> a
[4, 5, 1, 2, 3]
>>> id(a)
7696579685264
>>> a[1] = 10
>>> id(a)
7696579685264
>>> a
[4, 10, 1, 2, 3]

发表回复