Python 的复杂数据类型包括组合数据类型和自定义数据类型。
组合数据类型有:str(字符串)、tuple (元组)、list(列表)、dict(字典)、set(集合)。
组合数据类型的名称本身也是函数的名称,可以用于类型转换。
例如:
L = list("abcd") #L 值为['a','b','c','d']
Python中的函数isinstance(x,y)用于判断x是不是y类型的数据。 此处y是类型的名称。
例如:
a = "1233"
print(isinstance(a,str)) #>>True
print(isinstance("123",int)) #>>False
b = [1,3] #b 是一个列表
print(isinstance(b,list)) #>>True
Python中的len函数用于求组合数据类型中元素的个数。
例如,求字符串长度,列表元素个数:
print(len("12345")) #>>5 求字符串长度
print(len([1,2,3,4]) #>>4 求列表长度
print(len((1,2,3))) #>>3 求元组长度
print(len({1,2,3})) #>>3 求集合元素个数
print(len({'tom':2,'jack':3})) #>>2 求字典元素个数
组合数据类型中的字符串和元组是不可以修改的。 列表、字典和集合可以修改。
自定义数据类型也叫“类” ,在本章最后一节讲述。
7.1 Python 变量的指针本质
Python 中所有的变量,都是指针。
所有可赋值的东西都是变量,因此都是指针。
列表的元素是可赋值的,因此列表的元素就是指针。
指针的本质是内存地址。
可以将指针理解为一个箭头,它指向内存单元中存放的数据。
变量是箭头,对变量进行赋值,就是将该箭头指向内存中的某处,而不是改写该箭头指向的地方的内容。
请注意:其他程序设计语言中的变量,未必是上述的情况。也有的教材称Python变量是“引用”,和这里说的指针含义相同。但在其他语言里,“引用”和“指针”未必是一个意思。
a = 3
b = 4
对变量赋值,就是让变量指向某处。上面两条赋值语句的效果,可以理解为图7.1.1。
a 是个指针,指向内存中某处存放的 3。b一样是指针,它指向 4。
用一个变量对另一个变量进行赋值,就是让两个变量指向相同的地方。因此,若再执行:a = b产生的效果如图 7.1.2 所示。
a指向了b指向的地方,所以a的值也变成4。我们说变量a的值是4,归根到底是在说:a 指向4。
Python 中有两个运算符,"is"和"==",含义有所不同,但有些类似。
a is b 为 True,说的是a和b指向同一个地方;而a==b为True,说的是a和b指向的地方的内容相同,但a和b未必指向同一个地方。
Python中有一个函数id(x),能求表达式x的id。id不能说是内存地址,但类似于内存地址。两个变量如果指向同一个地方,等价于它们的id相同。
例如:
#prg0440.py
a = [1,2,3,4] #列表a指向了[1,2,3,4]
b = [1,2,3,4] #列表b也指向了[1,2,3,4]
print(a is b) #False a和b指向的不是同一个列表[1,2,3,4]
print(a == b) #True a和b内容相同
c = a #c = [1,2,3,4]
print(a == c) #True
print(a is c) #True
a[2] = "ok" #列表a下标为2的元素修改为 ”ok" a = [1,2,"ok",4]
print(c)
print(b)
print(id(a) == id(c)) #True a和c指向的是同一块内存地址
print(id(a) == id(b)) #False a和b指向的不是同一块内存地址
上面程序执行完第5行时,效果如图7.1.3所示。
内存中有两份列表[1,2,3,4], a和b分别指向它们。因此a和b指向不同的地方,但是它们指向的地方存放的内容是一样的。故第3行输出True而第4行输出False。
第5行使得c与a指向同一份列表。因此第6、7行都输出True。
第8行修改了a[2],情况变为如图7.1.4所示。
因为c和a指向同一个地方,所以a的内容变了,c的内容自然也变。所以输出c,结果就是[1,2,'ok',4]。
常见错误:初学者经常会写出a=b=[]这样的语句,本意是形成a、b两个不同的空表。但实际上这么写的结果,a、b都指向同一张列表,往a里添加元素,就等于往b 里添加元素。
对于 int、float、complex、str、tuple 类型的变量 a 和 b,只需关注 a == b 是否成立,一般不需要关注a is b是否成立。因为这些数据本身都不会更改,不会产生a指向的内容变了,b 指向的内容也跟着变的情况。
对于list、 dict、set类型的变量a和b,a==b和a is b的结果都需要关注。因为这些数据本身会改变,有可能发生改变了a指向的内容, b指向的内容也会改变的情况。
本课程中说a和b相等,或a和b的值相等,意思是a==b成立。而a is b可能成立,也可能不成立,要看具体情况。
因为列表的元素可以被赋值,因此,列表的元素其实也是指针。
a = [1,2,3,4]
b = [1,2,3,4]
执行完上面这两条语句,准确的效果如图 7.1.5 所示。
a和b的每个元素,如a[0],b[1],都是指针。
a[0]和b[0]没有分别指向不同的两个1,是因为1 本身不可变,没有必要保有两份。若对a[0]进行赋值,那就是让a[0]指向别处,而不是将a[0]所指向的那个1改成别的内容。
所以,假如a[0]被赋成别的值, b[0]并不会受影响,它仍然指向1。
Python 函数的参数也是指针。
Python 函数的形参是实参的复制——即形参和实参指向同一个地方。
对形参赋值就是让形参指向别处,当然不会影响实参。
例如:
def Swap(x,y):
tmp = x
x = y
y = tmp
a,b = 4,5
Swap(a,b) #x = a,y = b
print(a,b) #>>4 5
进入Swap函数时,x等于a, y等于b。
Swap函数执行的过程中交换了x, y的值,但这并不会影响a和b。
在函数中的tmp=x刚执行完时,效果如图7.1.6所示。
函数Swap(x,y)执行完的效果,交换了x和y的内存指向:
此刻x, y分别是a和b的复制,即x和a同指向4, y和b同指向5。tmp=x使得tmp也指向4。
Swap函数执行完后,x和y的值交换了,本质上是说x和y交换了它们的指向,因此情况变成图 7.1.7 所示。
显然, a和b的指向不会发生任何变化,它们的值自然不变。
但是如果函数执行过程中,改变了形参所指向的地方的内容,则实参所指向的地方内容也会被改变。
例如:
#prg0450.py
def Swap(x,y):
tmp = x[0]
x[0] = y[0] #请注意若x,y是列表,则x[0],y[0]都是指针
y[0] = tmp
a = [4,5]
b = [6,7]
Swap(a,b) #进入函数后, x和a指向相同地方, y和b指向相同地方
print(a,b)
这个程序中, Swap(a,b)使得 a 和 b 的下标为 0 的元素发生了交换。
这是因为,x和 a指向同一张列表[4,5], y和b指向同一张列表[6,7]。因此x[0]就是a[0], y[0]就是b[0]。
进入Swap函数,执行完tmp=x[0]时,情况如图7.1.8所示。
Swap交换了x[0]和y[0],也就交换了a[0]和b[0]。因此该函数执行完时,情况如图7.1.9所示
由于a和x指向相同的地方,所以x[0]变了,a[0]自然也变。b和y的关系亦然。
函数的返回值也是指针。
假设函数中的返回语句是return x,如果x是变量,则返回值和x指向相同的地方;如果x是一个非变量的表达式,那么返回值指向这个表达式的计算后的值。
可赋值的东西都是指针,但是指针未必都可赋值。
例如函数的返回值,就是不可赋值的。比如f是个无参数的函数,f()的返回值就是指针。
a=f() 是用f的返回值对a进行赋值,使得a和f的返回值指向同一个地方。但f()=100这种写法是不可行的。