动态类型
在我们的Java、C这些语言中,我们在定义变量的时候,都会指定它的数据类型,但是在python中却没有这样的操作,因为python中的类型是在运行的过程中决定的,而不是同于预先的定义来声明的。
python中的变量名和对象是分开的,变量名永远没有任何关联的类型信息,类型只和对象关联。一个变量名当第一次被赋值时被创建,而当新的赋值表达式出现时,它会马上被当前引用的对象所替代。这就是python所谓的动态类型机制。这个变量名我们其实就是指向具体对象的一个引用。
a = 'abcde'
print(a)
a = [1,2,3,4,5]
print(a)
abcde
[1, 2, 3, 4, 5]
结合上面这个简单的例子,我们再来从头仔细理一理:
1、创建了一个字符串对象’abcde’,然后创建了一个变量a,将变量a和字符串对象 ‘abcde’相连接,
2、之后又创建了一个列表对象[1,2,3,4,5],然后又将他和a相连接。
这种从变量到对象的连接,我们称之为引用,以内存中的指针形式实现。因此直白的说,在内部,变量事实上是到对象内存空间的一个指针,而且指向的对象可以随着程序赋值语句而不断变化。
总结一下:变量名没有类型,只有对象才有类型,变量只是引用了不同类型的对象而已。每一个对象都包含了两个头部信息,一个是类型标志符,标识这个对象的类型,以及一个引用的计数器,用来表示这个对象被多少个变量名所引用,如果此时没有变量引用他,那么就可以回收这个对象。
共享引用
x = [1,2,3,4]
L = ['a',x,'b']
D = {'z' : 3,'y' : x}
print(L)
print(D)
x[3] = 10
print(L)
print(D)
x1 = x
x1[2] = 20
x[0] = 30
print(x)
print(x1)
['a', [1, 2, 3, 4], 'b']
{'z': 3, 'y': [1, 2, 3, 4]}
['a', [1, 2, 3, 10], 'b']
{'z': 3, 'y': [1, 2, 3, 10]}
[30, 2, 20, 10]
[30, 2, 20, 10]
对于上面这段代码与输出结果,当我们更改x或x1的任何一个值时,x1与x的输出都是一样的,那么说明这两个变量都是指向同一个对象,我们把这个叫做共享引用。前面我们说过变量就是对象的引用。
对于赋值操作来说,总是存储对象的引用,而不是这些对象的拷贝。所以我们在修改可变对象的时候也会影响到其他引用该对象的变量。说直白点就是对象一直只有那一个,引用有很多个,所以你更改对象的内容,大家都会收到影响。
那么我想获得对象独立的复制,该怎么做呢?
我们有以下方法,1、copy方法也能够实现完全复制;2、内置函数list可以生成拷贝。3、分片表达式能返回一个新的对象拷贝,没有限制条件的分片表达式能够完全复制列表。
X = [1,2,3,4,5,6]
c1 = X.copy()
c1[0] = 10
print(X)
print(c1)
c2 = list(X)
c2[0] = 20
print(X)
print(c2)
c3 = X[1:4]
c3[0] = 30
print(X)
print(c3)
c4 = X
c4[0] =40
print(X)
print(c4)
输出:
[1, 2, 3, 4, 5, 6]
[10, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6]
[20, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6]
[30, 3, 4]
[40, 2, 3, 4, 5, 6]
[40, 2, 3, 4, 5, 6]
赋值、浅拷贝和深拷贝
1、对于直接赋值,两个引用指向同一个对象,所以对象改变,赋值也会跟着变化。
2、浅拷贝,是不会拷贝子对象的(即共享子对象),原始数据改变,子对象也会跟着变化。但是直接引用所指对象不会变化,因为完成了赋值。例子中用到了切片和copy两种方式实现。
3、深拷贝,包含对象里面的子对象的拷贝,所以原始对象变化不会改成拷贝对象的变化。
import copy
X = [1,2,3,]
L = [1,2,3,4,X]
A = L
print(A)
A[1] = 100
A[4].append(4000)
print(A)
print(L)
print(X)
print()
L = [1,2,3,4,X]
B = L[:]
print(B)
B[1] = 200
B[4].append(1000)
print(B)
print(L)
print(X)
print()
L = [1,2,3,4,X]
C = copy.copy(L)
print(C)
C[1] = 300
C[4].append(2000)
print(C)
print(L)
print(X)
print()
L = [1,2,3,4,X]
D = copy.deepcopy(L)
print(D)
D[1] = 400
D[4].append(3000)
print(D)
print(L)
print(X)
print()
输出:
[1, 2, 3, 4, [1, 2, 3]]
[1, 100, 3, 4, [1, 2, 3, 4000]]
[1, 100, 3, 4, [1, 2, 3, 4000]]
[1, 2, 3, 4000]
[1, 2, 3, 4, [1, 2, 3, 4000]]
[1, 200, 3, 4, [1, 2, 3, 4000, 1000]]
[1, 2, 3, 4, [1, 2, 3, 4000, 1000]]
[1, 2, 3, 4000, 1000]
[1, 2, 3, 4, [1, 2, 3, 4000, 1000]]
[1, 300, 3, 4, [1, 2, 3, 4000, 1000, 2000]]
[1, 2, 3, 4, [1, 2, 3, 4000, 1000, 2000]]
[1, 2, 3, 4000, 1000, 2000]
[1, 2, 3, 4, [1, 2, 3, 4000, 1000, 2000]]
[1, 400, 3, 4, [1, 2, 3, 4000, 1000, 2000, 3000]]
[1, 2, 3, 4, [1, 2, 3, 4000, 1000, 2000]]
[1, 2, 3, 4000, 1000, 2000]
Python中的比较、相等性与真值的问题
L1 = [1,2,['A','B']]
L2 = [1,2,['A','B']]
L3 = L1
print(L1 == L2, L1 is L2)
print(L1 == L3, L1 is L3)
输出:
True False
True True
==是比较两个对象值的相等与否,即递归的比较所有的内嵌对象。
is是测试两个对象的一致性,比较的是两者的内存地址是否一样。
还有一点,python中将False、None、各种类型的数值0、空序列、空映射都被视为假,其他各种值都是真。