Python基础
前言
由于作者在下学期(大二下期)要学习
Python基础课程,所以就顺便在假期提前把基础知识学完了,参考的B站马士兵教学视频,特地整理出了一些基础知识点希望以后有什么不懂的地方还能再拿出来看一看
B站视频链接:马士兵Python入门基础版(基础语法)
我的博客网站,更新有点慢,共同见证我的学习历程:东坡的藏书屋
1. print打印
- 功能:向目的地输出内容
- 输出的内容:数字、字符串、表达式
- 目的地:LDLE(集成开发环境)、控制台、文件
2. 转义字符
2.1 什么是转义字符
就是反斜杠+想要实现的转义功能首字母
2.2 为什么需要转义字符
当字符串中包含反斜杠、单引号和双引号等有特殊用途的字符时,必须使用反斜杠对这些字符进行转义(转换一个含义)
- 反斜杠
\- 单引号
\‘- 双引号
\“
当字符串中包含换行、回车、水平制表符或者退格等无法直接表示的特殊字符时,也可以使用转义字符
- 换行 \n newline换行首字母
- 回车 \r 回车会把前面的覆盖掉
print('hello \r world'),这里的打印出world,回车会把hello覆盖- 水平制表符 \t 制表符从前到后开始数,四个为一个制表位
- 退格 \b 退一格,会把上一个字符退格删了
2.3 原字符
原字符:不希望字符串中的转义字符起作用,就使用原字符, 就是在字符串之前加上
R或者r注意事项:最后一个字符不能是反斜杠
print(r'hello\nworld')最后只有一个字符是反斜杠就会报错,如果是两个反斜杠就没有问题
print(r'hello\nworld') #则打印出来显示的为”hello\nworld“
3. 二进制和字符编码
8bit(位)=1byte(字节) 1024byte=1kb 1024kb=1mb 1024mb=1gb 1024gb=1t
机器语言只能识别0和1
基本的汉字和一些符号有ASII表
在中国有GB2312、GBK、GB18030等一些编码,在国外有其他国家的字符编码
但是Unicode几乎包含了全世界的字符、他就是UTF-8编码
4. 标识符和保留字
4.1 保留字
有一些单词被赋予了特定的意义,这些单词在给任何对象起名字的时候都不能用到
None break except in raise True class finally is return and continue for lambda try as def from nonlocal while assert del global not with async elif if or yield
- True、False
- and、or、not
- class
- is、in
- try、except、finally、raise
- if、elif、else
- def、global、nonlocal、return、yield
- for、while
- break、continue
- assert
- del
- import、from、 as
- with
- pass
- await、async
- lambda 1. True、False
False 布尔类型的值,表示假,与True相反 True 布尔类型的值,表示真,与False相反
2. and、or、not
逻辑运算符 与、或、非
3.class
定义类的关键字
4.is、in
Python中的对象包含三个要素:id,type,value 其中:
- id:用来唯一标识一个对象
- type:标识对象的类型
- value:是对象的值
is:就是用来判断a对象是否就是b对象,是通过id来判断的
==:判断的是a对象的值是否和b对象的值相等,是通过value来判断的
in:判断变量是否存在序列中
5.try、except、finally、raise
使用try...except...finally语句来处理异常。
- 通常的语句块放在try块中
- 错误处理的语句放在except块中
- 始终执行的语句放在finally块中
- raise 语句抛出一个指定的异常。
try:
#some code here
raise MyException
except MyException:
print("MyException encoutered")
finally:
print("Arrive finally")
x = 10
if x > 5:
raise Exception('x 不能大于 5。x 的值为: {}'.format(x))
6.if、elif、else
判断语句,if语句用来检验一个条件,如果条件为真,运行一块语句(称为if…块),否则处理另外一块语句(称为else…块)。else从句是可选的
7.def、global、nonlocal、return、yield
- def 定义函数的关键字
- global 定义全局变量
- nonlocal 在局部寻找外层函数中离他最近的那个变量
- return 函数中的返回值
- yield的用法有点像return,除了它返回的是一个生成器
def createGenerator():
mylist = range(3)
for i in mylist:
yield i*i
8.for、while
for循环可以遍历任何序列的项目,如一个列表或者一个字符串 while循环可以通关判断条件真假无限循环
9.break、continue
- break语句是用来终止循环语句,即使哪怕循环条件没有称为false或者序列还没有被完全递归,也会停止循环
- continue语句被用来跳过当前循环块中的剩余语句,继续进行下一轮循环
10.assert
assert 关键字用于在代码中设置检查点,用于当程序出现错误时中断程序执行。
>>> a = 3
>>> assert 0 < a < 10
>>> a = 20
>>> assert 0 < a <10
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
assert 0 < a <10
AssertionError
# assert 条件后可以增加错误信息解释
>>> a = 20
>>> assert 0 < a < 10, '超出范围!'
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
assert 0 < a < 10, '超出范围!'
AssertionError: 超出范围!
11.del
del删除变量或者序列的值
a = [-1,3,'aa',85] # 定义一个list
del a[0] # 删除第0个元素
del a[2:4] # 删除从第2个到第3个元素。
12.import、from、 as
用于导包,as为重命名
from turtle import fd as f
13.with
用于文件的读写
# 写文件
with open("test.txt", "wt") as out_file:
out_file.write("公众号:一行数据,没想到吧,这里有植入")
14.pass
pass空的类,函数,方法的占位符
15.await、async
async 用来声明一个函数为异步函数,异步函数的特点是能在函数执行过程中挂起,去执行其他异步函数,等到挂起条件(假设挂起条件是sleep(5))消失后,也就是5秒到了再回来执行。
await 用来用来声明程序挂起,比如异步程序执行到某一步时需要等待的时间很长,就将此挂起,去执行其他的异步程序。
async def test2(i):
r = await other_test(i)
print(i,r)
async def other_test(i):
r = requests.get(i)
print(i)
await asyncio.sleep(4)
print(time.time()-start)
return r
url = ["https://segmentfault.com/p/1210000013564725",
"https://www.jianshu.com/p/83badc8028bd",
"https://www.baidu.com/"]
loop = asyncio.get_event_loop()
task = [asyncio.ensure_future(test2(i)) for i in url]
start = time.time()
loop.run_until_complete(asyncio.wait(task))
endtime = time.time()-start
print(endtime)
loop.close()
16.lambda
通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是匿名函数
<函数名> = lambda <参数> : <表达式>
f = lambda x, y, z: x+y+z #f是表达式的名字
>>> f(1,2,3) #像函数一样调用
4.2 标识符
-
变量、函数、类、模块和其他对象的起的名字都叫标识符
-
规则
- 字母、数字、下划线_
- 不能以数字开头
- 不能是python保留字
- 严格区分大小写
通过程序可以将所有保留字打印出来
import keyword
print(keyword.kwlist)23
#输出结果
['False', 'None', 'True', '__peg_parser__', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
5. 变量
变量就是内存中一个带标签的盒子
变量由三部分组成
- 标识:表示对象所存储的内存地址,使用内置函数
id(obj)来获取 - 类型:表示的是对象的数据类型,使用内置函数
type(obj)来获取 - 值:表示对象所存储的具体数据,使用
print(obj)可以将值进行打印输出
name = "张三"
print("标识:", id(name))
print("类型:", type(name))
print("值", name)
#标识: 2222422933936
#类型: <class 'str'>
#值 张三
当多次赋值之后,变量名会指向新的空间
6. 数据类型
常用的数据类型
| 数据类型 | 表示 | 举例 |
|---|---|---|
| 整数类型 | int | 98 |
| 浮点数类型 | float | 3.14159 |
| 布尔类型 | bool | TURE、FALSE |
| 字符串类型 | str | 人生苦短,我用python |
6.1 整数类型
-
英文为integer,简写为int,可以表示为正数、负数和零
-
整数的不同进制表示方式
- 十进制—>默认的进制
- 二进制—>以0b开头
- 八进制—>以0o开头
- 十六进制—>以0x开头
6.2 浮点类型
-
浮点数由整数部分和小数部分组成
-
浮点数存储不精确性
-
使用浮点数进行计算时,可能会出现小数位数不确定的情况
print(1.1+2.2) #3.3000000000000003 print(1.1+2.1) #3.2 -
解决方案
-
导入模块decimal
from decimal import Decimal print(Decimal('1.1')+Decimal('2.2'))
-
-
6.3 布尔类型
-
用来表示真或假的值
-
True表示真,False表示假
-
布尔值可以转化为整数
- True—>1
- False—>0
print(True+1) #2 print(False+1) #1
6.4 字符串类型
- 字符串又被称为不可变的字符序列
- 可以使用单引号、双引号、三引号来定义
- 单引号和双引号定义的字符串必须在一行
- 三引号定义的字符串可以分布在连续的多行
str1 = '人生苦短,我用python'
str2 = "人生苦短,我用python"
str3 = '''人生苦短
我用python'''
print(str1)
print(str2)
print(str3)
#人生苦短,我用python
#人生苦短,我用python
#人生苦短
#我用python
6.5 数据类型转换
将不同数据类型的数据拼接在一起
| 函数名 | 作用 | 注意事项 | 举例 |
|---|---|---|---|
str() | 将其他数据类型转换成字符串 | 也可用于引号转换 | str(123)—>'123' |
int() | 将其他数据类型转换成整数 | 1.文字类和小数类字符串,无法转换成整数 2.浮点数转换成整数,抹零取整 | int('123') int(9.8) |
float() | 将其他数据类型转换成浮点数 | 1.文字类无法转成整数 2.整数转成浮点数,末尾为.0 | float('9.9') float(9) |
7. 注释
-
在代码中对代码的功能进行解释说明的标注性文字,可以提高代码的可读性
-
注释的内容会被Python解释器忽略
-
通常包括三种类型的注释
- 单行注释——>以
#开头,直到换行结束 - 多行注释——>将一对三括号(
‘’‘或者""")注释 - 中文编码声明注释——>在文件开头加上中文声明注释,用以指明编码文件的编码格式
- 单行注释——>以
8. input函数
作用:接收来自用户的输入
返回值类型:输入值的类型为
str值的存储:使用
=对输入的值进行存储
#输入函数input
present=input('大圣想要什么礼物呢?')
print(present,type(present))
#大圣想要什么礼物呢?定海神针
#定海神针 <class 'str'>
# 从键盘录入两个整数,计算两个整数的和
a = input('请输入一个加数')
a = int(a) # 将转换之后的结果存储到a中
b = input('请输入另一个加数')
b = int(b)
print(a+b)
# 也可以直接a = int(input('请输入一个加数'))
9. 常用运算符
9.1 算术运算符
-
标准算术运算符 加(
+)、减(-)、乘(*)、除(/)、整除(//)取整(一正一负向下取整) 9//-4或者-9//4的答案都是-3,由-2向下取整为-3 -
取余运算符
%取余(一正一负要公式) 余数=被除数-除数*商9%-4结果为-3 9-(-4)*(-3)=9-12=-3-9%4结果为3 -9-(4)*(-3)=-9+12=3 -
幂运算符
**
9.2 赋值运算符
执行顺序:从右到左
支持链式赋值
a=b=c=20支持参数赋值
+=、-=、*=、/=、//=、%=支持系列解包赋值
a,b,c=20,30,40
9.3 比较运算符
>、<、>=、<=、!=返回值为True、False
==对象value的比较
is、is not对象的id的比较
其中,一个 = 称为赋值运算符,== 称为比较运算符
一个变量由三部分组成,标识,类型,值
== 比较的是值还是标识呢? 比较的是值
比较对象的标识使用 is 和 is not
下面由两个例子供参考
a = 10
b = 10
print(a == b) # True 说明,a与b的value 相等
print(a is b) # True 说明,a与b的id标识 相等
lst1 = [11, 22, 33, 44]
lst2 = [11, 22, 33, 44]
print(lst1 == lst2) # value -->True
print(lst1 is lst2) # id -->False
print(id(lst1))
print(id(lst2))
'''
输出结果
True
False
1351505079360
1351504973504
'''
9.4 布尔运算符
布尔运算符主要是对于布尔值之间的运算
包括and 、or 、not 、in 、not in
-
运算符
and当两个运算数都为True时,运算结果才为True -
运算符
or只要有一个运算数为True,运算结果就为True -
运算符
not如果运算数为True,运算结果为False,反之 -
运算符
in表示在不在里面,下面用例子演示一下-
s = 'helloworld' print('w' in s) # True,表示字符w在变量s里面出现 print('k' in s) # Flase,表示字符k不在变量s里面出现
-
9.5 位运算符
在做位运算的操作时候一定要先把数据转换成二进制再进行计算
| 位运算符 | 操作 |
|---|---|
位与 & | 对应数位都是1,结果数位才是1,否则是0 |
位或 竖线 | 对应数位都是0,结果数位才是0,否则为1 |
左移运算符<< | 高位溢出舍弃,低位补0 |
右移运算符>> | 低位溢出舍弃,高位补0 |
9.6 运算符的优先级
优先级依次为:
**幂运算符号
*,/,//,%乘除、取余
+,-加减算术运算:先算乘除,后算加减,有幂运算,先算幂运算
<<,>>左移位、右移位运算符
&位与
|位或位运算:先算移位,再算位与和位或
>,<,>=,<=,==,!=大于小于、比较比较运算:结果集为True、False
and布尔运算符
or布尔运算符布尔运算:对上一步结果集进行布尔运算
=最后再是赋值运算符
10 程序的组织结构
程序的结构一般都分为顺序结构、选择结构和循环结构,此处和本人学过的C语言、C++语言以及Java语言相识,都是同为三种基本结构
10.1 顺序结构
顺序结构,顾名思义,就是按照一定的顺序来执行语言程序,在python中,就是按照自上到下的顺序来执行程序,又比如在赋值运算中就是从右到左,按着一定的优先级的顺序来执行赋值运算。
10.2 对象的布尔值
在进行下面两种结构总结之前,应该先介绍一下对象的布尔值
-
Python一切皆对象,所有对象都有一个布尔值
- 获取对象的布尔值,就要使用到内置函数bool()
-
以下对象的布尔值为False
- False
- 数值0
- None
- 空字符串
- 空列表
[]、list() - 空元组
()、tuple() - 空字典
{}、dict() - 空集合
set()
10.3 选择结构
选择结构,就是程序根据判断条件的布尔值选择性地执行部分代码,明确的让计算机在什么条件下,该去做什么
语法结构通常有单分支结构、双分支结构、多分支结构、嵌套if结构
语法结构:
#单分支结构
if 条件表达式:
条件执行体1
条件执行体2
#双分支结构
if 条件表达式:
条件执行体1
else:
条件执行体2
#多分支结构
if 条件表达式1:
条件执行体1
elif 条件表达式2:
条件执行体3
elif 条件表达式N:
条件执行体N
else:
条件执行体N+1
#嵌套if结构
if 条件表达式1:
if 内层条件表达式:
内层条件执行体1
else:
内层条件执行体2
else:
条件执行体
选择结构中可以简写成条件表达式if....else,:【条件为真的结果】if 条件 else 【条件为假的结果】
下面以一个例子来参考一下用法
#比较大小
if num_a>=num_b:
print(num_a,'大于等于',num_b)
else:
print(num_a,'小于',num_b)
#使用条件表达式进行比较
print( (num_a,'大于等于',num_b) if num_a>=num_b else (num_a,'小于',num_b))
#或者将打印出来的东西化简
print( str(num_a)+'大于等于'+str(num_b) if num_a>=num_b else str(num_a)+'小于'+str(num_b) )
此外,在Python中还有一点与其他语言不相同之处
在C与Java中,表示两个数之间的范围通常只能用score > 80 && score < 90
但是在Python中,表示两个数之间的范围可以用两种表达方式score > 80 and score < 90或者直接80 < score < 90,此处的&&也换成了Python中布尔运算符的and,并且可以直接用数学中连等的方式来表示
10.4 pass语句
pass语句:语句什么都不做,只是一个占位符,用在语法上需要语句的地方
pass语句可以用在:先搭建语法结构,还没想好代码怎么写的时候
关于什么语法可以一起使用:
- if语句的条件执行体
- for-in语句的循环体
- 定义函数时的函数体
下面是使用方式,以if语句作为例子参考学习
if a>b:
pass
else:
pass
#此时的代码是没有报错的,但是也不会执行出结果,有种类似于只为了不报错也不想看到出格的结果的感觉
10.5 range()函数
在总结循环结构之前还是需要先写一下range()函数的用法,因为可能后面的for循环会经常用到这个函数
range()函数:就是用来生成一个整数序列
-
创建
range对象有三种方式,后面会有相关代码例子介绍range(stop)range(start,stop)range(start,stop,step)
-
range()函数的返回值是一个迭代器对象 -
range类型的优点
-
不管
range对象表示的整数序列有多长,所有range对象占用的内存空间都是相同的,因为仅仅需要存储start,stop和step,只有当用到range对象时,才会去计算序列中的相关元素 -
in与not in可以判断整数序列中是否存在(不存在)指定的整数
下面就一个例子,简单描述一下range()函数的一些创建方式
# rang()的三种创建方式
"""第一种创建方式:只有一个参数(小括号里面只给了一个数字)"""
r = range(10) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],默认从0开始,默认相差1称为步长
print(r) # range(0, 10)
print(list(r)) # 用于查看range对象种的整数序列 ——>list是列表的意思
"""第二种创建方式:给了两个参数(小括号里面给了两个数字)"""
r = range(1, 10) # 指定了起始值,从1开始,到10结束(不包括10),默认步长为1
print(list(r)) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
"""第三种创建方式:给了三个参数(小括号里面给了三个数字)"""
r = range(1, 10, 2) # 指定了起始值,从1开始,到10结束(不包括10),指定了步长为2
print(list(r)) # [1, 3, 5, 7, 9]
"""判断指出的整数 再序列中是否存在 用in, not in"""
print(10 in r) # False, 10不在当前的r这个整数序列 中
print(9 in r) # True, 9在当前的r这个序列中
10.6 循环结构
循环结构就是指反复做一件事情
循环结构一般有while、for -in
选择结构的if与循环结构while的区别
if是判断一次,条件为True执行一次while是判断N+1次,条件为True执行N次
while的语法结构
while 条件表达式:
条件执行体(循环体)
while的一个万金油例子,计算1-100之间的偶数和
a = 0
sum = 0
while a <= 100:
if a % 2 == 0:
sum += a
a += 1
print('1-100之间的偶数和为', sum)
#输出结果:1-100之间的偶数和为 2550
for-in循环
in表达从(字符串、序列等)中依次取值,又称为遍历for-in遍历的对象必须是可迭代对象
for-in的语法结构
for 自定义的变量 in 可迭代对象:
循环体
另外,循环体内不需要访问自定义变量,可以将自定义变量替代为下划线
下面以几个代码例子演示一下for-in的用法
for item in 'Python': # 第一次取出来的是P,将P赋值给item,将item的值输出
print(item)
# 输出结果依次为 P y t h o n
# range()产生一个整数序列 -->也是一个可迭代对象
for i in range(10):
print(i)
# 输出结果依次为 0 1 2 3 4 5 6 7 8 9
# 如果在循环体中不需要使用到自定义变量,可将自定义变量写成”_“
for _ in range(5):
print('人生苦短,我用Python')
# 输出结果为循环五次 人生苦短,我用Python
10.7 流程控制语句
break语句:用于结束循环结构,通常与分支结构if一起使用
continue语句:用于结束当前循环,进入下一次循环,通常与分支结构中的if一起使用
通常在多重循环中的
break和continue只用于控制本层的循环
此处提到的控制语句break和continue其实与C语言中的作业一样,都是结束循环和结束本次循环的意思
10.8 else语句
else语句在前面的if...else中有讲到,但是它其实有这三种搭配方式,所以单独提出来再总结一些东西
else可以与if配合使用,但是if条件表达式不成立的时候才能执行else
else也可以和while、for配合使用,但是只有当没有碰到break的时候才执行else
下面列举几个例子方便理解使用
for item in range(3):
pwd = input('请输入你的密码:')
if pwd == '8888':
print('密码正确')
break
else: # 此处的else是与上面的if搭配使用的,如果if执行成功,就不会再执行else
print('密码不正确')
else: # 此处的else是与最上面的for搭配使用的,如果没有遇到break,那么else就一定会被执行
print('对不起,三次密码均输入错误')
a = 0
while a < 3:
pwd = input('请输入你的密码:')
if pwd == '8888':
print('密码正确')
break
else: # 此处的else是与上面的if搭配使用的,如果if执行成功,就不会再执行else
print('密码不正确')
a+=1
else: # 此处的else是与最上面的while搭配使用的,如果没有遇到break,那么else就一定会被执行
print('对不起,三次密码均输入错误')
由此可见,其实
else的用法和C语言的其实也不太一样,但是代码其实从上到下按照本身的逻辑走一遍,这里面的else是与谁搭配使用的就很好被理解了
10.9 嵌套循环
嵌套循环在前面几个知识点的例子中都有涉及到,只是没有指出来细细讲解
其意义就是: 循环结构中又嵌套了另外的完整的循环结构,其中内层循环作为外层循环的循环体执行
下面就直接举一个打印九九乘法表的代码例子
for item in range(1, 10):
for it in range(1, item+1):
print(it, '*', item, '=', item*it, end='\t')
print('\n')
"""
1 * 1 = 1
1 * 2 = 2 2 * 2 = 4
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
"""
果然啊,用Python写九九乘法表就是很容易,比起C语言和Java的双层for循环,这个似乎简单了不少
11. 列表
11.1 为什么需要列表
- 变量可以存储一个元素,但是列表是一个“大容器”,可以存储N多个元素,程序可以方便地对这些数据进行整体操作
- 列表相当于其它语言中的数组
11.2 列表的创建
-
列表需要使用中括号[],元素之间使用英文的逗号进行分割
-
lst = ['大圣', '孙悟空'] -
列表的创建方式
- 直接使用中括号
lst = ['大圣', '孙悟空'] - 调用内置函数list()
lst2 = list(['大圣', '孙悟空'])
- 直接使用中括号
11.3 列表的特点
- 列表元素按照顺序有序排序
- 索引映射唯一一个数据
- 列表可以存储重复数据
- 列表可以任意数据类型混存
- 根据需要动态分配和回收内存
11.4 列表的查询操作
- 获取列表中指定元素的索引
index()
- 如查列表中存在
N个相同元素,只返回相同元素中的第一个元素的索引 - 如果查询的元素在列表中不存在,则会抛出
ValueError - 还可以在指定的
start和stop之间进行查找
下面用一个简单的代码例子来说明index()方法查询的三个特点
lst = ['hello', 'world', 985, 'hello']
print(lst.index('hello')) # 如查列表中有相同元素,只返回相同元素中的第一个元素的索引
print(lst.index('python')) # 如果不存在,则会抛出异常.ValueError: 'python' is not in list
print(lst.index('hello', 1, 3)) # 索引从1到3,但是不包括3.ValueError: 'hello' is not in list
- 获取列表中的单个元素
- 正向索引从
0到N-1举例:lst[0] - 逆向索引从
-N到-1举例:lst[-N] - 指定索引不存在时,会抛出
indexError
下面用一个简单的代码例子来说明获取单个元素的方法查询三个特点
lst = ['hello', 'world', 985, 'hello', 'world', 211]
# 获取索引为2的元素
print(lst[2]) # 985
# 获取索引为-3的元素
print(lst[-3]) # hello
# 获取索引为10的元素
print(lst[10]) # 报错提示列表超出范围,查询不到。IndexError: list index out of range
- 获取列表中的多个元素
语法格式:列表名[start : stop : step]
-
切片的结果:原列表片段的拷贝
-
切片的范围:
[start, stop] -
step默认为1:可以简写为[start: stop] -
step为正数的时候[: stop: step]:切片的第一个元素默认是列表中的第一个元素[start: :step]:切片的最后一个元素默认是列表的最后一个元素
-
step为负数的时候[: stop: step]:切片的第一个元素默认是列表中的最后一个元素[start: :step]:切片的最后一个元素默认是列表的第一个元素
下面通过一个例子解释一下,如何获取列表中的多个元素,以及它们的不同形式写法
lst = [10, 20, 30, 40, 50, 60, 70, 80]
# start=1,stop=6,step=1
print(lst[1:6:1]) # [20, 30, 40, 50, 60]
print('原列表', id(lst)) # 原列表 2125077867328
lst2 = lst[1:6:1]
print('切的片段', id(lst2)) # 切的片段 2125077761664
# 默认步长为1 后面加一个冒号,step不填也默认步长为1
print(lst[1:6]) # [20, 30, 40, 50, 60]
print(lst[1:6:]) # [20, 30, 40, 50, 60]
# start=1,stop=6,step=2
print(lst[1:6:2]) # [20, 40, 60]
# stop=6,step=2,start采用默认
print(lst[:6:2]) # [10, 30, 50]
# start=1,step=2,stop采用默认
print(lst[1::2]) # [20, 40, 60, 80]
print('-------------------step步长为负数的情况-----------------------')
print(lst[::-1]) # [80, 70, 60, 50, 40, 30, 20, 10]
# start=7,stop省略,step=-1
print(lst[7::-1]) # [80, 70, 60, 50, 40, 30, 20, 10]
# start=6,stop=0,step=-1
print(lst[6:0:-2]) # [70, 50, 30]
- 判断指定元素在列表中是否存在
- 元素
in列表名 - 元素
not in列表名
下面是一个例子说明如何判断指定元素是否在列表中存在
lst = [10, 20, 'python', 'hello']
print(10 in lst) # True
print(100 in lst) # False
print(10 not in lst) # False
print(100 not in lst) # True
- 列表元素的遍历
for 迭代变量 in 列表名 :
下面是一个例子说明如何操作对列表元素的遍历
lst = [10, 20, 'python', 'hello']
for item in lst:
print(item)
#输出结果
"""
10
20
python
hello
"""
11.5 列表的增加操作
列表元素的增加操作
| 方法/其它 | 操作描述 |
|---|---|
append() | 在列表的末尾添加一个元素 |
extend() | 在列表的末尾添加至少一个元素 |
insert() | 在列表的任意位置添加一个元素 |
| 切片 | 在列表的任意位置添加至少一个元素 |
下面用一个代码例子来说明上面四种添加方法的不同之处
lst = [10, 20, 30]
# 向列表的末尾添加一个元素
lst2 = ['hello', 'world']
# lst.append(lst2) # 将lst2作为一个元素添加到列表的末尾
print(lst) # [10, 20, 30, 100, ['hello', 'world']]
lst.extend(lst2) # 将lst2里面的各个元素作为单独元素添加到列表的末尾
print(lst) # [10, 20, 30, 100, 'hello', 'world']
# 在任意位置上添加一个元素
lst.insert(1, 90)
print(lst) # [10, 90, 20, 30, 100, 'hello', 'world']
lst3 = [True, False, 'hello']
# 在任意的位置上添加N多个元素
lst[1:] = lst3
print(lst) # [10, True, False, 'hello']
11.6 列表的删除操作
| 方法/其它 | 操作描述 |
|---|---|
remove() | 1. 一次删除一个元素 2. 重复元素只能删除第一个 3. 元素不存在抛出ValueError |
pop() | 1. 删除一个指定索引位置上的元素 2. 指定索引不存在抛出IndexError 3. 不指定索引,删除列表种最后一个元素 |
| 切片 | 一次至少删除一个元素 |
clear() | 清空列表 |
del | 删除列表 |
下面通过一个代码例子,来说明一下几种列表的删除操作的一些用法
lst = [10, 20, 30, 40, 50, 60, 30]
lst.remove(30) # 从列表种移除一个元素,如果有重复的只移除第一个元素
print(lst) # [10, 20, 40, 50, 60, 30]
# lst.remove(100) # ValueError: list.remove(x): x not in list
# pop()根据索引移除元素
lst.pop(1)
print(lst) # [10, 40, 50, 60, 30],将索引为1的元素20删除
# lst.pop(5) # IndexError: pop index out of range,如果指定的索引位置不存在,将抛出异常
lst.pop() # 如果不指定参数(索引),那么将删除列表种的最后一个元素
print(lst) # [10, 40, 50, 60]
# 切片操作
"""切片操作,删除至少一个元素,将产生一个新的列表对象"""
new_list = lst[1:3]
print('原列表', lst) # 原列表 [10, 40, 50, 60]
print('新列表', new_list) # 新列表 [40, 50]
"""不产生新的列表对象,而是删除原列表中的内容"""
lst[1:3] = []
print(lst) # [10, 60]
# 清除列表中的所有元素
lst.clear()
print(lst) # []
# del语句会将列表对象删除
del lst
print(lst) # NameError: name 'lst' is not defined
11.7 列表元素的修改操作
- 为指定索引的元素赋予一个新值
- 为指定的切片赋予一个新值
下面是一个代码例子,来参考学习一下列表元素的两种修改操作
lst = [10, 20, 30, 40]
# 一次修改一个值
lst[2] = 100
print(lst) # [10, 20, 100, 40]
# 利用切片,一次修改多个值
lst[1: 3] = [300, 400, 500, 600]
print(lst) # [10, 300, 400, 500, 600, 40]
11.8 列表元素的排序操作
列表元素的排序操作,常见的有两种方式
- 调用
sort()方法,列表中的所有元素默认按照从小到大的顺序进行排序,可以指定reverse=True,进行降序排序 - 调用内置函数
sorted(),可以指定reverse=True,进行降序排序,原列表不发生改变
下面通过一个例子,了解一下两种不同函数进行排序操作的一些特点
lst = [20, 40, 10, 98, 54]
print('排序前的列表', lst, id(lst)) # 排序前的列表 [20, 40, 10, 98, 54] 2701818270784
# 开始排序,利用列表对象的sort方法,升序排序
lst.sort()
print('排序后的列表', lst, id(lst)) # 排序后的列表 [10, 20, 40, 54, 98] 2701818270784
# 通过指定关键字参数,将列表中的元素进行降序排序
lst.sort(reverse=True)
print(lst) # [98, 54, 40, 20, 10]
# ---------使用内置函数sorted()对列表进行排序,将产生一个新的列表对象-----------
lst = [20, 40, 10, 98, 54]
new_list = sorted(lst)
print(new_list) # [10, 20, 40, 54, 98]
# 指定关键字参数,实现列表元素的降序排序
desc_list = sorted(lst, reverse=True)
print(desc_list) # [98, 54, 40, 20, 10]
11.9 列表生成式
列表生成式简称 “生成列表的公式”
下面通过一段代码,加深对列表生成式的理解
lst = [i for i in range(1, 10)]
print(lst) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
lst2 = [i*i for i in range(1, 10)]
print(lst2) # [1, 4, 9, 16, 25, 36, 49, 64, 81]
"""列表中的元素的值为2,4,6,8,10"""
lst3 = [i*2 for i in range(1, 6)]
print(lst3) # [2, 4, 6, 8, 10]
由上述代码可见,其实真正决定
lst的取值的就在于for前面的表达式为多少,如果是i,那就是range()本身迭代的元素,如果是i*2,那就要把迭代的元素全扩大2倍,就如注意事项说的一样,i*i表示为列表元素的表达式。
12 字典
12.1 什么是字典
-
Python内置的数据结构之一,与列表一样是一个可变序列
[可变序列] 可变就是指可以有增删改操作
-
以键值对的方式存储数据,字典是一个无序的序列
-
scores = {'张山': 100, '李四': 98, '王五': 45}- 他们分别由字典名、花括号、逗号、键、冒号、值一一对应组成
12.2 字典的原理
字典的实现原理
字典的实现原理与查字典类似,查字典是先根据部首或者拼音查找对应的页码,而Python中的字典是根据key查找value所在的位置
12.3 字典的创建与删除
- 字典的创建
-
最常用的方式:使用花括号
scores = {'张山': 100, '李四': 98, '王五': 45}
-
使用内置函数
dict()dict(name = 'jack', age = 20)
下面通过一段代码,来了解一下
12.4 字典的查询操作
字典中元素的获取有两种方式
[]——> 举例:scores['张三']get()方法 ——> 举例:scores.get('张三')
两种方法有着一些区别之处
[]:如果字典中不存在指定的key,抛出KeyError异常get()方法取值:如果字典中不存在指定的key,并不会抛出KeyError,而是返回None,可以通过参数设置默认的value,以便指定的key不存在时返回 下面通过一段代码,了解一下字典的查询操作
"""获取字典中的元素"""
scores = {'张三': 100, '李四': 98, '王五': 45}
# 第一种方式,使用[]
print(scores['张三']) # 100
print(scores['陈六']) # KeyError: '陈六'
# 第二种方式,使用get()方法
print(scores.get('张三')) # 100
print(scores.get('陈六')) # 并不会报错,而是输出None
print(scores.get('麻七', 99)) # 99是在查找‘麻七’时,所对应的value不存在时,提供的一个默认值
12.5 字典元素的增、删、改操作
1. key的判断
| key的判断 | 解释 | 举例 |
|---|---|---|
in | 指定的key在字典中存在返回True | '张三' in scores |
not in | 指定的key在字典中不存在返回True | 'Marry' not in scores |
scores = {'张三': 100, '李四': 98, '王五': 45}
"""key值的判断"""
print('张三' in scores) # True
print('张三' not in scores) # False
2. 字典元素的删除
del scores['张三']
scores = {'张三': 100, '李四': 98, '王五': 45}
"""字典的删除"""
del scores['张三'] # 删除指定的键值对(key-value)
print(scores) # {'李四': 98, '王五': 45}
scores.clear() # 清空字典中的所有元素
print(scores) # {}
3. 字典元素的新增
scores['jack'] = 90
scores = {'李四': 98, '王五': 45}
"""字典元素的新增"""
scores['陈六'] = 98 # 新增元素
print(scores) # {'李四': 98, '王五': 45, '陈六': 98}
"""字典元素的修改"""
scores['陈六'] = 100
print(scores) # {'李四': 98, '王五': 45, '陈六': 100}
值得注意的是,字典元素的新增也可以作为修改来运行,只需把括号里面的键改成自己需要修改的元素,即可对他的value进行修改
4. 获取字典视图的三个方法
keys()——>获取字典中所有的keyvalues()——>获取字典中所有valueitems()——>获取字典中所有key,value对
scores = {'张三': 100, '李四': 98, '王五': 45}
# 获取所有的key
keys = scores.keys()
print(keys) # dict_keys(['张三', '李四', '王五'])
print(type(keys)) # <class 'dict_keys'>
print(list(keys)) # ['张三', '李四', '王五'],将所有的key组成的试图转成列表
# 获取所有的value
values = scores.values()
print(values) # dict_values([100, 98, 45])
print(type(values)) # <class 'dict_values'>
print(list(values)) # [100, 98, 45]
# 获取所有的key-value对
items=scores.items()
print(items) # dict_items([('张三', 100), ('李四', 98), ('王五', 45)])
print(type(items)) # <class 'dict_items'>
print(list(items)) # [('张三', 100), ('李四', 98), ('王五', 45)],转换之后的列表元素是有元组组成的
5. 字典元素的遍历
代码格式:
for item in scores:
print(item)
下面是一个代码例子,用来理解一下元素的遍历方法的使用
scores = {'张三': 100, '李四': 98, '王五': 45}
# 字典元素的遍历
for item in scores:
print(item, scores[item], scores.get(item))
"""
张三 100 100
李四 98 98
王五 45 45
"""
由代码例子可见,遍历的时候,获取
value的值也是和上面的方法类似,可以直接使用[]方法,也可以使用get()方法。但是使用[]方法的时候可能会抛出异常,而get()方法不会抛出异常,只会输出None
12.6 字典的特点
- 字典中的所有元素都是一个
key-value对,key不允许重复,value可以重复 - 字典中的元素是无序的
- 字典中的
key必须是不可变对象 - 字典也可以根据需要动态地伸缩
- 字典会浪费较大的内存,是一种使用空间换时间的数据结构
12.7字典推导式
items = ['Fruits', 'Books', 'Others'] prices = [96, 78, 85]
{'Fruits': 96,'Books': 78, 'Others': 85}
-
内置函数
zip()- 用于将可迭代对象作为参数,将对象中对应的元素打包成一个元组,然后返回由这些元组组成的列表
items = ['Fruits', 'Books', 'Others']
prices = [96, 78, 85]
lst = zip(items, prices)
print(list(lst)) # [('Fruits', 96), ('Books', 78), ('Others', 85)]
d = {item: price for item, price in zip(items, prices)}
print(d) # {'Fruits': 96, 'Books': 78, 'Others': 85}
items = ['Fruits', 'Books', 'Others']
prices = [96, 78, 85, 100, 200]
d = {item: price for item, price in zip(items, prices)}
print(d) # {'Fruits': 96, 'Books': 78, 'Others': 85}
d = {item.upper(): price for item, price in zip(items, prices)}
print(d) # {'FRUITS': 96, 'BOOKS': 78, 'OTHERS': 85}
根据上述代码的演示,可以发现,就是利用两个列表合并,用for循环遍历生成了一个字典,其中使用到了内置函数
zip(),包括使用到了str.upper()函数,这个函数的意思为将小写字母变成大写字母
13. 元组
13.1 什么是元组
-
t = ('Python', 'hello', 90) -
元组:Python内置的数据结构之一,是一个不可变序列
-
不可变序列和可变序列
-
不可变序列:字符串、元组
- 不可变序列没有增删改的操作
-
可变序列:列表、字典
- 可变序列可以对序列执行增删改操作,对象地址不发生更改
-
13.2 元组的创建方式
- 直接小括号
t = ('Python', 'hello', 90)
- 使用内置函数tuple()
t = tuple(('Python', 'hello', 90))
- 只包含一个元组的元素需要使用逗号和小括号
t = (10, )
下面是几段代码,来解释一下元组的创建方式
# 第一种创建方式
t = ('Python', 'hello', 90)
print(t) # ('Python', 'hello', 90)
print('t', type(t)) # t <class 'tuple'>
t2 = 'Python', 'hello', 90 # 省略了小括号
print(t2) # ('Python', 'hello', 90)
print('t2', type(t2)) # t2 <class 'tuple'>
t3 = ('python', ) # 如果元组种只有一个元素,逗号不能省略
print(t3) # ('python',)
print('t3', type(t3)) # t3 <class 'tuple'>
# 第二种创建方式,使用内置函数tuple()
t4 = tuple(('Python', 'hello', 90))
print(t4) # ('Python', 'hello', 90)
print('t4', type(t4)) # t4 <class 'tuple'>
# 空列表的创建方式
lst = []
lst1 = list()
print('空列表', lst, lst1) # 空列表 [] []
# 空字典的创建方式
d = {}
d1 = dict()
print('空字典', d, d1) # 空字典 {} {}
# 空元组的创建方式
t = ()
t1 = tuple()
print('空元组', t, t1) # 空元组 () ()
上面代码种也提到了一些注意事项,例如元组只有一个元素的时候,应该怎么创建,在后面加上逗号。另外,还提到了空列表、空字典、空元组的创建方式
13.3 元组的遍历
元组是可迭代对象,所以可以使用for in进行遍历
t = (10, [20, 30], 9)
for item in t:
print(item)
13.4 为什么将元组设计成不可变序列
- 在多任务环境下,同时操作对象时不需要加锁
- 因此,在程序种尽量使用不可变序列
注意事项:
-
元组种存储的是对象的引用
- 如果元组中对象本身是不可对象,则不能再引用其他对象
- 如果元组种的对象是可变对象,则可变对象的引用不允许改变,但数据可以改变
下面是一段代码例子,用来说明一下注意事项中的两条
t = (10, [20, 30], 9)
print(id(t)) # 1505236190656
print(t) # (10, [20, 30], 9)
print(type(t)) # <class 'tuple'>
print(t[0], type(t[0]), id(t[0])) # 10 <class 'int'> 2168335919696
print(t[1], type(t[1]), id(t[1])) # [20, 30] <class 'list'> 1505235036864
print(t[2], type(t[2]), id(t[2])) # 9 <class 'int'> 2168335919664
"""尝试将t[1]修改成100"""
print(id(100))
# t[1] = 100 # 元组是不允许修改元素的
# 由于[20,30]是列表,而列表是可变序列,所以可以向列中添加元素,但是列表的内存地址不变
t[1].append(100) # 向列表中添加元素
print(t, id(t[1])) # (10, [20, 30, 100], 9) 1505235036864
由此可见,如果元组对象中的是不可变对象,比如
10,9,那么他们就不能再引用其他对象,因为他们的id是固定的,如果是t[1],[20, 30],本身是一个可变的列表,那么它的引用是不可变的,因为它的id是固定的。但是可以再列表后面接着追加变量,例如append(100),此时它的id仍然是没有变化
14. 集合
14.1 什么是集合
- Python语言提供的内置数据结构
- 与列表、字典一样都属于可变类型的序列
- 集合是没有value的字典
14.2 集合的创建
-
直接
{}s = {'python', 'hello', 90}
-
使用内置函数
set()
下面通过一些代码例子,来了解一下集合的创建
# 第一种创建方式,使用{}
s = {2, 3, 4, 5, 5, 6, 7, 7} # 集合中的元素不允许重复
print(s) # {2, 3, 4, 5, 6, 7}
# 第二种创建方式,使用内置函数set()
s1 = set(range(6))
print(s1, type(s1)) # {0, 1, 2, 3, 4, 5} <class 'set'>
s2 = set([1, 2, 4, 5, 5, 6, 6]) # 将列表中的元素转换成集合
print(s2, type(s2)) # {1, 2, 4, 5, 6} <class 'set'>
s3 = set((1, 2, 4, 5, 5, 65)) # 元组是无序的,所以打印出来的数字顺序变化
print(s3, type(s3)) # {65, 1, 2, 4, 5} <class 'set'>
s4 = set('python') # 将str类型的元素转换成集合
print(s4, type(s4)) # {'h', 'o', 'p', 'n', 't', 'y'} <class 'set'>
s5 = set({1, 31, 23, 45, 22, 45}) # 将集合中的元素转换成另外一个集合,元素的顺序发成变化
print(s5, type(s5)) # {1, 22, 23, 45, 31} <class 'set'>
# 定义一个空集合
s6 = {} # 直接定义一个花括号,类型是字典,并不是集合
print(type(s6)) # <class 'dict'>
s7 = set() # 用set()定义的时候才是集合
print(type(s7)) # <class 'set'>
14.3 集合的增删查改操作
1. 集合元素的判断操作
in或者not in
s = {10, 20, 30, 40, 50}
# 集合元素的判断操作
print(10 in s) # True
print(100 in s) # False
print(10 not in s) # False
2. 集合元素的新增操作
- 调用
add()方法,一次添加一个元素 - 调用
update()方法,至少添加一个元素
s = {10, 20, 30, 40, 50}
# 集合元素的新增操作
s.add(80) # 一次添加一个元素
print(s) # {80, 50, 20, 40, 10, 30}
s.update({100, 200, 300}) # 一次添加至少一个元素
print(s) # {100, 40, 200, 10, 300, 80, 50, 20, 30}
s.update([100, 101])
s.update((201, 202))
print(s) # {100, 101, 40, 200, 10, 201, 300, 202, 80, 50, 20, 30}
3. 集合元素的删除操作
- 调用
remove()方法,一次删除一个指定元素,如果指定的元素不存在,则抛出KeyError异常 - 调用
discard()方法,一次删除一个指定元素,如果指定的元素不存在,不抛出异常 - 调用
pop()方法,一次只删除一个任意元素 - 调用
clear()方法,清空集合
s = {100, 101, 40, 200, 10, 201, 300, 202, 80, 50, 20, 30}
# 集合元素的删除操作
s.remove(100)
print(s) # {101, 40, 200, 10, 201, 300, 202, 80, 50, 20, 30}
# s.remove(500) # KeyError: 500
s.discard(101)
s.discard(500) # 集合中没有500,但是程序也没有报错没有抛出异常
print(s) # {40, 200, 10, 201, 300, 202, 80, 50, 20, 30}
s.pop() # 随机把40删除了,pop()函数是没有参数的,删除的元素也是随机的
print(s) # {200, 10, 201, 300, 202, 80, 50, 20, 30}
s.clear() # 直接把集合清空了
14.4 集合之间的关系
-
两个集合是否相等
- 可以使用运算符
==或者!=进行判断
- 可以使用运算符
# 判断两个集合是否相等
s = {10, 20, 30, 40}
s2 = {30, 40, 20, 10}
print(s == s2) # True
print(s != s2) # False
-
一个集合是否是另一个集合的子集
- 可以调用方法
issubset进行判断 B是A的子集
- 可以调用方法
# 一个集合是否是另一个集合的子集
s1 = {10, 20, 30, 40, 50, 60}
s2 = {10, 20, 30, 40}
s3 = {10, 20, 90}
print(s2.issubset(s1)) # True
print(s3.issubset(s1)) # False
-
一个集合是否是另一个集合的超集
- 可以调用方法
issuperset进行判断 A是B的超集
- 可以调用方法
# 一个集合是否是另一个集合的超集
s1 = {10, 20, 30, 40, 50, 60}
s2 = {10, 20, 30, 40}
s3 = {10, 20, 90}
print(s1.issuperset(s2)) # True
print(s1.issuperset(s3)) # False
-
两个集合是否没有交集
- 可以调用方法
isdisjoint进行判断
- 可以调用方法
# 两个集合是否没有交集
s1 = {10, 20, 30, 40, 50, 60}
s2 = {10, 20, 30, 40}
s3 = {10, 20, 90}
print(s2.isdisjoint(s3)) # False 有交集为False
s4 = {100, 200, 300, 400}
print(s2.isdisjoint(s4)) # True s2和s4是没有交集的,没有交集为True
14.5 集合的数据操作
交集、并集、差集、对称差集
# (1)交集
s1 = {10, 20, 30, 40}
s2 = {20, 30, 40, 50}
print(s1.intersection(s2)) # {40, 20, 30}
print(s1 & s2) # {40, 20, 30} intersection()和 & 等价,都是交集操作
# (2)并集
print(s1.union(s2)) # {40, 10, 50, 20, 30}
print(s1 | s2) # {40, 10, 50, 20, 30} union()和 | 等价,都是并集操作
# (3)差集
print(s1.difference(s2)) # {10}
print(s1-s2) # {10} difference()和 - 等价,都是差集操作
# (4)对称差集
print(s1.symmetric_difference(s2)) # {10, 50}
14.6 集合生成式
-
用于生成集合的公式
{i * i for i in range(1, 10)}i*i中的i表示集合元素的表达式,第二个i是自定义变量,range是可迭代对象
-
将
{}修改为[]就是列表生成式 -
没有元素生成式
# 列表生成式
lst = [i*i for i in range(6)]
print(lst) # [0, 1, 4, 9, 16, 25]
# 集合生成式
s = {i*i for i in range(10)}
print(s) # {0, 1, 64, 4, 36, 9, 16, 49, 81, 25}
15. 列表、字典、元组、集合总结
| 数据结构 | 是否可变 | 是否重复 | 是否有序 | 定义符号 |
|---|---|---|---|---|
列表(list) | 可变 | 可重复 | 有序 | [] |
元组(tuple) | 不可变 | 可重复 | 有序 | () |
字典(dict) | 可变 | key不可重复 value可重复 | 无序 | {key: value} |
集合(set) | 可变 | 不可重复 | 无序 | {} |
16. 字符串
16.1 字符串的驻留机制
-
字符串
- 在Python中字符串是基本数据类型,是一个不可变的字符序列
-
什么叫字符串驻留机制呢?
- 仅保存一份相同且不可变字符串的方法,不同的值被存放在字符串的驻留池中,Python的驻留机制对相同的字符串只保留一份拷贝,后续创建相同字符串时候,不会开辟新空间,而是把该字符串的地址赋给新创建的变量
# 字符串的驻留机制
a = 'Python'
b = "Python"
c = '''Python'''
print(a, id(a)) # Python 1827795263216
print(b, id(b)) # Python 1827795263216
print(c, id(c)) # Python 1827795263216
由此可见,无论是
a还是b还是c,他们指向的空间都是同一块地址
-
驻留机制的几种情况(交互模式)
- 字符串的长度为0或者1时
- 符合标识符的字符串
- 字符串直在编译时进行驻留,而非运行时
- [-5, 256]之间的整数数字
Python 3.9.2 (tags/v3.9.2:1a79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
------------------------------------
>>> s1=''
>>> s2=''
>>> s1 is s2 创建两个字符串,正好他们的长度为0,都是空字符串,那么得到的就是 同一块地址。
True 得到的答案还是True,对应着驻留机制的第一种情况
------------------------------------
>>> s1='%'
>>> s2='%'
>>> s1 is s2 创建两个字符串,正好他们的长度为1,都是只有1个长度的字符串,那么得到的就是 同一块地址。
True 得到的答案还是True,对应着驻留机制的第一种情况
------------------------------------
>>> s1='abc%'
>>> s2='abc%'
>>> s1==s2
True
>>> s1 is s2 两个字符串不是具有符合标识符的字符串,所以得到的结果是false,指向的不是同一块空间
False
>>> id(s1)
2149930897456
>>> id(s2)
2149930897520
------------------------------------
>>> s1='abcx'
>>> s2='abcx'
>>> s1 is s2 两个字符串是符合标识符的字符串,那么他们指向的就是同一块空间
True 得到的答案就是True,所以他们对应着驻留机制的第二种情况
>>> id(s1)
2149930897584
>>> id(s2)
2149930897584
------------------------------------
>>> a='abc'
>>> b='ab'+'c'
>>> c=''.join(['ab','c'])
>>> a is b a和b是在编译的时候,都成为了abc,所以在编译的时候就已经具有驻留机制
True 他们得到的结果也是True,指向同一块空间地址,对应驻留机制的第三中情况
>>> a is c
False 但是c用的是join方法,他是在运行的时候才生成abc,所以不具有驻留机制
>>> c
'abc'
>>> type(c)
<class 'str'>
------------------------------------
>>> a=-5
>>> b=-5
>>> a is b 这是对应驻留机制的最后一条,数字是有范围的,在[-5, 256]之间的数字就具有驻留机制
True
>>> a=-6
>>> b=-6
>>> a is b
False
------------------------------------
>>> import sys
>>> a='abc%'
>>> b='abc%'
>>> a is b
False
>>> a=sys.intern(b)
>>> a is b 这是利用sys中的intern方法,可以强制将两个字符串指向同一个对象,从而具有驻留机制
True
sys中的intern()方法强制两个字符串指向同一个对象PyCharm对字符串进行了优化处理
字符串驻留机制的优缺点
- 当需要值相同的字符串时,可以直接从字符串池里拿出来使用,避免频繁的创建和销毁,提升效率和节约内存,因此拼接字符串和修改字符串是会比较影响性能的
- 在需要进行支付穿拼接的时候建议使用
str类型的join方法,而非+,因为join()方法是闲计算出所有字符串中的长度,然后再拷贝,只new一次对象,效率比“+”效率高
16.2 字符串的常用操作
1. 字符串的查询操作的方法
| 方法名称 | 作用 |
|---|---|
index() | 查找子串substr第一次出现的位置,如果查找的子串不存在时,则抛出ValueError |
rindex() | 查找子串substr最后一次出现的位置,如果查找的子串不存在时,则抛出ValueError |
find() | 查找子串substr第一次出现的位置,如果查找的子串不存在时,则返回-1 |
rfind() | 查找子串substr最后一次出现的位置,如果查找的子串不存在时,则返回-1 |
# 字符串的查询操作
s = 'hello,hello'
print(s.index('lo')) # 3
print(s.find('lo')) # 3
print(s.rindex('lo')) # 9
print(s.rfind('lo')) # 9
print(s.index('k')) # ValueError: substring not found
print(s.find('k')) # -1
由上述代码空可见,
index和find都能查找子串出现的位置,但是index会抛出异常,所以一般建议使用find方法
2. 字符串的大小写转换操作的方法
| 方法名称 | 作用 |
|---|---|
upper() | 把字符串中所有字符都转换成大写字母 |
lower() | 把字符串中所有字符都转换成小写字母 |
swapcse() | 把字符串中所有大写字母转换成小写字母,把所有小写字母转换成大写字母 |
capitalize() | 把第一个字符转换成大写,把其余字符转换成小写 |
title() | 把每一个单词的第一个字符转换成大写,把每个单词剩余字符转换成小写 |
# 字符串中的大小写转换的方法
s = 'hello,python'
print(s, id(s)) # hello,python 2514180985840
a = s.upper() # 转成大写会产生一个新的字符串对象
print(a, id(a)) # HELLO,PYTHON 2380455957488
b = s.lower() # 转换小写之后,仍然会产生一个新的字符串对象
print(b, id(b)) # hello,python 2329172722096
print(s is b) # False
s2 = 'hello,Python'
print(s2.swapcase()) # HELLO,pYTHON 小写变大写,大写变小写
print(s2.capitalize()) # Hello,python 首字母大写,其他字母小写
print(s2.title()) # Hello,Python 每一个单词的首字母大写,其余字母小写
3. 字符串内容对齐操作的方法
| 方法名称 | 作用 |
|---|---|
center() | 居中对齐,第1个参数指定宽度,第2个参数指定填充符,第2个参数的可选的,默认是空格,如果设置宽度小于实际宽度则返回原字符串 |
ljust() | 左对齐,第1个参数指定宽度,第2个参数指定填充符,第2个参数的可选的,默认是空格,如果设置宽度小于实际宽度则返回原字符串 |
rjust() | 右对齐,第1个参数指定宽度,第2个参数指定填充符,第2个参数的可选的,默认是空格,如果设置宽度小于实际宽度则返回原字符串 |
zfill() | 右对齐,左边用0填充,该方法只接收一个参数,用于指定字符串的宽度,如果指定的宽度小于等于字符串的长度,返回字符串本身 |
# 字符串的对齐方式
s = 'hello,Python'
"""居中对齐"""
print(s.center(20, '*')) # ****hello,Python****
print(s.center(20)) # hello,Python ,如果第二个参数不写,填充符默认为空格
"""左对齐"""
print(s.ljust(20, '*')) # hello,Python********
print(s.ljust(10)) # hello,Python
"""右对齐"""
print(s.rjust(20, '*')) # ********hello,Python
print(s.rjust(20)) # hello,Python
print(s.rjust(10)) # hello,Python 第一个从参数的长度小于字符串实际长度,所以还是原样显示
"""右对齐,使用0进行填充"""
print(s.zfill(20)) # 00000000hello,Python
print(s.zfill(10)) # hello,Python
print('-8910'.zfill(8)) # -0008910 0添加到了负号的后面
4. 字符串劈分操作的方法
| 方法名称 | 作用 |
|---|---|
split() | 1. 从字符串的左边开始劈分,默认的劈分字符是空格字符串,返回的值都是一个列表 2. 以通过参数sep指定劈分字符串是的劈分符 3. 通过参数maxsplit指定劈分字符串时的最大劈分次数,在经过最大次劈分之后,剩余的子串会单独作为一部分 |
rsplit() | 1. 从字符串的右边开始劈分,默认的劈分字符是空格字符串,返回的值都是一个列表 2. 以通过参数sep指定劈分字符串是的劈分符 3. 通过参数maxsplit指定劈分字符串时的最大劈分次数,在经过最大次劈分之后,剩余的子串会单独作为一部分 |
# 字符串的劈分操作
s = 'hello world python'
"""split(),从左侧开始劈分"""
lst = s.split()
print(lst) # ['hello', 'world', 'python']
s1 = 'hello|world|python'
print(s1.split()) # ['hello|world|python'] 由于字符串中没有空格,所以没能劈分,还是1个字符串
print(s1.split(sep='|')) # ['hello', 'world', 'python'] ,当我们指定了sep的切割符号之后,就会按照符号来切割
print(s1.split(sep='|', maxsplit=1)) # ['hello', 'world|python']
"""rsplit(),从右侧开始劈分"""
print(s.rsplit()) # ['hello', 'world', 'python']
print(s1.rsplit(sep='|')) # ['hello', 'world', 'python']
print(s1.rsplit(sep='|', maxsplit=1)) # ['hello|world', 'python']
如果字符串劈分没有设定
maxsplit,其实split和rsplit的效果是一样的
5. 判断字符串操作的方法
| 方法名称 | 作用 |
|---|---|
isidentifier() | 判断指定的字符串是不是合法的标识符(字母、数字、下划线) |
isspace() | 判断指定的字符串是否全部由空白字符组成(回车、换行,水平制表符) |
isalpha() | 判断指定的字符串是否全部由字母组成 |
isdecimal() | 判断指定的字符串是否全部由十进制的数字组成 |
isnumeric() | 判断指定的字符串是否全部由数字组成 |
isalnum() | 判断指定的字符串是否全部由字母和数字组成 |
# 判断字符串操作的方法
s = 'hello,python'
print('1、是否为合法标识符', s.isidentifier()) # False
print('张三'.isidentifier()) # True
print('2、是否为空白字符', s.isspace()) # False
print('\t'.isspace()) # True
print('3、是否全部为字母组成', s.isalpha()) # False
print('abc'.isalpha()) # True
print('4、是否全部由十进制数字组成', s.isdecimal()) # False
print('123'.isdecimal()) # True
print('5、是否全部由数字组成', s.isnumeric()) # False
print('123123000'.isnumeric()) # True
print('四五'.isnumeric()) # True
print('Ⅱ'.isnumeric()) # True
print("6、是否全部由字母和数字组成", s.isalnum()) # False
print('123abc'.isalnum()) # True
在上面的代码例子中,有几个比较特殊的地方,一个是罗马数字和汉语数字一二三并不是十进制数字,但是在判断是否由数字组成的时候,他们的返回值是
True,就说明,他们虽然不是十进制数字,但是也是数字,系统判断为True
6. 字符串操作的其它方法
| 功能 | 方法名称 | 作用 |
|---|---|---|
| 字符串替换 | replace() | 第1个参数指定被替换的子串,第2个参数指定替换子串的字符串,该方法返回替换得到的字符串,替换前的字符串不会发生改变,调用该方法时可以通过第3个参数指定最大替换次数 |
| 字符串合并 | join() | 将列表或者元组中的字符串合并为一个字符串 |
# 字符串操作的其他方法
"""字符串替换"""
s = 'hello,python'
print(s.replace('python', 'java')) # hello,java
s1 = 'hello,python,python,python,python'
print(s1.replace('python', 'java', 2)) # hello,java,java,python,python
"""字符串合并"""
lst = ['hello', 'java', 'python'] # 列表
print('|'.join(lst)) # hello|java|python
print(''.join(lst)) # hellojavapython
t = ('hello', 'java', 'python') # 元组
print(''.join(t)) # hellojavapython
print('*'.join('python')) # 字符串 p*y*t*h*o*n
16.3 字符串的比较
字符串的比较操作
- 运算符: >、>=、<、<=、==、!=
- 比较规则: 首先比较两个字符串的第一个字符,如果相等则继续比较下一个字符,依次比较下去,直到两个字符串中的字符不相等时,其比较结果就是两个字符串的比较结果,两个字符串中的所有后续字符将不再被比较
- 比较原理: 两个字符进行比较时,比较的是其
ordinal value(原始值),调用内置函数ord可以得到指定字符的ordinal value。与内置函数ord对应的是内置函数chr,调用内置函数chr时指定ordinal value可以得到其对应的字符
# 字符串比较
print('apple' > 'app') # True
print('apple' > 'banana') # False
print(ord('a'), ord('b')) # 97 98
print(chr(97), chr(98)) # a b
"""
== 与 is的区别
1、==比较的是value值
2、is比较的是id是否相等
"""
16.4 字符串的切片操作
字符串是不可变类型
- 不具备增、删、改等操作
- 切片操作将产生新的对象
# 字符串的切片
s = 'hello,Python'
s1 = s[:5] # 没有写起始位置,只有结束位置,默认从第一个字符0开始切
s2 = s[6:] # 没有写结束位置,只有起始位置,默认切到最后一个字符
s3 = '!'
newstr = s1+s3+s2
print(s1) # hello
print(s2) # Python
print(newstr) # hello!Python
print('-----------------------------')
print(id(s)) # 2243942765552
print(id(s1)) # 2243945323888
print(id(s2)) # 2243943173552
print(id(newstr)) # 2324519028080
"""
完整格式
切片[start:end:step]
没有指定步长,默认步长就为1
"""
print(s[1:5:1]) # ello 从1开始截取到5(不包含5),步长为1
print(s[::2]) # hloPto 没有写起始,默认从0开始,没有写结束,默认到最后一个元素,步长为2
print(s[::-1]) # nohtyP,olleh 默认从字符串的最后一个元素开始,到字符串第一个元素结束,以为步长为负数
print(s[-6::1]) # Python 从索引为-6开始,到字符串的最后一个元素结束,步长为1
16.5 格式化字符串
# 格式化字符串
"""(1) % 占位符"""
name = '张三'
age = 20
print('我叫%s,今年%d岁' % (name, age)) # 我叫张三,今年20岁
"""(2) {} 占位符"""
print('我叫{0},今年{1}岁'.format(name, age)) # 我叫张三,今年20岁
"""(3) f-string"""
print(f'我叫{name},今年{age}岁') # 我叫张三,今年20岁
上面代码是常见的三种格式化字符串的表示方式,第一中是利用
%占位符,其中%s代表字符串,%i和%d代表整数,%f代表浮点数。第三中方法的使用,需要在输出语句的前面加上一个f,来格式化字符串
"""第一种方法,利用%来确定宽度"""
print('%10d' % 99) # 99,这个10表示的是宽度
print('%.3f' % 3.1415926) # 3.142,这个.3表示小数点后三位
# 同时表示宽度和精度
print('%10.3f' % 3.1415926) # 3.142,一共总宽度为10,小数点后三位
"""第二种方法,利用{}来确定宽度"""
print('{0}'.format(3.1415926)) # 3.1415926
print('{0:.3}'.format(3.1415926)) # 3.14,:.3表示一共保留三位数字
print('{0:.3f}'.format(3.1415926)) # 3.142,:.3f表示三位小数
print('{0:10.3f}'.format(3.1415926)) # 3.142,同时表示宽度和精度
上面代码是来格式化数字的一些宽度和精度的,一共展示了两种比较常见的方法
16.6 字符串的编码与转码
为什么需要字符串的编码转换
A计算机(str在内存种以Unicode表示)——编码——> ——byte字节传输——> ——解码——> B计算机(显示)
编码与解码的方式
- 编码: 将字符串转换成二进制数据(
bytes) - 解码: 将
bytes类型的数据转换成字符串类型
# 字符串的编码和解码
"""编码操作"""
s = '天涯共此时'
print(s.encode(encoding='GBK')) # b'\xcc\xec\xd1\xc4\xb9\xb2\xb4\xcb\xca\xb1'
# 在GBK这种编码格式种,一个中文占两个字节
print(s.encode(encoding='UTF-8')) # b'\xe5\xa4\xa9\xe6\xb6\xaf\xe5\x85\xb1\xe6\xad\xa4\xe6\x97\xb6'
# 在UTF-8这种编码格式种,一个中文占三个字节
"""解码操作"""
byte = s.encode(encoding='GBK')
print(byte.decode(encoding='GBK')) # 天涯共此时
byte = s.encode(encoding='UTF-8')
print(byte.decode(encoding='UTF-8')) # 天涯共此时
上面的代码,就是字符串的编码和解码的操作,
encode为编码,decode为解码,需要注意的是编码和解码的格式必须一一对应
17. 函数
17.1 函数的创建和调用
什么是函数
- 函数就是执行特定任何以完成特定功能的一段代码
为什么需要函数
- 复用代码
- 隐藏实现细节
- 提高可维护性
- 提高可读性便于调试
函数的创建
def 函数名([输入参数]) :
函数体
[return xxx]
- 函数的创建
# 函数的创建
def calc(a, b):
c = a + b
return c
- 函数的调用
# 函数的调用
result = calc(10, 20)
print(result)
- 跳到定义函数的函数体内
- 执行函数体
- 跳到函数的调用处
- 继续执行下一条语句
17.2 函数的参数传递
-
位置实参
- 根据形参对应的位置进行实参传递
-
关键字实参
- 根据形参名称进行实参传递
# 函数的创建
def calc(a, b): # a,b称为形式参数,简称形参,形参的位置在函数的定义处
c = a + b
return c
# 函数的调用
result = calc(10, 20) # 10,20称为实际参数,简称实参,实参的位置是函数的调用处
print(result)
res = calc(b=10, a=20) # =左侧的变量的名称为关键字,采用关键字传参
print(res) # 30,虽然结果都一样,但是传递参数的值是不一样的,这里的b的10就是传给形参b了,a的20就是传给形参a了
函数调用的参数传递内存分析
def fun(arg1, arg2):
print('arg1', arg1) # arg1 11
print('arg2', arg2) # arg2 [22, 33, 44]
arg1 = 100
arg2.append(10)
print('arg1', arg1) # arg1 100
print('arg2', arg2) # arg2 [22, 33, 44, 10]
n1 = 11
n2 = [22, 33, 44]
print('n1', n1) # n1 11
print('n2', n2) # n2 [22, 33, 44]
fun(n1, n2) # 位置传参
print('n1', n1) # n1 11
print('n2', n2) # n2 [22, 33, 44, 10]
"""
在函数的调用过程中,进行参数的传递
如果是不可变对象,在函数体的修改不会影响实参的值 arg1的修改为100,不会影响n1的值
如果是可变对象,在函数体内的修改会影响实参的值 arg2的修改,append(10),会影响n2的值
"""
17.3 函数的返回值
- 函数返回多个值时,结果为元组
# 函数的返回值
def fun(num):
odd = [] # 存奇数
even = [] # 存偶数
for i in num:
if i % 2:
odd.append(i)
else:
even.append(i)
return odd, even
# 函数的调用
lst = [10, 29, 34, 23, 44, 53, 55]
print(fun(lst)) # ([29, 23, 53, 55], [10, 34, 44])
"""
函数的返回值
(1)如果函数没有返回值【函数执行完毕之后,不需要给调用处提供数据】 return可以省略不写
(2)函数的返回值,如果是1个,直接返回类型
(3)函数的返回值,如果是多个,返回的结果为元组
"""
def fun1():
print('hello')
# return
fun1() # hello
def fun2():
return 'hello'
res = fun2()
print(res) # hello
def fun3():
return 'hello', 'world'
print(fun3()) # ('hello', 'world')
"""函数在定义时,是否需要返回值,视情况而定"""
函数在定义时,是否需要返回值,视情况而定,如果需要返回值,就写上返回值,需要注意的就是,返回多个值的时候,返回的类型为元组
def fun(**args):
print(args)
fun(a=10) # {'a': 10}
fun(a=20, b=30, c=40) # {'a': 20, 'b': 30, 'c': 40}
"""
def fun2(*args, *a):
pass
以上代码,程序会报错,可变的位置参数,只能是1个
def fun2(**args, **a):
pass
以上代码,程序会报错,个数可变的关键字参数,只能是1个
"""
def fun2(*args1, **args2):
pass
"""
def fun3(**args1, *args):
pass
在一个函数的定义过程中,既有个数可变的关键字形参,也有个数可变的位置形参
要求:个数可变的位置形参,放在个数可变的关键字形参前面
"""
上面代码是函数定义的参数定义的一个特殊情况,如果又有关键字形参也有位置形参,那就要求个数可变的位置形参,放在个数可变的关键字形参的前面
| 序号 | 参数的类型 | 函数的定义 | 函数的调用 | 备注 |
|---|---|---|---|---|
| 1.1 | 位置实参 | √ | ||
| 1.2 | 将序列中的每个元素都转换成位置实参 | √ | 使用* | |
| 2.1 | 关键字实参 | √ | ||
| 2.2 | 将序列中的每个键值对都转换为关键字实参 | √ | 使用** | |
| 3 | 默认值形参 | √ | ||
| 4 | 关键字形参 | √ | 使用* | |
| 5 | 个数可变的位置形参 | √ | 使用* | |
| 6 | 个数可变的关键字形参 | √ | 使用** |
函数调用的时候的参数传递
def fun(a, b, c): # a,b,c在函数的定义处,所以是形式参数
print("a=", a, 'b=', b, 'c=', c)
# 函数的调用
fun(10, 20, 30) # 函数调用时的参数传递,称为位置传参
lst = [11, 22, 33]
fun(*lst) # 在函数调用时,将列表中的每个元素都转换成位置实参传入
fun(a=100, c=300, b=200) # 函数的调用,所以是关键字传参
dic = {'a': 111, 'b': 222, 'c': 333}
fun(**dic) # 在函数调用时,将字典中的键值对都转换成关键字实参传入
函数调用的时候,函数的定义形参问题
def fun(a, b=10): # b是在函数的定义处,所以b是形参,而且进行了赋值,所以b称为默认值形参
print('a=', a, 'b=', b)
def fun2(*args): # 个数可变的位置形参
print(args)
def fun3(**args): # 个数可变的关键字形参
print(args)
fun2(10, 20, 30, 40) # (10, 20, 30, 40)
fun3(a=11, b=12, c=143, d=15) # {'a': 11, 'b': 12, 'c': 143, 'd': 15}
def fun4(a, b, c, d):
print("a=", a, 'b=', b, 'c=', c, 'd=', d)
# 调用fun4函数
fun4(10, 20, 30, 40) # 位置实参传递
fun4(a=10, b=20, c=30, d=40) # 关键字实参传递
fun4(10, 20, c=30, d=40) # 前两个参数,采用的是位置实参传递,而c,d采用的是关键字实参传递
"""
需求,c,d只能采用关键字实参传递
def fun4(a, b, *, c, d): # 从*之后的参数,在函数调用的时候,都必须采用关键字参数传递
"""
"""函数定义时的形参顺序问题"""
def fun5(a, b, *, c, d, **args):
pass
def fun6(*args, **args2):
pass
def fun7(a, b=10, *args, **args2):
pass
17.5 变量的作用域
-
程序代码能访问该变量的区域
-
根据变量的有效范围可以分为
-
局部变量
- 在函数内定义并使用的变量,只在函数内部有效,局部变量使用global声明,这个变量就成为全局变量
-
全局变量
- 函数体外声明的变量,可作用于函数内外
-
def fun(a, b):
c = a+b # C成为局部变量,因为C是再函数体内进行定义的变量,a,b为函数的形参,作用范围也是函数内部,相当于局部变量
print(c)
name = '张三' # name的作用范围为函数内部和外部都可以使用,被称为全局变量
print(name)
def fun2():
print(name)
# 调用函数
fun2() # 张三
def fun3():
global age # 函数内部定义的变量,局部变量,局部变量使用global声明,变量就变成全局变量了
age = 20
print(age)
fun3()
print(age) # 20
17.6 递归函数
什么是递归函数
- 如果在一个函数的函数体内调用了该函数本身,这个函数就称为递归函数
递归的组成部分
- 递归调用与递归终止条件
递归的调用过程
- 每递归调用一次函数,都会在栈内存分配一个栈帧
- 每执行完一次函数,都会释放相应的空间
递归的优缺点
- 缺点: 占用内存多,效率底下
- 优点: 思路和代码简单
使用递归来计算阶乘
def fac(n):
if n==1:
return 1
else:
return n*fac(n-1)
print(fac(3))
使用递归来计算斐波那契数列
def fib(n):
if n == 1:
return 1
elif n == 2:
return 1
else:
return fib(n-1)+fib(n-2)
# 斐波那契数列在第6位上的数字
print(fib(6))
# 输出这个数列的前6位上的数字
for i in range(1,7):
print(fib(i))
18. Bug
18.1 Bug的由来及分类
马克2号当年计算机出现问题,始终没有修理好,最终打开计算机,发现里面有一只飞蛾,便有了Bug
- 世界上第一部万用计算机的进化版-马克2号(Mark Ⅱ)
bug的常见类型
-
粗心导致的语法错误
SyntaxError -
知识点不熟练导致的错误
- (1) 索引越界问题IndexError
lst = [11,22,33,44]print(lst[4])应该输出lst[3]- (2) append()方法的使用掌握不熟练
lst = []lst = append('a','b','c','d')append是列表的方法,在调用的时候应该是lst.append('A')print(lst)并且append方法一次只能添加一个元素
18.2 不同异常类型的处理方式
-
粗心导致错误的自查宝典
- 漏了末尾的冒号,如if语句、循环语句、else子句等
- 缩进错误,该缩进的没缩进,不该缩进的瞎缩进
- 把英文符号写出中文符号,比如说:冒号、引号、括号
- 字符串拼接的时候,把字符串和数字拼接在一起
- 没有定义变量,比如说while的循环条件的变量
- “
==”比较运算符和“=”赋值运算符的混用
-
思路不清晰导致的问题解决方案
- 使用
print()函数 - 使用“
#”暂时注解部分代码
- 使用
-
被动掉坑:程序代码逻辑没有错,只是因为用户错误操作或者一些“例外情况”而导致的程序崩溃
-
被动掉坑问题的解决方案
- Python提供了异常处理机制,可以在异常出现的时候及时捕获,然后内部“消化”,让程序继续运行
-
多个except结构
- 捕获异常的顺序按照先子类后父类的顺序,为了避免遗漏可能出现的异常,可以在最后增加BaseException
-
try:
可能会出现异常的代码
except xxx(异常类型):
报错后执行的代码
try:
a = int(input('请输入第一个整数:'))
b = int(input('请输入第二个整数:'))
result = a/b
print('结果为:', result)
except ZeroDivisionError:
print('对不起,除数不允许为0')
print('程序结束')
上面的代码是只处理一个异常,所以只使用了一个
except
try:
a = int(input('请输入第一个整数:'))
b = int(input('请输入第二个整数:'))
result = a/b
print('结果为:', result)
except ZeroDivisionError:
print('对不起,除数不允许为0')
except ValueError:
print('不能将字符串转换成数字')
except BaseException as e:
print('程序结束')
上面的代码,想要处理多个异常,所以加了很多个except分支,从而判断异常的类型
18.3 异常处理机制
try...except...else结构-
- 如果
try块中没有抛出异常,则执行else块,如果try中抛出异常,则执行except块
- 如果
try:
a = int(input('请输入第一个整数:'))
b = int(input('请输入第二个整数:'))
result = a/b
except BaseException as e:
print('出错了')
print(e)
else:
print('结果为:', result)
print('程序结束')
-
try...except...else...finally结构finally块无论是否发生异常都会被执行,能常用来释放try块中申请的资源
try:
a = int(input('请输入第一个整数:'))
b = int(input('请输入第二个整数:'))
result = a/b
except BaseException as e:
print('出错了')
print(e)
else:
print('结果为:', result)
finally:
print('无论是否残生异常,总会被执行的代码')
print('程序结束')
- Python常见的异常错误
| 序号 | 异常类型 | 描述 |
|---|---|---|
| 1 | ZeroDivisionError | 除(或取模)零(所有数据类型) |
| 2 | IndexError | 序列中没有此索引(index) |
| 3 | KeyError | 映射中没有这个键 |
| 4 | NameError | 未声明/初始化对象(没有属性) |
| 5 | SyntaxError | python语法错误 |
| 6 | ValueError | 传入无效的参数 |
traceback模块- 使用traceback模块打印异常信息
import traceback
try:
print('-----------------------')
print(1/0)
except:
traceback.print_exc()
"""
-----------------------
Traceback (most recent call last):
File "D:\PythonPractice\PyTest\Test01.py", line 820, in <module>
print(1/0)
ZeroDivisionError: division by zero
"""
18.4 PyCharm的调试模式
-
断点
- 程序运行到此处,暂时挂起,停止执行,此时可以详细观察程序的运行情况,方便做出进一步判断
-
进入调试试图
-
进入调试试图的三种方式
-
- 单机工具栏上的按钮
- 右键单击编辑区:点击:
debug‘模块名’ - 快捷键:
shift+F9
-
-
19. 编程的两大思想
19.1 两大编程思想
- 面向过程:事物比较简单,可以用线性的思维去解决
- 面向对象:事物比较复杂,使用简单的线性思维无法解决
- 共同点:面向对象和面向过程都是解决实际问题的一种思维方式
二者相辅相成,并不是对立的
解决复杂问题,通过面向对象方式便于我们从宏观上把握事物之间复杂的关系,方便我们分析整个系统,具体到围观操作,仍然使用面向过程方式来处理
19.2 类和对象的创建
-
类
- 类别:分门别类,物以类聚,人类、鸟类、动物类、植物类
- 类是多个类似事物组成的群体的统称。能够帮助我们快速理解和判断事物的性质
-
数据类型
- 不同的数据类型属于不同的类
- 使用内置函数
type()查看数据类型
-
对象
- 100、99都是
int类之下包含的相似的不同个例,这个个例专业术语称为实例或者对象 - 一切皆对象、万物皆对象
- 100、99都是
- 创建类的语法
class Student : # student为类的名称,有一个或多个单词组成,每个单词的首字母大写,其余小写(默认规则)
pass
-
类的组成
- 类属性
- 实例方法
- 静态方法
- 类方法
class student:
native_plase = '河南' # 直接写在类里面的变量,称为类属性
def __init__(self, name, age): # init初始化方法
self.name = name # 赋值操作,将局部变量的name的值赋给实体属性
self.age = age # self、name称为实体属性
# 实例方法
def info(self):
print('我的名字叫做:', self.name, '年龄为:', self.age)
# 类方法
@classmethod
def cm(cls):
print('类方法')
# 静态方法
@staticmethod
def sm():
print('静态方法')
# 在类之外定义的称为函数,在类之内定义的称为方法
-
对象的创建
- 对象的创建又称为类的实例化
- 语法:
实例名 = 类名() - 意义:有了实例,就可以调用类中的内容
19.3 类对象与类属性
# 创建student类的实例对象
stu = student('张三', 19)
print(id(stu))
print(type(stu))
print(stu.name)
print(stu.age)
stu.info()
"""
2877277908752
<class '__main__.student'>
张三
19
我的名字叫做: 张三 年龄为: 19
"""
在调用方法的时候,有一些不一样的方式,第一种就是
对象名.方法名,如stu1.eat(),第二种是类名.方法名(类的对象),如student.eat(stu1),这里的类的对象,其实就是指在声明中的self
19.4 类方法与静态方法
- 类属性: 类中方法外的变量称为类属性,被该类的所有对象所共享
- 类方法: 使用
@classmethod修饰的方法,使用类名直接访问的方法 - 静态方法: 使用
@staticmethod修饰的主发,使用类名直接访问的方法
print(student.native_place) # 访问类属性
student.cm() # 调用类方法
student.sm() # 调用静态方法
动态绑定属性和方法
- Python是动态语言,在创建对象之后,可以动态地绑定属性和方法
class Student:
def __init__(self, name, age): # 初始化方法
self.name = name
self.age = age
def eat(self):
print(self.name+'在吃饭')
stu1 = Student('张三', 20)
stu2 = Student('李四', 30)
print(id(stu1))
print(id(stu2))
print('-------------为stu2动态绑定性别属性---------------')
stu2.gender = '女' # 动态绑定属性
print(stu1.name, stu1.age)
print(stu2.name, stu2.age, stu2.gender)
def show():
print('定义在类之外的,称为函数')
print('-------------为stu2动态绑定方法---------------')
stu1.show = show # 动态绑定方法
stu1.show()
-
编程实现
- 面向对象
- 面向过程
-
类对象
class- 类属性
- 类方法
- 实例方法
- 静态方法
-
实例对象
- 类名(),创建实例对象
- 动态绑定属性
- 动态绑定方法
20. 面向对象的三大特性
面向对象的三大特性
-
封装: 提高程序的安全性
- 将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样,无需关心方法内部的具体实现细节,从而隔离了复杂类
- 在Python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前边使用两个"_"
-
继承: 提高代码的复用性
-
多态: 提高程序的可扩展性和可维护性
20.1 封装
封装就是将一些属性或者方法封装在类内,使得类内可以访问,类外不能访问
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age # 年龄不希望在类的外部被使用,所以加了两个_
def show(self):
print(self.name, self.__age)
stu = Student('张三', 20)
stu.show()
# 在类的外面使用name和age
print(stu.name)
# print(stu.__age) # AttributeError: 'Student' object has no attribute '__age'
# print(dir(stu)) # 打印出来stu的所有属性
print(stu._Student__age) # 在类的外部可以通过 _Student__age 进行访问
20.2 继承
- 语法格式
class 子类类名(父类1, 父类2...):
pass
- 如果一个类没有继承任何类,则默认继承object
- python支持多继承
- 定义子类时,必须在其构造函数中调用父类的构造函数
# 定义人类父类
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print('姓名:{0}, 年龄:{1}'.format(self.name, self.age))
# 定义学生子类
class Student(Person):
def __init__(self, name, age, score):
super().__init__(name, age)
self.score = score
# 定义老师子类
class Teacher(Person):
def __init__(self, name, age, teacherFyear):
super().__init__(name, age)
self.teacherFyear = teacherFyear
# 测试
stu = Student('张三', 20, 90)
stu.info()
teacher = Teacher('李四', 34, 10)
teacher.info()
20.3 方法重写
- 如果子类对继承自父类的某个属性或方法不太满意,可以从子类中对其(方法体)进行重新编写
- 子类重写后的方法中可以通过
super().xxx()调用父类中被重写的方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print('姓名:{0}, 年龄:{1}'.format(self.name, self.age))
# 定义学生子类
class Student(Person):
def __init__(self, name, age, score):
super().__init__(name, age)
self.score = score
def info(self): # 子类方法重写
super().info()
print('学生分数:{}'.format(self.score))
# 定义老师子类
class Teacher(Person):
def __init__(self, name, age, teacherFyear):
super().__init__(name, age)
self.teacherFyear = teacherFyear
def info(self): # 子类方法重写
super().info()
print('教师年龄:{}'.format(self.teacherFyear))
# 测试
stu = Student('张三', 20, 90)
stu.info()
teacher = Teacher('李四', 34, 10)
teacher.info()
"""
姓名:张三, 年龄:20
学生分数:90
姓名:李四, 年龄:34
教师年龄:10
"""
20.4 object类
boject类是所有类的父亲,因此所有类都有object类的属性和方法- 内置函数
dir()可以查看指定对象所有属性 object有一个__str__()方法,用于返回一个对于“对象的描述”,对应于内置函数str()经常用于print()方法,帮助我们查看对象的信息,所以我们经常会对__str__()进行重写
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self): # 重写str方法
return '我的名字是{0},今年{1}岁了'.format(self.name, self.age)
stu = Student('张三', 20)
print(dir(stu)) # 内置函数,查看指定对象的所有属性
"""
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
"""
print(stu) # 默认会调用__str__()这样的方法
# 我的名字是张三,今年20岁了
我们经常会在定义一个类之后,重写他的
__str__()方法,来对类进行一个描述
20.5 多态
- 简单的说,多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法
class Animal(object):
def eat(self):
print('动物会吃')
class Dog(Animal):
def eat(self):
print('狗吃骨头')
class Cat(Animal):
def eat(self):
print('猫吃鱼')
class Person:
def eat(self):
print('人吃五谷杂粮')
# 定义一个函数
def fun(obj):
obj.eat()
# 开始调用函数
fun(Cat())
fun(Dog())
fun(Animal())
fun(Person())
"""
猫吃鱼
狗吃骨头
动物会吃
人吃五谷杂粮
"""
-
静态语言和动态语言关于多态的区别
-
静态语言实现多态的三个必要条件
- 继承
- 方法重写
- 父类引用指向子类对象
-
动态语言的多态崇尚“鸭子类型”,当看到一只鸟走起来像鸭子、游泳起来像鸭子、收起来也想鸭子,那么这只鸟就可以被称为鸭子。在鸭子类型中,不需要关心对象是什么类型,到底是不是鸭子,只关心对象的行为
-
20.6 特殊方法和特殊属性
| 名称 | 描述 | |
|---|---|---|
| 特殊属性 | __dict__ | 获得类对象或实例对象所绑定的所有属性和方法的字典 |
| 特殊方法 | __len__() | 通过重写方法,让内置函数len()的参数可以自定义类型 |
| 特殊方法 | __add__() | 通过重写方法,可使用自定义对象具有“+”功能 |
| 特殊方法 | __new__() | 用于创建对象 |
| 特殊方法 | __init__() | 对创建的对象进行初始化 |
- 特殊属性
class A:
pass
class B:
pass
class C(A, B):
def __init__(self, name):
self.name = name
# 创建C类的对象
x = C('张三') # x就是C类型的一个实例对象
print(x.__dict__) # 实例对象的属性字典 {'name': '张三'}
print(C.__dict__)
print('-------------')
print(x.__class__) # <class '__main__.C'> 输出了对象所属的类
print(C.__bases__) # C类的父类类型的元素 (<class '__main__.A'>, <class '__main__.B'>)
print(C.__base__) # C类的父类类型的一个基类 <class '__main__.A'>
print(C.__mro__) # 类的层次结构
print(A.__subclasses__()) # 子类的列表 [<class '__main__.C'>]
- 特殊方法
class Student:
def __init__(self, name):
self.name = name
def __add__(self, other):
return self.name + other.name
def __len__(self):
return len(self.name)
stu1 = Student('张三')
stu2 = Student('李四')
s = stu1+stu2 # 实现了两个对象的加法运算(因为在student类中,编写了__add__()特殊 的方法)
print(s)
s = stu1.__add__(stu2)
print(s)
print('------------------')
lst = [11, 22, 33, 44]
print(len(lst)) # len是内置函数len
print(len(stu1)) # 2
print(len(stu2)) # 2
下面的代码主要是介绍__init__()和__new__()方法的意思
class Person:
def __new__(cls, *args, **kwargs):
print('__new__被调用执行了,cls的id值为{0}'.format(id(cls)))
obj = super().__new__(cls)
print('创建的对象id为:{0}'.format(id(obj)))
return obj
def __init__(self, name, age):
print('__init__被调用了,self的id值为:{0}'.format(id(self)))
self.name = name
self.age = age
print('object这个类对象的id为:{0}'.format(id(object)))
print('person这个类对象的id为:{0}'.format(id(Person)))
# 创建Person类的实例对象
p1 = Person('张三', 20)
print('p1这个person类的实例对象的id:{0}'.format(id(p1)))
"""
object这个类对象的id为:140710131355136
person这个类对象的id为:2337138898688
__new__被调用执行了,cls的id值为2337138898688
创建的对象id为:2337139834592
__init__被调用了,self的id值为:2337139834592
p1这个person类的实例对象的id:2337139834592
"""
根据上面代码的输出结果,不难得出有一些结论,其实在创建实例对象的过程中,首先先创建了一个
Person('张三', 20),在类内,先是调用了new方法,在方法中又调用 了父类object的new方法,之后调用了init方法,最后才把实例化的Person赋值给p1
20.7 类的深拷贝和浅拷贝
-
变量的赋值操作
- 只是形成两个变量,实际上还是指向同一个对象
class CPU:
pass
class Disk:
pass
class Computer:
def __init__(self, cpu, disk):
self.cpu = cpu
self.disk = disk
# (1)变量的赋值
cpu1 = CPU()
cpu2 = cpu1
print(cpu1) # <__main__.CPU object at 0x000001F9FF699FD0>
print(cpu2) # <__main__.CPU object at 0x000001F9FF699FD0>
-
浅拷贝
- Python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对象
class CPU:
pass
class Disk:
pass
class Computer:
def __init__(self, cpu, disk):
self.cpu = cpu
self.disk = disk
# (2)类的浅拷贝
print('------------------------')
disk = Disk() # 创建一个硬盘类的对象
computer = Computer(cpu1, disk) # 创建一个计算机类的对象
print(disk)
# 浅拷贝
import copy
computer2 =copy.copy(computer)
print(computer, computer.cpu, computer.disk)
print(computer2, computer2.cpu, computer2.disk)
print('------------------------')
"""
<__main__.Computer object at 0x000001FBCA0EAF70> <__main__.CPU object at 0x000001FBCA0EAFD0> <__main__.Disk object at 0x000001FBCA0EAFA0>
<__main__.Computer object at 0x000001FBCA0EAD90> <__main__.CPU object at 0x000001FBCA0EAFD0> <__main__.Disk object at 0x000001FBCA0EAFA0>
"""
由代码打印出来的
id可以得到,浅拷贝只会拷贝父对象,子对象没有进行拷贝,得到的子对象的id都是相同的
-
深拷贝
- 使用
copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不相同
- 使用
class CPU:
pass
class Disk:
pass
class Computer:
def __init__(self, cpu, disk):
self.cpu = cpu
self.disk = disk
print('------------------------')
disk = Disk() # 创建一个硬盘类的对象
computer = Computer(cpu1, disk) # 创建一个计算机类的对象
print(disk)
# (3)类的深拷贝
computer3 = copy.deepcopy(computer)
print(computer, computer.cpu, computer.disk)
print(computer3, computer3.cpu, computer3.disk)
"""
<__main__.Computer object at 0x000001FBCA0EAF70> <__main__.CPU object at 0x000001FBCA0EAFD0> <__main__.Disk object at 0x000001FBCA0EAFA0>
<__main__.Computer object at 0x000001FBCA0EAC10> <__main__.CPU object at 0x000001FBCA0EA1C0> <__main__.Disk object at 0x000001FBCA0EC940>
"""
由打印出来的
id可以发现,深拷贝中,不仅父对象被拷贝,父对象中的每个子对象也都被拷贝
21. 模块
21.1 什么叫模块
-
模块的英文单词为
Modules -
函数与模块的关系
- 一个模块中可以包含N多个函数
-
在Python中一个扩展名为
.py的文件就是一个模块 -
使用模块的好处
- 方便其它程序和脚本的导入与使用
- 避免函数名和变量名冲突
- 提高代码的可维护性
- 提高代码的可重用性
21.2 自定义模块
-
创建模块
- 新建一个
.py文件,名称尽量不要与Python自带的标准模块名称相同
- 新建一个
-
导入模块
import 模块名称 [as别名]
from 模块名称 import 函数/变量/类
21.3 以主程序的形式执行
- 在每个模块的定义中都包括一个记录模块名称的变量
__name__,程序可以检查该变量,以确定他们在哪个模块中执行。如果一个模块不是被导入到其它程序中执行,那么它可能在解释器的顶级模块中执行。顶级模块的__name__变量的值为__main__
if __name__ = '__main__':
pass
def add(a, b):
return a+b
if __name__ == '__main__':
print(add(10, 20)) # 只有运行test01时,才会执行运算
21.4 Python中的包
-
包是一个分层次的目录结构,它将一组功能相近的模块组织在一个目录下
-
作用:
- 代码规范
- 避免模块名称冲突
-
包和目录的区别
- 包括
__init__.py文件的目录称为包 - 目录里通常不包含
__init__.py文件
- 包括
-
包的导入
import 包名.模块名
-
导入带有包的模块时注意事项
import pageage1import cakc- 使用
import方式导入时,只能跟包名或者模块名 from pageage1 import module_Afrom pageage1.module_A import a- 使用
from...import可以导入包、模块、函数、变量
21.5 第三方模块的安装以及使用
- python中常用的内置模块
| 模块名 | 描述 |
|---|---|
sys | 与Python解释器及其环境操作相关的标准库 |
time | 提供与时间相关的各类函数的标准库 |
os | 提供了访问操作系统服务功能的标准库 |
calendar | 提供与日期相关的各类函数的标准库 |
urllib | 用于读取来自网上(服务器)的数据标准库 |
json | 用于使用json序列化和反序列化对象 |
re | 用于在字符串中执行正则表达式匹配和替换 |
math | 提供标准算术运算函数的标准库 |
decimal | 用于进行精确控制运算精度、有效数位和四舍五入操作的十进制运算 |
logging | 提供了灵活的记录事件、错误、警告和调试信息等日志信息的功能 |
-
第三方模块的安装
pip install 模块名
-
第三方模块的使用
import 模块名
22. 编码
22.1 编码格式介绍
-
常见的字符编码格式
- Python的解释器使用的是
Unicode(内存) .py文件在磁盘上使用UTF-8存储(外存)
- Python的解释器使用的是
22.2 文件的读写原理
-
文件读写俗称“IO操作”
-
文件读写操作流程
-
操作原理
- Python操作文件
- 打开或新建文件
- 读、写文件
- 关闭资源
- 又从
.py文件——>解释器——>OS——>操作——>硬盘
22.3 文件的读写操作
- 内置函数opem()创建文件对象
- 语法规则
file = open(filename [, mode, encoding])
file = open('a.txt', 'r')
print(file.readlines())
file.close()
-
常用的文件打开模式
-
按照文件中数据的组织形式,文件分为以下两大类
- 文本文件:存储的普通“字符”文本,默认为Unicode字符集,可以使用记事本程序打开
- 二进制文件:把数据内容用“字节”进行存储,无法用记事本打开,必须使用专用的软件打开,如MP3音频文件,jpg图片等
-
| 打开模式 | 描述 |
|---|---|
r | 以只读模式打开文件,文件的指针将会放在文件的开头 |
w | 以只写模式打开文件,如果文件不存在则创建,如果文件存在,则覆盖原有内容,文件的指针在文件的开头 |
a | 以追加模式打开文件,如果文件不存在则创建,文件指针在文件开头,如果文件存在,则在文件末尾追加内容,文件指针在原文件末尾 |
b | 以二进制方式打开文件,不能单独使用,需要与其它模式一起使用,rb或者wb |
+ | 以读写方式打开文件,不能单独使用,需要与其它模式一起使用,如a+ |
22.4 文件对象常用的方法
| 方法名 | 说明 |
|---|---|
read([size]) | 从文件中读取size个字节或字符的内容返回。若省略[size],则读取到文件末尾,即一次读取文件所有内容 |
readline() | 从文本文件中读取一行内容 |
readlines() | 把文本文件中每一行都作为独立的字符串对象,并将这些对象放入列表返回 |
write(str) | 将字符串str内容写入文件 |
writelines(s_list) | 将字符串列表s_list写入文本文件,不添加换行符 |
seek(offset[, whence]) | 把文件指针移动到新的位置,offset表示相对于whence的位置: offset:为正往结束方向移动,为负往开始方向移动 whence不同的值代表不同含义: 0:从文件开头开始计算(默认值) 1:从当前位置开始计算 2:从文件尾开始计算 |
tell() | 返回文件指针的当前位置 |
flush() | 把缓冲区的内容写入文件,但不关闭文件 |
close() | 把缓冲区的内容写入文件,同时关闭文件,释放文件对象相关资源 |
22.5 with语句(上下文管理器)
- with语句可以自动管理上下文资源,无论什么原因跳出with块,都能确保文件正确的关闭,以此来达到释放资源的目的
with open('logo.png', 'rb') as src_file:
print('src_file.read()')
with open('a.txt', 'r') as file:
print(file.read())
"""
你好,世界!
"""
22.6 目录操作
- OS模块是Python内置的与操作系统功能和文件系统相关的模块,该模块中的语句的执行结果通常与操作系统有关,在不同的操作系统上运行,得到的结果可能不一样
- OS模块与
os.path模块用于对目录或者文件进行操作
# os模块与操作系统相关的一个模块
import os
os.system('notepad.exe') # 打开记事本文件
os.system('calc.exe') # 打开系统自带的计算器
# 直接调用可执行文件
os.startfile('D:\QQ\Bin\QQ.exe')
- os模块操作目录相关函数
| 函数 | 说明 |
|---|---|
getcwd() | 返回当前的工作目录 |
listdir(path) | 返回指定路径下的文件和目录信息 |
mkdir(path[, mode]) | 创建目录 |
makedirs(path1/path2...[, mode]) | 创建多级目录 |
rmdir(path) | 删除目录 |
removedirs(path1/path2...) | 删除多级目录 |
chdir(path) | 将path设置为当前工作目录 |
os.path模块操作目录相关函数
| 函数 | 说明 |
|---|---|
abspath(path) | 用于获取文件或目录的绝对路径 |
exists(path) | 用于判断文件或目录是否存在,如果存在返回True,否则返回False |
join(path, name) | 将目录与目录或者文件拼接起来 |
splitext() | 分离文件名和扩展名 |
basename(path) | 从一个目录中提取文件名 |
dirname(path) | 从一个路径中获取文件路径,不包括文件名 |
isdir(path) | 用于判断是否为路径 |