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]