Python学习笔记
Python的环境配置不再多说,成功配置完系统后,就可以在终端会话中运行python了。
一、运行python
1.进入python交互式环境
-
在“开始”菜单栏输入command,按回车,即可打开一个命令窗口
-
在终端窗口中输入python,若是安装成功,会出现符号:>>>
-
这样我们就进入了python的交互式环境,可以在这个窗口中运行python了,比如执行经典的输出"Hello Word"语句,或者是进行简单的运算:
2.退出交互式环境
每当要运行一个Python片段时,打开该窗口,进入该终端会话即可,而要退出该会话,两种方法:
- Ctrl+Z,再按回车
- 执行exit()命令
这里分别对两种方式进行了演示。
需要注意的是要对命令行模式和python交互模式进行区分。
- 在命令行模式下,可以执行
python
进入python交互环境,也可以运行一个.py
文件 - python交互环境不能运行
.py
文件
3.使用文本编辑器
(此后均使用vs code作为演示)
使用VS code编辑保存hello.py文件后,在cmd下运行该文件,操作步骤如下:
(1)确定当前目录,若不是hello.py保存的目录,则需要切换目录
(2)cmd下切换目录格式: cd 地址
-
特别注意,如果需要切换盘,那么命令应该改为:
cd/d 目标地址
如cd/d G:\python
(3)运行py文件
-
用文本编辑器写Python程序,然后保存为后缀为
.py
的文件,就可以用Python直接运行这个程序了。 -
Python的交互模式和直接运行
.py
文件有什么区别呢?-
直接输入
python
进入交互模式,相当于启动了Python解释器,但是等待你一行一行地输入源代码,每输入一行就执行一行。 -
直接运行
.py
文件相当于启动了Python解释器,然后一次性把.py
文件的源代码给执行了,是没有机会以交互的方式输入源代码的。
-
二、Python基础
1.输入和输出
(1)输出
使用函数print()
-
用
print()
在括号中加上字符串,就可以向屏幕上输出指定的文字。比如输出'hello, world'
-
print()
函数也可以接受多个字符串,用逗号“,”隔开,就可以连成一串输出 -
print()
函数也可以接受多个字符串,用逗号“,”隔开,就可以连成一串输出: -
print()
也可以打印整数,或者计算结果
print("Hello,World!")
print(100+200) #可以直接输出结果
#将上面的输出变得美观
print("100 + 200=",100+200) #这里区分一下和java的区别,java要使用+连接,但是python不用
#print函数会依次打印字符串,在遇到“,”的时候会自动输出一个空格,比如下面的例子
print("the brown fox","jumps over","the lazy dog")
(2)输入
使用函数 input()
- 该函数允许用户输入一个字符串存入,并且存放到一个变量
给出示例:
2.基础语法
-
Python使用缩进来组织代码块,请务必遵守约定俗成的习惯,坚持使用4个空格的缩进。
-
当一个语句以“:”结尾时,后面的缩进的语句被视为代码块。
-
以#开头的语句是注释
# "#"开头的语句是注释
a=100 #注意,在python中是没有分号的
if a>=0: #当语句以冒号结尾时,后面的代码被视为代码块
print(a)
else:
print(-a)
(1)数据类型
python能够直接处理的数据类型有:
-
整数
- python可以处理任意大小的整数(比C语言的优势)
- -800 100000 0等
- 也可使用16进制,如
0Xff000
- 对于很大的数,如10000000000,python允许在其中加上
-
用以分隔,比如写成:1000-000-000-000
,或者写成16进制0xa1b2_c3d4
-
浮点数
-
浮点数的小数位是可变的,比如 1.23 ∗ 1 0 5 1.23*10^5 1.23∗105和 12.3 ∗ 1 0 4 12.3*10^4 12.3∗104是完全等价的
-
浮点数可以用数学写法或者是科学计数法
-
但是如果是很大或者是很小的浮点数,就必须使用科学计数法,其中10用e代替
1.23e9
12.3e8
-
整数计算永远精确,浮点数计算可能会有四舍五入的误差,二者储存机制不同
-
-
字符串
-
字符串是以单引号’'或者双引号“”括起来的任意文本
- ‘abc’,“我是李华”
- ''和""只是一种表达,不是字符串的一部分
-
如果字符串内部既包含
'
又包含"
可以用转义字符\
来标识-
i\'m LiHua
-
转义字符
\
可以转义很多字符,比如\n
表示换行,\t
表示制表符,字符\
本身也要转义,所以\\
表示的字符就是\
-
如果有太多的要转义,那么可以使用
r''
表示''
内部的字符串默认不转义 -
如果字符串内部有很多换行,用
\n
写在一行里不好阅读,为了简化,Python允许用'''...'''
的格式表示多行内容
-
-
-
布尔值
-
布尔值只有
True
和False
两种,注意大小写写法和c++以及java区别 -
布尔值可以用
and
or
和not
连接,这一点也要和c++和java区别-
>>> True or True True >>> True or False True >>> False or False False >>> 5 > 3 or 1 > 3 True
-
>>> True and True True >>> True and False False >>> False and False False >>> 5 > 3 and 3 > 1 True
-
not就是非运算
>>> not True False >>> not False True >>> not 1 > 2 True
注意不等于不是not =而是!=
-
-
-
空值
空值是Python里一个特殊的值,用
None
表示。None
不能理解为0
,因为0
是有意义的,而None
是一个特殊的空值。类比C语言中的
NULL
-
变量
变量命名规则:变量名必须是大小写英文、数字和
_
的组合,且不能用数字开头区别java中可以用$命名
a = 123 # a是整数 print(a) a = 'ABC' # a变为字符串 print(a)
这种变量本身类型不固定的语言称之为***动态语言***,与之对应的是***静态语言***。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。例如Java是静态语言:
int a = 123; // a是整数类型变量 a = "ABC"; // 错误:不能把字符串赋给整型变量
python是动态语言(可以和matlab类比)
-
常量
在Python中,通常用全部大写的变量名表示常量。
PI=3.1415926
-
注意点
/
除法计算结果是浮点数,即使是两个整数恰好整除,结果也是浮点数,这里要与其他的语言区别一下
>>> 10 / 3
3.3333333333333335
/
除法计算结果是浮点数,即使是两个整数恰好整除,结果也是浮点数:
>>> 9 / 3
3.0
与众不同“地板除”
>>> 10 // 3
3
(2)字符编码(了解)
现代操作系统和大多数编程语言都直接支持Unicode。
ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节
-
字母
A
用ASCII编码是十进制的65
,二进制的01000001
; -
字符
0
用ASCII编码是十进制的48
,二进制的00110000
,注意字符'0'
和整数0
是不同的; -
汉字
中
已经超出了ASCII编码的范围,用Unicode编码是十进制的20013
,二进制的01001110 00101101
。把ASCII编码的A
用Unicode编码,只需要在前面补0就可以,因此,A
的Unicode编码是00000000 01000001
如果统一成Unicode编码,乱码问题从此消失了。但是,写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。
所以又有了可变长编码:UTF-8
UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间:
从上面的表格还可以发现,UTF-8编码有一个额外的好处,就是ASCII编码实际上可以被看成是UTF-8编码的一部分,所以,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码
用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:
(3)python的字符串
对于单个字符的编码,Python提供了ord()
函数获取字符的整数表示,chr()
函数把编码转换为对应的字符
①修改大小写
-
title()
-
upper()
-
lower()
name="scarlett johansson" #使用title()方法将首字母大写 print(name.title()) #输出为: #使用这种方法的好处是可以将ScarLett SCArlett等大小写不一的单词都视作一个单词 #即都视作:Scarlett Johansson #将字符串改为全大写 print(name.upper()) #SCARLETT JOHANSSON #将字符串改为全小写 print(name.lower()) #scarlett johansson
②合并字符串
python这点和java一样,都是使用+,即可连接两个字符串(print输出的时候注意是不一样的哦~)
first_name='Scarlett'
last_name='Johansson'
full_name=first_name+" "+last_name
print(full_name)
#输出结果为:Scarlett Johansson
③使用制表符或者换行符
这一部分其实在“1.输入与输出”部分已经详细描述过,不予赘述
④删除空白
python能够找到字符串的开头或者末尾的空格,并且使用方法strip()
去除这些空格,使用的时候要调用相应的字符串的strip()
方法。
- 去除左边空格
lstrip()
- 去除右边空格
rstrip()
- 两边都去除
strip()
#讲述去除字符串开头的空白
favorite_language=' java'
print(favorite_language)
print(favorite_language.lstrip())
#注意该字符串并不会因为调用这个方法而改变
print(favorite_language)
#要想永久的改变,要赋值结果给原变量
favorite_language=favorite_language.lstrip()
print(favorite_language)
#此时输出结果便是不带有空格的java了
(4)使用list和tuple
①list
list是一种有序集合,可以随时增添。
list里可以放“万物”:数字,字符串…元素之间允许不存在任何关系
感觉可以类比一下java中未使用泛型集合collection接口,被实现后(ArrayList,Linklist都是Collection的实现)可以存放任何Object的子类(基本数据类型会被自动装箱),且元素之间可以不存在任何的关系。
//Collection c=new Collection();//接口是抽象的,无法实例化
//多态
Collection c=new ArrayList();
//测试Collection类中的方法
c.add(1200);//自动装箱,实际上是放进去了一个对象的内存地址. Integer x=new Integer(1200
c.add(3.14);
c.add(new Object());
c.add(true);//自动装箱
c.add(new Student());
在Python中列表list使用[ ]表示,其中的元素使用“,”间隔。(并不需要像java那样需要指定类型然后new出来),并且可以直接调用Print()方法得到列表中的元素。(Java的话就必须使用迭代器或者for each了,并且取出来后直接打印是会调用该类的to string方法的,因为放进去的并不是对象本身,而是对象的地址,或者说是对象的引用)
bicycles = ['trek','cannondale','redline','specialized']
print(bicycles)
a.访问列表元素
list是有序的——直接使用位置索引即可访问list的任意元素
步骤:
- 指出list的名称
- 方括号里填索引
bicycles = ['trek','cannondale','redline','specialized']
print(bicycles)
print("bicycle列表中的第一个元素是",bicycles[0])
从上面的示例可以看出,使用列表名+所以访问列表元素时只返回该元素,并不含有方括号和引号
list元素也可以是另一个list,比如:
>>> s = ['python', 'java', ['asp', 'php'], 'scheme']
>>> len(s)
4
要注意s
只有4个元素,其中s[2]
又是一个list,如果拆开写就更容易理解了:
>>> p = ['asp', 'php']
>>> s = ['python', 'java', p, 'scheme']
要拿到'php'
可以写p[1]
或者s[2][1]
,因此s
可以看成是一个二维数组,类似的还有三维、四维……数组
b.获取list中的元素个数len()
len(listName)
c.索引的使用
-
索引从0开始,而不是从1开始
-
访问最后一个元素,可以使用
bicycle[len(bicycle)-1]
bicycle[-1]
bicycles = ['trek','cannondale','redline','specialized'] print(bicycles) print("bicycle列表中的第一个元素是",bicycles[0]) print(len(bicycles)) for i in range(0,len(bicycles)-1): print(bicycles[i]) print("列表的最后一个元素为",bicycles[-1])
-
以此类推,可以有-2 -3获取倒数第2,第三个元素
d.元素的修改、添加、删除
-
修改,直接利用索引进行修改,比如要修改第一个元素
bicycle[0]='jieFang'
-
添加
- 在末尾添加
listName.append()
方法追加- 因此,可以先建一个空表,不断使用appen函数进行追加,完善列表
- 在末尾添加
-
插入
- 插入元素的操作不需要像C语言那样手动编写代码去右移元素
- 直接使用这种写法:
listName.insert(int index,insert_Object)
-
删除
- 和插入类似,直接利用索引即可,使用的函数为
del listName[index]
- 注意这是一个语句,而不是一个函数
- 和插入类似,直接利用索引即可,使用的函数为
-
删除并且获得删除元素
-
pop方法(类比数据结构中自定义的stack类型)
-
popedOne=listName.pop()
-
弹出任意位置的元素并且获取该元素只需要在
pop()
括号中添加索引即可
-
-
根据值删除元素
- 当只知道元素的内容而不知道元素的索引时,使用
remove()
方法删除元素 listName.remove(object)
- 当然,括号里面可以是变量名,也可以就是本身
- 当只知道元素的内容而不知道元素的索引时,使用
这里只写出一个例子,综合上述的所有操作
#创建空列表
animals=[]
#元素的添加
animals.append("panda")
animals.append("cat")
animals.append("dog")
animals.append("giraf")
animals.append("ant")
animals.append("fox")
print("animals列表:",animals)
#元素的修改
animals[1]="pig"
#元素的插入
animals.insert(3,"snake")#注意是在索引处插入,不是第几个元素处插入
#元素的遍历
print("animals列表:",animals)
#元素的删除
A=animals.pop()
print("animals列表:",animals)
B=animals.pop(2)
print("animals列表:",animals)
print(B)
#使用del 语句删除
del animals[3]
print("animals列表:",animals)
animals.remove("ant")
print("animals列表:",animals)
输出结果:
animals列表: ['panda', 'cat', 'dog', 'giraf', 'ant', 'fox']
animals列表: ['panda', 'pig', 'dog', 'snake', 'giraf', 'ant', 'fox']
animals列表: ['panda', 'pig', 'dog', 'snake', 'giraf', 'ant']
animals列表: ['panda', 'pig', 'snake', 'giraf', 'ant']
dog
animals列表: ['panda', 'pig', 'snake', 'ant']
animals列表: ['panda', 'pig', 'snake']
②tuple
tuple和list不同之处在于tuple是不可变的有序表。
-
不可变一位置tuple元组在初始化后就不再能够修改,这也意味着它没有类似于list的append或者remove,pop之类的方法。其他的操作时基本一致的,我们仍然可以用索引的方式来获取元组中的元素。
-
list列表使用[ ]扩住元素,使用逗号来间隔元素;而tuple是使用( )来扩住元素的,元素间仍以逗号间隔。
-
不可变的tuple有什么意义?
因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple
#tuple在一开始初始化的时候就必须规定好元素,后面不可以修改
classmates=("Judy","Mike","LiHue","Lucy")
print(classmates)
for i in range(len(classmates)): #注意不能越界,越界报错:tuple index out of range
print(classmates[i])
print("tuple中的最后一个元素是:",classmates[len(classmates)-1])
#下面一个例子说明,要想tuple是不可变的,那么tuple中的每一个元素都是不可变的
constantTuple=("A","B",["李华","张明"])
print(constantTuple)
constantTuple[2][0]="张亮"
constantTuple[2][1]="胡文"
print(constantTuple)
输出的结果为:
Judy
Mike
LiHue
Lucy
tuple中的最后一个元素是: Lucy
('A', 'B', ['李华', '张明'])
('A', 'B', ['张亮', '胡文'])
最后我们解释tuple“可变”的原因:
表面上看,tuple的元素确实变了,但其实变的不是tuple的元素,而是list的元素。tuple一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a'
,就不能改成指向'b'
,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的
理解了“指向不变”后,要创建一个内容也不变的tuple怎么做?那就必须保证tuple的每一个元素本身也不能变。
(5)条件判断
if的条件判断主要是记住三点:
- 缩进
- elseif的写法为elif
- 冒号不要忘记
#条件判断,主要是知道语法,稍微记忆即可
age=10
if age>=18 : #注意一定不要忘记冒号
print("adult")
elif age<18 and age>0:
print("children")
else:
print("您的输入不合法")
#格式记住
格式总结:
if <条件判断1>:
<执行1>
elif <条件判断2>:
<执行2>
elif <条件判断3>:
<执行3>
else:
<执行4>
if的条件还可以简化,可类比C原因,只要if 后面跟着的是非零的数,那么执行,即:
if x:
print('True')
只要x≠0,那么判断就为True
,反之为False
开始写这个代码的时候,记住了if的格式后就开始了类似于C语言的操作:从操作台读取用户输入数据后进行判断。代码如下:
print("尊敬的用户,请输入您的年龄:")
age=input()
if age>=18 : #注意一定不要忘记冒号
print("adult")
elif age<18 and age>0:
print("children")
else:
print("您的输入不合法")
上述代码却出现了下面的报错:
TypeError: '>=' not supported between instances of 'str' and 'int'
这是因为input()
返回的数据类型是str
,str
不能直接和整数比较,必须先把str
转换成整数。Python提供了int()
函数来完成这件事情:
#input再解析
print("尊敬的用户,请输入您的姓名和年龄:")
string=input()
age=int(string) #使用int()函数,将string由字符串转化为整型
if age>=18 : #注意一定不要忘记冒号
print("adult")
elif age<18 and age>0:
print("children")
else:
print("您的输入不合法")
(6)循环
python的循环有两种,下面分别学习
①for…in循环
for…in循环依次把list或者tuple中的元素取出来
animals=["dog","cat","ant","pig"]
for animal in animals:
print(animal)
所以for x in ...
循环就是把每个元素代入变量x
,然后执行缩进块的语句。
#计算1-5的和
sum=0
for x in [1,2,3,4,5]:
sum = sum + x
print("和为",sum)
#计算1-100的和
#Python提供一个range()函数,可以生成一个整数序列,再通过list()函数可以转换为list
sum=0
print(list(range(101)))
for i in range(101):
sum=sum+i
print("1到100的和为",sum)
生成序列需要用到一个函数range()
,需要注意的是range(x)
是会生成一个从0开始小于x的序列,比如:
range(5)=[0,1,2,3,4]
②while循环
和c,java一样,满足while后面的条件即进入循环即可
③break语句
④continue语句
③④点都和C以及java等语法相同,不赘述
(7)字典dict
Python内置了字典:dict的支持,dict全称dictionary,在java中也称为map,使用键-值(key-value)存储,具有极快的查找速度。
dict是无序表。
使用{}括住元素,键值对之间以冒号:链接,元素之间以逗号,隔开
myFirstDict={"Mike":85,"LiHua":93,"Judy":56,"Amily":90}
这种key-value存储方式,在放进去的时候,必须根据key算出value的存放位置,这样,取的时候才能根据key直接拿到value
①新增一个元素
使用赋值语句:dict["XXX"]=xxx
即可
myFirstDict={"Mike":85,"LiHua":93,"Judy":56,"Amily":90}
#已知键key,寻值value
print(myFirstDict["Mike"])
#存放时还能够根据key放置value
myFirstDict["Judy"]=5
print(myFirstDict["Judy"])
#新增一个元素:
myFirstDict["Jack"]=66
print(myFirstDict["Jack"])
#如果没有这个元素会报错
print(myFirstDict["ZhangHao"])
需要注意的是:
-
由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值覆盖
-
要避免Key不存在,要先进行判断,方法有两种
-
"Thomas" in myFirstDict
,会返回True
或者False
-
通过dict提供的
get()
方法,如果key不存在,可以返回None
,或者自己指定的value-
>>> d.get('Thomas') >>> d.get('Thomas', -1) -1
-
-
-
返回None的时候python交互式环境不显示结果
gradeDict={"Mike":65,"LiHua":89,"Judy":44,"Peter":93} #避免key不存在的两种方法: #法①: xxx in xxx 即通过in判断 if "Amily" in gradeDict: print("Amily的成绩为",gradeDict) else: gradeDict["Amily"]=78 print("成功添加Amily信息") print(gradeDict) #法② get()方法 dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value print(gradeDict.get("Thomas",32)) print(gradeDict)
运行结果:
②删除一个元素
使用pop(key)
方法,对应的value也会从dict中删除
gradeDict={"Mike":65,"LiHua":89,"Judy":44,"Peter":93}
print(gradeDict)
A=gradeDict.pop("Mike")
#可以看出返回结果是value
print(A)
print(gradeDict)
运行结果:
{'Mike': 65, 'LiHua': 89, 'Judy': 44, 'Peter': 93}
65
{'LiHua': 89, 'Judy': 44, 'Peter': 93}
②list与dict比较
-
dict
- 查找和插入的速度极快,不会随着键值对的增多而变慢
- 占用大量空间,耗费内存大
-
list
- 查找和插入的时间随着元素的增加而增加
- 占用空间小,浪费内存很少
所以dict是用空间换取时间的一种方式,学过数据结构可以知道其实实现原理便是哈希表
dict可以用在需要高速查找的很多地方,在Python代码中几乎无处不在,正确使用dict非常重要,需要牢记的第一条就是dict的key必须是不可变对象。
这是因为dict根据key来计算value的存储位置,如果每次计算相同的key得出的结果不同,那dict内部就完全混乱了。这个通过key计算位置的算法称为哈希算法(Hash)。
(8)set
一样是无序表。set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key
特别的是,创建set的方式和dict不同,需要用一个list作为输入集合(难道是()【】{}都被用光了?)
animalsMove=["猫在走猫步","鸟儿在飞翔","大象喝水"]
myFirstSet=set(animalsMove)
print(animalsMove)
注意,显示的顺序并不表示set是有序的
①添加一个元素
使用add()
方法即可,可以重复添加同一个元素,但是不会有效果
animalsMove=["猫在走猫步","鸟儿在飞翔","大象喝水"]
myFirstSet=set(animalsMove)
print(animalsMove)
myFirstSet.add("hh在睡觉")
myFirstSet.add("hh在学习")
#重复添加没有效果
myFirstSet.add("hh在睡觉")
print(myFirstSet)
②删除元素
通过remove(key)
方法可以删除元素
myFirstSet.remove("hh在睡觉")
③集合操作
set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作
mySecondSet=set(["狗子打呼噜","猫在走猫步","鸟儿在飞翔","狐狸干坏事"])
print("两个集合求交集",myFirstSet&mySecondSet)
print("两个集合求并集",myFirstSet|mySecondSet)
输出结果:
两个集合求交集 {'猫在走猫步', '鸟儿在飞翔'}
两个集合求并集 {'狗子打呼噜', '鸟儿在飞翔', '大象喝水', '狐狸干坏事', '猫在走猫步', 'hh在学习'}
(9)再议不可变对象
str是不变对象,而list是可变对象。
对于可变对象,比如list,对list进行操作,list内部的内容是会变化的,比如:
>>> a = ['c', 'b', 'a']
>>> a.sort()
>>> a
['a', 'b', 'c']
而对于不可变对象,比如str,对str进行操作呢:
>>> a = 'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc'
虽然字符串有个replace()
方法,也确实变出了'Abc'
,但变量a
最后仍是'abc'
,应该怎么理解呢?
我们先把代码改成下面这样:
>>> a = 'abc'
>>> b = a.replace('a', 'A')
>>> b
'Abc'
>>> a
'abc'
要始终牢记的是,a
是变量,而'abc'
才是字符串对象!有些时候,我们经常说,对象a
的内容是'abc'
,但其实是指,a
本身是一个变量,它指向的对象的内容才是'abc'
:
┌───┐ ┌───────┐
│ a │─────────────────>│ 'abc' │
└───┘ └───────┘
当我们调用a.replace('a', 'A')
时,实际上调用方法replace
是作用在字符串对象'abc'
上的,而这个方法虽然名字叫replace
,但却没有改变字符串'abc'
的内容。相反,replace
方法创建了一个新字符串'Abc'
并返回,如果我们用变量b
指向该新字符串,就容易理解了,变量a
仍指向原有的字符串'abc'
,但变量b
却指向新字符串'Abc'
了:
┌───┐ ┌───────┐
│ a │─────────────────>│ 'abc' │
└───┘ └───────┘
┌───┐ ┌───────┐
│ b │─────────────────>│ 'Abc' │
└───┘ └───────┘
,对str进行操作呢:
>>> a = 'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc'
虽然字符串有个replace()
方法,也确实变出了'Abc'
,但变量a
最后仍是'abc'
,应该怎么理解呢?
我们先把代码改成下面这样:
>>> a = 'abc'
>>> b = a.replace('a', 'A')
>>> b
'Abc'
>>> a
'abc'
要始终牢记的是,a
是变量,而'abc'
才是字符串对象!有些时候,我们经常说,对象a
的内容是'abc'
,但其实是指,a
本身是一个变量,它指向的对象的内容才是'abc'
:
┌───┐ ┌───────┐
│ a │─────────────────>│ 'abc' │
└───┘ └───────┘
当我们调用a.replace('a', 'A')
时,实际上调用方法replace
是作用在字符串对象'abc'
上的,而这个方法虽然名字叫replace
,但却没有改变字符串'abc'
的内容。相反,replace
方法创建了一个新字符串'Abc'
并返回,如果我们用变量b
指向该新字符串,就容易理解了,变量a
仍指向原有的字符串'abc'
,但变量b
却指向新字符串'Abc'
了:
┌───┐ ┌───────┐
│ a │─────────────────>│ 'abc' │
└───┘ └───────┘
┌───┐ ┌───────┐
│ b │─────────────────>│ 'Abc' │
└───┘ └───────┘
所以,对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。