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作为演示)

python交互gdb python交互环境的退出命令_数据结构

使用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()

  • 该函数允许用户输入一个字符串存入,并且存放到一个变量

给出示例:

python交互gdb python交互环境的退出命令_数据结构_02

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
  • 浮点数
  • 浮点数的小数位是可变的,比如python交互gdb python交互环境的退出命令_字符串_03python交互gdb python交互环境的退出命令_python_04是完全等价的
  • 浮点数可以用数学写法或者是科学计数法
  • 但是如果是很大或者是很小的浮点数,就必须使用科学计数法,其中10用e代替
  • 1.23e9
  • 12.3e8
  • 整数计算永远精确,浮点数计算可能会有四舍五入的误差,二者储存机制不同
  • 字符串
  • 字符串是以单引号’'或者双引号“”括起来的任意文本
  • ‘abc’,“我是李华”
  • ''和""只是一种表达,不是字符串的一部分
  • 如果字符串内部既包含'又包含"可以用转义字符\来标识
  • i\'m LiHua
  • 转义字符\可以转义很多字符,比如\n表示换行,\t表示制表符,字符\本身也要转义,所以\\表示的字符就是\
  • 如果有太多的要转义,那么可以使用r''表示''内部的字符串默认不转义
  • 如果字符串内部有很多换行,用\n写在一行里不好阅读,为了简化,Python允许用'''...'''的格式表示多行内容
  • 布尔值
  • 布尔值只有TrueFalse两种,注意大小写写法和c++以及java区别
  • 布尔值可以用andornot连接,这一点也要和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编码就能节省空间:

python交互gdb python交互环境的退出命令_数据结构_05

从上面的表格还可以发现,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])

python交互gdb python交互环境的退出命令_python交互gdb_06

从上面的示例可以看出,使用列表名+所以访问列表元素时只返回该元素,并不含有方括号和引号

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()返回的数据类型是strstr不能直接和整数比较,必须先把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"])

python交互gdb python交互环境的退出命令_数据结构_07

需要注意的是:

  • 由于一个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)

运行结果:

python交互gdb python交互环境的退出命令_字符串_08

②删除一个元素

使用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' │
└───┘                  └───────┘

所以,对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。