手动反爬虫,禁止转载:原博地址 https://blog.csdn.net/lys_828/article/details/119800777(CSDN博主:Be_melting)
知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息
在python中,所有的数分为两大类:实例 VS 类型。具体的区别可以用汽车来进行说明,比亚迪电动(属于汽车中的一种类型)汽车,有秦、汉、元、宋等,然后看车之后结合自己手中金额和喜好最终购买一辆比亚迪汉(这个就是具体的实例)。
针对于计算要求,python中有三种数据类型,分别是int
(整型)、float
(浮点型)和complex
(复数)。同时当数字较大时候也有对应的科学计数法的表示。接下来就是一一进行介绍
#int整数类型
print(5,type(5))
#float浮点数类型
print(5.0,type(5.0))
#complex复数类型
print(complex(1+3j),type(complex(1+3j)))
输出结果如下:(其中print()
函数用于输出用户写的代码内容,type()
函数可以查看数值对应的类别,当要输出多个内容时候可以用逗号进行分隔)
数值的加减乘除是最常用的计算功能,对应的操作代码如下:
#加法操作
print(6+2)
#减法操作
print(6-2)
#乘法操作
print(6*2)
#除法操作
print(6/2)
输出结果如下:
有时候单次运算结果可能需要进行保存,就需要找一个变量代表数值,然后再进行操作,如下
#数值+变量
a = 6*2
print(3 + a)
#变量+变量
b = 6/2
print(a+b)
输出结果如下:(除法运算的结果float
浮点数类型)
此外还有一些常用的计算符号,比如取余(%
),取整(//
),自增(+=
),自减(-=
),次方(**
)等
#取余操作
print(10%3)
#取整操作
print(10//3)
#自增操作
a = 10
a += 5
print(a)
#自减操作
a = 10
a -= 5
print(a)
#3次方计算
a = 2
b = 3
print(2**3)
输出结果如下:(次方操作中**
后面的数字就代表的次方的大小,注意留意除法运算的符号和整除的运算符号之间的差别,乘法和次方符号之间的区别)
在计算的数值较多时候也会存在数值的运算优先级,python中的运算优先级和日常我们的思维逻辑是一致,示例如下:(括号中的数值计算的优先级最高,其次是乘除,再者就是加减)
关于进位制,由于大家从小接受的教育都是从十以内的加减乘除开始,遇十进一的思想已经很牢固了,除了十进制外还有其他进位制的方式如:二进制、八进制、十六进制等,在python中操作进位制的转化很方便,每一个进位制都有其对应的函数,操作如下
如果需要将不同进制的数值转化为十进制的数值,操作方式也很简单,就是把刚刚输出的结果,直接放置到print()
函数中
对于一些复杂的数值计算符号,比如sin()
,log()
等函数,都可以通过导入math
模块来完成,使用的方式如下:(使用的方式就是import math
后,使用math.xxx
的进行,比如输出π
的值,计算正弦90度和余弦60度,10000以10位底点的对数)
如果还想知道math
模块下面有哪些可以直接使用的计算方法,第一种肯定是直接查看官方文档,但是都是英文,第一次接触可能有点难以下手,还有一种方式就是dir()
函数,这个是python中内置的一个函数,可以查看一个模块中有哪些具体的方法或者属性,比如应用在math
模块上输出的结果如下
在输出的结果中可以看到有一个e
,这个就是代表着自然对数的底,需要和python中的科学计数法进行区分,两个都是有e
表示,使用方式如下
变量英文单词:Variable(编程中常用简称var指代变量)
16世纪,法国数学家【弗朗索瓦·韦达】,开始使用字母表示已知及未知数字,并将这些字母视同数字一样运算,以在最后简单代入数值求解
1637年,勒內·笛卡尔引入了以 x , y , z表示公式中的未知数,以 a , b , c表示已知数字的习惯
变量是一种占位符,用于引用计算机内存地址;使用变量并不需要了解变量在计算机内存中的地址,只要通过变量名引用变量就可以查看或更改变量的值;
变量用以保存或关联各种类型和来源的数据,如用户输入的数据、特定运算结果的数据等
创建变量的方式:
Python不是强类型语言,不需要声明变量类型;变量与对象(数据)之间是引用关系;定义变量的原则:“随用随定义”。具体的操作如下:(变量对应的类型是可以重新赋值的内容发生变化,赋值后就代表了数据类型被定义)
为了更好地理解变量对应的含义,应该注意到一下五点:
apple
和Apple
不是同一个变量最后一条注意事项可能会比较陌生。就是编程语言开发出来后有一些内置的功能和一些特殊含义的单词,因此变量命名时候就要避免使用这类名称,比如最多使用的print()
函数,如果以print
进行命名变量,最后print()
将无法正常使用
如果要知道有哪些内置函数和关键词,也可以查询到,但是没有必要,因为里面的数量还是相对较多,建议今后对于变量的命名就是用内容的实际含义的英文单词,如果超过一个单词,中间使用下划线连接
语句英文单词:Expression,又称:数学式、运算式
由数字、算符、符号(如括号)等以能求得数值的有意义排列方法所得的组合;
例如: x 2 − 2 x + 1 x^{2}-2x+1 x2−2x+1
表达式英文单词:Statement
组成程序的独立元素,表示要执行的动作;
不同编程语言,语句的语法规则会有所差别:
例如
a = a + 4
import math
前面介绍了数值类型,还有简单的测试中出现了字符串数据类型,接下来就介绍一下字符串相关的基础知识以及数值类型之间的转化。字符串是由引号构成的数据,可以是单引号、双引号和三引号
为什么有这么多的定义方式,就是因为字符串里面有时候会存在单引号,或者双引号,要想显示出正确的字符串就需要使用更高一级的引号构建字符串,如下(使用时候三种方式都可以创建字符串,当字符串中要输出引号时候注意选择的方式,三引号情况下会保存原有的数据格式)
对于字符串数据有一个需要注意的地方,转义字符\
的使用可能会导致输出结果并不是想要的内容,比如当\
后面遇到n时,就构成了换行符,程序在输出时候就自动回车换行,解决这种问题的方式有两种,一种是再添加一个\
,或者在引号前面加一个字母r
此外字符串里面也可以单纯的包含数值,这类数据可以称作为数值型字符串,能够与整型,浮点型数据类型进行转化,归纳如下图
具体转化的代码操作如下,其中刚开始容易犯得错误点就是浮点数值字符串转化为整型的过程,因为中间需要先转化为浮点型数据类型,然后再转换成为整型,如果是整数字符串,可以直接采用int()进行数据类型转化
如果字符串中的内容不是数值,那么强行进行转换就会报错,因此转化之前需要确认字符串中的内容是否为数值。有时候还需要知道字符串的长度,不需要进行手动数数,直接利用len()
就可以统计数量了,注意空格也算是一个数量
前面讲解了最基础的字符串的创建及转义字符的注意事项,也在最后涉及了一个统计字符串长度的方法,除此之外,字符串数据类型还有很多功能强大的方法
首先补充一下关于三引号的字符串的知识点,前面提到可以保存原有的数据类型,代码操作输出如下(原有的空格和换行格式以及数据仍原样保留,如果有转义字符可以配合着前面提得到的两种方式解决)
进入本章节的重点内容,回到字符串本身。字符串就是有一个个字母数字(元素)组成,元素排列的方式不同对应的字符串信息也就不同,故字符串本身就是一个有序的序列(序列两个字在数学上的解释就是:被排成一列的对象(或事件),这里就是指被排成一列的元素)。每个元素所在的位置就被称作为索引,可以根据索引的顺序进行字符串中对应元素的提取。由于字符串是有序的序列,那么对应的索引就有两个方向,即正向和负向,如下图示
代码操作实例,比如要进行元素的提取(正向索引是从0开始,负向索引是从-1开始),单个元素提取时候索引就是字符所在的位置即可,如果是多个元素的提取,注意中间是以:
(冒号)进行分隔,最后的数值要比最终提取的元素位置多1(简单的记忆方式就是:含头不含尾),需要注意无论是正向还是反向,索引的值都是左边小右边大,即方向是→。
更进一步的除了指定开始和结束的索引,还可以指定一个参数,就是步长,步长就是规定在开始和结束索引之间的数据按照几“步”取一个数据,根据代码输出结果进行理解(当步长指定为1时候,就默认和前面的结果一致,挨个取元素,当步长指定为2时,隔1个元素取1个,注意是基于开始和结束索引取值的基础上,所以第一个值是l,隔两个元素就是o,接着就是w,然后步长为3,隔2个元素取1个,第一个元素l取后,接着就是空格)
这种索引搭配着步长的操作就是字符串的切片,相当于把数据切分,常用的切片操作如下(如果开始和结束的索引不指定数值,默认就是从开始和结束进行数据提取,第一个就是提取全部数据,第二个是将字符串进行反序,第三个就是隔一个元素取值)
(1) 字符串拼接
两个字符串进行加法运算即可完成拼接
str1='hello'
str2='world'
print (str1+ ' ' + str2)
(2)字符串重复
对单一字符串多次进行加法运算,或者单次乘法运算。这种重复字符串的方法,最常用的就是做分割线,比如将==
为分割符号,来个50遍重复,就形成了一个提示分割线
str1='hello '
#多次加法运算
print(str1 + str1+ str1)
#单次乘法运算,后面的数字代表重复的次数
print(str1 * 3)
#华丽的分割线
print ('=='*50)
(3) 判断字符是否存在
使用方式:xxx in
‘字符串’(xxx既可以表示单个字符也可以表示多个字符)
str3='Mars老师'
print('M' in str3)
print('MarsW' in str3)
print('Mars' in str3)
(4)dir()函数
第(4)(5)两个函数都不属于字符串下的函数,属于系统的内置函数,在介绍常用的字符串函数之前以及再今后的数据类型的介绍中,先学会使用这两个函数,会帮助我们快速提升学习速度,其中dir()
函数,可以获得当前变量对应的数据类型下有哪些可用的方法或者属性,比如就以字符串数据类型的变量,操作输出如下(注意红框中的内容,也就是非左右双下划线的部分,均是字符串的操作方法)
(5)help()函数
dir()
函数可以获得对应数据类型下面有哪些可以用的方法,但是具体怎么使用就需要help()
函数出场了。通过打印输出help(方法名)
就能调出这个方法的使用文档,比如查看一下第一个方法capitalize
的使用说明
红线框中说明:该方法就是对字符串的首字母进行大小,那么输出验证一下(输出结果核实无误,注意一点使用help()
函数时,括号中是对应的方法名称不带括号的,如果要调用方法需要加括号。总结就是查看使用文档不需要加括号,但是调用方法执行需要加括号)
(1)是否为数值性型字符串
之前介绍过数值型字符串与整型和浮点型之间的转换,字符串里面就有一个方法isdigit()
专门判断内容是否全为数值
str1='123'
str2='abc'
str3='12ab'
print(str1.isdigit())
print(str2.isdigit())
print(str3.isdigit())
(2)字符串中是否不包含数值
isalpha()
方法判断字符串中是否不包含数值,如果字符串中含有数字就返回False
,否则就返回True
str1='123'
str2='abc'
str3='Mars老师'
print(str1.isalpha())
print(str2.isalpha())
print(str3.isalpha())
(3)字符串分割与合并
采用split()
方法指定一个分割凭据将字符串进行分割,最终形成的是列表数据类型。如果要把列表转化为字符串需要使用到join()
方法,使用时候也需要指定合并的凭据
str1='hello world !!!'
print (str1.split())
print (",".join(str1.split()))
(4)字符串格式化
format()
方法是今后使用最多的字符串中的方法,可以实现数据的格式化输出。实现格式化的方式就是借用{}
花括号实现,这里的花括号也称作占位符,和format
括号中的变量一一对应,如果不在花括号中指定数字顺序,就按照默认从前往后填充占位符,然后输出结果,如果指定顺序,占位符就会按照指定顺序的位置变量进行填充,比如下面的第三行代码。需要注意事项占位符的个数要和后面的变量的个数保持一致
print("{} {}".format("hello","world"))
print("{} {} {}".format("hello","world","!!!!"))
print("{0} {1} {0}".format("hello","world","!!!!"))
(5)字符串内容替换
replace()
方法,可以指定字符进行替换,而且可以有指定替换的次数,如下(第一个参数是带替换的字符,可以是一个也可以是多个字符,第二个参数是要替换成为的字符,第三个参数是替换的次数)
(6)匹配字符串开头和结尾
用于文件的筛选时,经常会用到endswith()
和startswith()
方法,比如判断是不是照片,就可以看一下文件名的后缀是不是图片格式等,如下示例测试endswith()
方法,对于startswith()
方法的使用也是同理
(7)字符串大小写
也是用于排版输出,upper()
和lower()
方法分别对字符串中的英文字母进行大写和小写的转化
此外还有一些其它的使用方法,通过dir()
输出的内容可以进行查看,有兴趣也可以利用help()
函数看一下对应的使用说明
前面一直使用print()
函数进行变量的输出,也可以使用input()
函数获取用户的输入,但需要注意一点,无论输入的什么内容,最终的赋值变量均为字符串数据类型
首先列表存在的意义,功能上相当于一个容器,里面可以放置多种数据类型,比如前面介绍的数值型和字符串都可以放在列表中,即列表中的元素可以是任何类型的对象(通俗理解:“列表是个筐,什么都能装”)
列表的创建有两种方式,一种使用[]
中括号,将要存放的元素添加到中括号中,多个元素使用英文的逗号隔开,另一种方式就是使用内置函数list()
列表作为数据的容器,存放的数据是有前后的加入的顺序,因此也是一种有序的序列,故也能使用字符串的索引和切片的方法。但是与字符串不同的是,列表还可以通过索引修改对应位置的元素,而字符串不能修改
查找某元素对应的索引,可以使用index()
方法,这个方法在字符串中也可以使用,具体操作如下
此外需要补充一个字符串数据类型的find()
方法,这个方法的功能是在字符串中查找元素位置,和index()
方法的功能是一致的,唯一不同的是两个方法再没有找到指定元素位置后返回的结果有所区别,可以通过调用说明文档进行对比(未找到指定元素时,find()
返回-1值,index()
调用结果会提醒报错)。这两个方法的功能上重复了,在列表数据类型中只保留了index()
方法
由于切片是基于索引之上,列表的切片和字符串同理,具体的切片操作就不进行赘述了,有一点就是由于列表是可以根据索引修改元素,那么自然能够通过切片的方式修改批量的列表中的元素,比如替换某一区域的元素,或者每隔几个元素替换元素等。如下第11步输出中的代码就是将奇数位的元素进行反序
除了index()
方法外,还可以使用dir()
函数看一下其他的一些方法,其中未带双下划线的方法均是列表数据类型常用的操作,接下来会一一讲解
按照对单个元素操作和整个列表中所有元素操作的不同,将剩余的方法可以分为两类
(1) 元素添加
append()
中只能添加一个元素,如果要添加多个元素需要用到后面的extend()
方法,如果将多个元素使用小括号传入,自动将这个元素当做元祖数据类型,下一节会讲解到
(2)元素的插入
使用insert()
方法,第一个参数为元素要插入的索引位置,从0开始计数,第二个参数为要插入的元素
(3)元素的删除
删除的方式根据有没有返回要删除元素的信息分为两个函数,首先是直接移除目标元素的remove()
方法,然后就是删除目标元素并返回目标元素的pop()
方法
remove()
操作方法由于没有返回值,所以不需要将操作的过程再进行赋值为某一变量,但是pop()
方法如果不填任何内容是会以最后一个元素进行删除,并返回这个元素的值,因此可以使用赋值语句将这个删除的值进行暂时保留。pop()
括号中添加的是索引值,既可以是正向索引,也可以是负向索引,默认要删除元素的索引值为-1
(4)元素的计数
列表中有count()
方法,可以直接查看某一元素在列表中出现的次数
(1)列表数值元素统计最值
之前介绍过一个内置函数len()
用于统计字符串中的元素个数,这个函数也可以用于统计列表中的元素个数,此外还有两个内置函数很好用,就是max()
和min()
函数,求解出列表中最大和最小的值。使用的前提是列表中元素的数据类型都需要是数值类型(整型和浮点数类型都可)
(2)列表元素备份
实际上如果没有对列表数据进行备份,前面对单个元素的操作是会影响到最初的列表的数据,可以通过下面的操作实例看一下。简单的赋值操作,b的操作会影响到a中的数据,为了保护最初数据,可以通过copy()
方法进行列表备份
(3)列表元素排序
可以适用于字符串和数值类型数据,默认是按照升序进行排列,数值就是从小到大,字母就是按照从a-z
,有一个reverse
参数控制排列的顺序,默认是False
,也可以指定为True
,进行降序排列(有兴趣可以探究一下如果是字符串元素,同时出现了大小写的字母,看看系统同默认是如何排序)
(4)列表元素反向
这个操作和列表反序还是有区别,就是将原有的排列顺序直接反向过来,不需要进行大小的对比,使用到的方法是reverse()
,方法名称和上面排序的改变方向的参数名是一致,使用方式如下(原来列表的元素顺序是bac
,转变后变成了cab
)
(5)列表元素删除
分为两种,一种是将列表中的所有元素清空的clear()
方法,还有一种是彻底删除这个列表对象,借助del
关键词实现
(6)列表与列表元素合并
向列表中添加多个元素,可以通过extend()
方法,将多个元素以列表或者元祖的形式传递到括号中
元祖也是一种容器,中间也可以放置各种数据类型的对象,但是元祖和列表是有区别,其中主要区分点在于列表是可变的容器,但是元祖是不可变的容器,换句话说如果元祖被赋值定义后就无法被更改,可以起到保护数据的作用,故对于元祖的操作几乎除了索引相关的操作外,其它列表改变元素的操作方法,元祖数据类型都没有了,这一点可以通过dir()
函数来验证(发现仅有两个可用的方法,一个是查看元素的索引值,还有一个就是查看元素的个数,这两个方法都不会改变数据)
和列表定义的方式一样,元祖定义也有两个方式,第一种是()
小括号方式,另一种就是使用内置的tuple()
函数
创建时什么都不填,默认是空元祖,两者变量对应是等价的,且最终都显示为tuple
数据类型,这里tuple()
函数和list()
函数使用上有异曲同工之处,传递的多个元素可以是以小括号传入,也可以以中括号传入,有兴趣的话可以尝试一下大括号,看一看是否可行
元祖关于索引切片部分行和前面字符串类似,但是需要有一点索引使用的是[]
,这个是一个python内定的方式,需要和数据类型的特征分别开,不要看到[]
就是表示列表,然后小括号()
就表示元祖。 字符串,列表,元祖三者都属于序列(简称seq
),有一些共同的操作汇总如下
操作 | 使用说明 |
---|---|
seq[num] | 获取索引值为num的元素 |
seq[start : end : step] | start表示索引开始值,end表示索引结束值,但是含头不含尾,step指定步长 |
obj in seq | obj元素是否在序列中 |
obj not in seq | obj元素是否不在序列中 |
seq*n | 序列重复n次 |
seq_1 + seq_2 | 序列拼接 |
len(seq) | 序列长度 |
尽然已经有字符串、列表和元祖数据类型,那么为啥还需要字典?这个问题就在前面的汇总提到,字符串、列表和元祖这三种数据类型都是有序的序列,现实中需要有些无序的序列类型,具体有两类,第一类就是字典数据类型,还有一类是集合数据类型。首先通过代码验证一下数据类型的顺序性(验证无误)
由上面的输出,字典数据类型dict
对应的结构样式首先是有一个大花括号,里面的元素构成是通过键值对一一对应,冒号前面成为字典的键,后面称作为字典的值,当多个元素存在时候,中间使用逗号隔开,有点类似我们查询新华字典中的汉字一样。通过字典数据类型就可以对数据进行备注,比如键用来表示学生成绩,后面的值就对应成绩的具体取值{"score":[60,80,75]}
,这样就可以把一个学生的成绩用字典的形式表示出来(字典的取值是根据键进行索引,不是数值)
创建空字典也有两种方式,第一种就是直接使用{}
赋值,比如dic = {}
,还有一种就是采用dict()
函数转化,比如dic = dict()
如果字典中的键有重复的,系统会默认把最后一个键作为最终的键保留下来,而之前的键值对内容就会被覆盖掉(由于字典是无序的,所以最终输出的键值对位置信息可以不用计较,关键是看键值对的内容)
最后需要注意,创建字典时,字典的键要是不可变的数据结构,列表数据不能作为字典的键
(1)查看键值对个数
可以通过len()
函数查看字典的长度,输出的结果是字典中包含键值对的个数(统计时会以最终的键值对数量进行输出)
(2)修改字典的值
往往字典中的键是不需要改动的,变化的都是对应的值,处理的方式为d[k] = v
,其中d
为字典变量,k
为字典的键,v
为字典的值
(3)删除字典的值
由于字典中的键值对是一一对应,删除字典的值也就意味删除了对应的键,可以借助关键词del
实现,如果指定的是del d[k]
就是删除字典中k
的键以及对应的值,如果指定的是del d
那么删除的就是整个字典变量
(4)字典比较
当存在两个字典需要计较里面的键值对的差异时候,可以使用operator
模块下的eq()
函数
(5)获取字典内容
d.items()
方法获取字典中所有的键值对信息,d.keys()
方法获取字典中所有的键,d.values()
获取字典中所有的值(可以使用list()
函数将输出的结果转化为列表数据类型)
(6)列表转字典
将两组列表转化为字典数据,一组为键,一组为值,需要用到zip()
方法(zip就是将内容挤压到一起的含义,然后再使用dict()
函数进行转化就变成字典数据)
(7)字典防错处理
在根据键进行查找值的时候,可能由于人员误输入会出现键不存在的时候,因此为了防止程序遇错后退出运行,可以使用字典的get()
方法,程序会尝试根据指定的键进行获取值,如果没有找到会返回None
,也可以指定返回值(关于KeyError
的报错,普遍的就是要查找的这个键不存在)
此外字典虽然也是属于序列,但是和前面字符串、元祖和列表之间序列操作有很大的不同,前面总结的三者的共同用法在字典数据类型中并不完全适用。首先索引的操作,字典是按照键进行索引,不是数值;切片操作是基于数值索引,朝→进行,但是字典是无序;字典的拼接需要使用update()
方法,并不能简单的两者相加;判断元素是否属于序列也要注意,判断的是键,而不是整个键值对;剩下两个方法:统计元素个数和进行重复输出是和前面介绍的三者一致
布尔数据类型就是我们熟知的真假判断,对应的数据只有两个,即True
和False
,在使用上为了简便,也用1和0来表示真假判断的结果,但是它们两者点的数据类型是不同的,实例操作如下(python中双等于号==
表判断,单等号=
表示赋值)
除了等于判断之外,还有不等于!=
,大于等于>=
,小于等于<=
以及是否存在判断in/not in
等都返回的是布尔数据
无序的数据类型除了字典之外,剩下一个就是集合数据类型。集合存在的必要就是存放较多的数据类型数据(列表字典数据除外),但是不在乎里面的顺序,不像字典那样有构成的规则,数据不同数据类型直接扔进花括号{}
中即可,而且还有强大的自动去重功能,最终得到的数据就是不重复数据
由于字典和集合都是由花括号组成的,前面创建空字典的时候用的空花括号,那么创建空集合的方式就只有一种了,就是使用set()
函数进行创建
可以通过dir()
函数看一下集合的操作方法
通过上图输出可以发现,clear
、copy
、pop
、remove
、update
等方法都是之前介绍过的内容,接下来就介绍一下剩下的一些方法
(1)添加和删除一个元素
add()
方法是向集合中添加一个元素,discard()
方法是向集合中删除一个元素,前提该元素是属于该集合
(2)并交差补
在学习高中数学知识时候,就会接触到集合与集合之间的运算,主要就是四大运算:并交差补
union()
intersection()
difference()
symmetric_difference()
如果考虑更新原集合,可以使用带_update()
的方法
集合的常常配合着列表的使用,而且并交差补存在着简单的运算符号,不需要使用函数,如下
(3)集合之间的类别判断
isdisjoint()
issubset()
issuperset()
条件判断语句,一共可以分为三种:单分支if
语句;二分值if-else
语句;多分支if-elif-...-elif-else
语句
最基本的条件判断结构,也就是单分支if
语句,由五部分组成,最核心的是判断的条件和冒号后的语句块。python中通过严格的缩进控制语句的逻辑,冒号后要有相同的缩进长度
代码实例演示如下(之前介绍布尔数据类型,True
和False
可以简化使用1
和0
代替。如果程序判断是False
就不会运行冒号后的语句块,而是跳出条件语句,执行条件判断外的语句)
除了直接使用True
和False
表示判断条件外,if
后也可以使用判断语句,如下(二分支结构,对于if
判断不满足的情况,会转到else
后的语句执行)
if
后面如果要进行的判断条件较多时候可以进行嵌套,也就是多次if
语句的执行,实操如下
嵌套的语句也可以直接写在一个if
后面,这样会显着一行代码特别长(多个判断语句之间可以通过and
或者or
进行连接,为了避免误解,建议每个判断语句都用小括号包含起来)
多分支语句的使用就是在一种情况无法满足的情况下需要进行多次判断,而且每次判断都是基于上一次判断之前的条件,常见的就是对于学习成绩的划分,比如将分数低于60分标记为不及格,60-80分标记为合格,90以上标记为优秀
对于条件判断,可以通过三元判断的方式进行代码的简化。具体的结构就是if
左边是满足第一个条件判断的输出结果,if
后面else
前面的是判断条件,else
后就是判断条件不满足时的输出;逻辑顺序上,先看输出结果'python'
后的if
判断,如果判断满足,就输出'python'
,否则就输出else
后的结果'rust'
for循环也称遍历循环,就是将变量中的元素一个个都查找一遍,for
循环的语句结构如下
比如将列表中的所有元素打印输出或者进行数字的依次输出。生成数字的range()
函数通常配合着for
循环使用,当指定一个整数数值时,程序会输出0到指定数值-1个整数
range()
函数可以实现切片的效果,也是含头不含尾,也存在第三个参数表示步长。
如果要把输出的结果收集起来,可以使用列表容器或者集合容器。实例如下
类似前面条件判断的三元判断,for
循环也可进行简化,具体的过程被称作“列表推导”。比如遍历列表中的数值,将偶数添加到一个新的列表中,使用正常的for
循环语句进行操作如下
采用“列表推导”的方式进行操作就会很很简洁的执行这部分代码,操作如下。代码解读:结构上,对比条件判断的三元操作,列表推导就是在if
左边和要输出的变量i
的右边增加了循环的条件for i in ls
,if
的右边是判断条件,最后将整个过程用中括号包含;逻辑顺序上,先看要输出变量后的for
循环语句,然后再看后面的if
条件判断,最后才是最前面的输出变量i
同理最外边也可以改成集合的大括号,这样好可以顺带完成去重的功能,输出如下
除了列表和集合,字典也可以使用推导的过程,比如将键值对反向输出,此外有一个常用的推导式,就是根据字典中的值来查找其对应的键,操作如下
如果要将两个长度相同的列表进行对应元素的一一配对或者是组成字典,就需要使用到for-zip
的搭配
如果两个列表的长度不一致,程序并不会报错,而是会尽可能进行匹配对应位置的元素,没有办法匹配到的元素将会被舍弃
如果要对列表或者字符串中的元素进行标号,可以采用for-enumerate
搭配,最终配合着键值对就可以构成字典,从而实现字符串和列表快速向字典转变的过程
while
循环也称作判断循环,当while
后面的内容是True
或者判断语句结果是True
时,进入循环体,直至判断的条件不满足就会跳出循环。while
循环的语句结构如下
比如输出五次hello world
,实例操作如下(可以对比for
循环)。for
循环属于遍历循环,前提是要遍历的对象是已知长度的类型,比如这里要遍历5次,或者遍历一个已经赋值的列表,对象的长度都是已知的。如果要遍历的次数是无法确定,需要达到一定条件才能触发时,for
循环就没有办法使用了,比如流量达到一定值进行报警,定时发送邮件等,此时就需要使用while
循环
简单测试一个猜数字游戏,每次开局系统会自动生成一个随机数,用户进行数值输入,输入后会有提示,如果猜对就退出程序,没有就提示输入错误继续输入,直至正确。关于随机数的生成,可以调用random
模块,比如随机生成1-100之间的数值,实操如下(random.randint()
生成的数值范围默认从0
开始,也是含头不含尾)
由于猜字游戏不确定什么时候程序会停止,所以可以直接设定一个死循环(while
后面直接跟着True
),程序就会一直执行下去,后面的程序代码语句会根据用户的输入结果进行判断,在满足条件需要结束程序时候加上一行break
,如果用户猜对了程序就会自动退出
需要注意的是,while
循环使用需要小心谨慎一些,如果没有理清逻辑关系,程序很容易进入死循环,导致运行文件奔溃。
在初学pyhton时候,难免会遇到各种出错,程序执行就抛出异常,并提示错误所在行数以及报错详细信息,利用报错提醒可以很快速定位和知悉程序报错的问题,如下变量未使用就进行调用,红框中通过箭头指示执行代码出错的行,最后一行提示是NameError
错误类型
常见的异常错误类型,汇总如下
NameError
:尝试访问一个没有声明的变量SyntaxError
:拼写错误,一般检查标点符号和大小写ZeroDivisionError
:除数(分母)为0IndexError
:索引超出序列范围KeyError
:请求一个不存在的字典的键IOError
:输入/输出错误AttributeError
:尝试访问未知的对象属性python中处理和捕获异常错误的语句为:try-except-else-finally
,使用的结构如下。
常用的就是try-except
组合,如果try
中的代码出现异常报错,except
会进行捕获,并不会造成程序异常退出
如果结合else
,程序没有报错除了会正常执行try
中的语句,else
后的代码也会正常执行
如果希望程序无论是否异常总是要输出一定的内容或者执行某部分代码,可以再结合最后的finally
使用
除了保证程序的正常运行外,还可以针对不同的报错进行具体错误的捕捉,这里举例分母为0时候的场景和索引越界的情况,其它的错误捕捉也是类似
之前使用到的很多内置函数,都是python软件下载完后直接就可以调用的,在编辑器中输入print(dir(__builtins__))
,结果如下,红框中就是最常用的两个强大的内置函数dir()
和help()
除了使用内置的函数外,在编辑器中也可以自定义函数,并进行调用,函数的结构如下:
创建一个简单的函数,如下。其中第一行代码就是add_func
就是自定义的函数名,括号内的两个参数就是函数调用时候需要传递的变量,第一行下面可以通过三引号添加自定义函数的功能说明,调用函数说明的方式就是print(函数名.__doc__)
,三引号下面就是正常的程序代码,最后根据有无需求指定是否存在返回值
函数的作用就是将一个程序实现的功能进行封装,方便下次使用时候进行调用,比如后续在进行数据分析时候,可以将一些数据清洗的过程直接封装成一个函数,数据读入后直接调用该函数,返回值就是经过清洗的结果,这样就大大减少了很多重复的工作,提高了工作效率
案例:编写一个函数输出1-100以内所有的质数(质数就是只能够被自身和1整数的数)
def prime1(upper):
try:
num = []
for i in range(2,int(upper)):
j = 2
for j in range (2,i):
if (i % j ==0):
break
else:
num.append(i)
except :
print ("输入数据有问题")
return num
print(prime(100))
# print(prime1('100a'))
函数名后的括号中是函数调用时候需要传入的参数,参数传递一共有三种方式:
分别对这三种方式进行实例操作如下。通过位置传递,参数值的个数要和函数定义时括号中的参数个数一致;通过名称传递,变量的位置顺序不会受影响,比如y赋值在前,x赋值在后;默认值传递,没有指定默认参数,调用时候以默认值传入,指定参数后,默认值被覆盖
在进行函数学习时,会把print
和return
搞混淆,如下代码。如果函数内部存在print
语句,函数调用时就会执行print
语句,输出对应变量的结果,但是没有return
返回值,函数调用后返回的就是None
,所以再赋值给b
也就是None
。当函数中有返回值时,函数被调用后就会将return
后的结果返回,此时赋值给b
,b
的值打印结果就是15
对于返回的内容,可以是一个对象(包含变量和表达式),也可以是多个对象。当返回多个对象时候,最终返回的数据类型是元组,可以通过多变量赋值进行函数返回值的使用,注意返回的对象的个数要和赋值的对象个数一样,否则程序会进行报错
关于*
号的使用,在看别人封装好的函数括号要传递的参数中,经常会遇到*args
和**kwargs
,其中*args
代表元祖,**kwargs
代表着字典。前面介绍了参数传递的三种方式,都是一对一的情形,如果传递的参数值较多时候,前面的三种方式都没有办法完美的解决,比如某个程序要求进行10个传感器数据的获取并进行统计均值,使用传统的参数传递,至少要传入10个参数,如果采用*args
就可以很简单的解决(要传递的参数越多越能显示出这种方式传参的优势),实例演示如下
可以举例二个简单的式子看一下*args
究竟是啥。在函数中可变的参数*args
会以元祖的形式进行传入
如果不在函数中,而是直接调用*args
,会直接将剩下的部分以列表的形式赋值
**kwargs
可变参数的使用,代表传入函数的参数是字典,给定的形式是key = value
,也是属于可变参数,既可传入单个键值对,也可以传入多个
一个函数体内定义了另外一个函数
进行代码实操示例如下。首先看第一步中的代码,前四行就是前面介绍过常见的函数,在此基础上又复制一份代码放置于第一个print
同等缩进的位置形成第二个函数,最后再调用第二个函数。根据缩进判断,相同缩进为一个部分,这里包含了两个部分也就是多个函数,形成了嵌套函数
分别输出name
,第一个函数和第二个函数调用结果。其中name
变量是和第一个函数同等缩进,函数内的变量赋值不会影响函数外的变量,所以name
仍然是最初的赋值结果。函数内部二个函数都对name
变量进行重新赋值,但是只在函数内部生效,所以对应的name
值在调用第一个函数时发生变化,且函数最终没有返回值,调用输出后就会出现None
,由于第二个函数是在第一个函数内部,所以无法直接调用显示没有被定义
通过上面的测试示例,可以发现name这个变量在函数内外的作用域(变量的有效范围)是不一样的。如果是嵌套函数,函数内部的第二个函数只能在第一个函数体内生效,无法被第一个函数外部的代码进行调用,这种作用效果就称作局部作用域,还有就是第一个name是定义在第一个函数外的,属于对全局都有效
默认划分作用域
if
语句块、for
语句块、with
上下文管理器等等不能划分变量作用域def
,class
,lambda
改变作用域:global
和nonloacal
global
:指定当前变量使用外部的全局变量nonlocal
:内层函数中的变量它外一层函数的变量global
修饰的变量可能事先并未存在于全局作用域内,但nonlocal
修饰的变量必须已经存在于外层函数,不能只存在于全局
在第一个函数内部添加一行代码print(name)
,然后再调用函数,会发现报错提醒中指出局部变量未声明即使用,因此可以通过global
来进行调整变量的作用域,操作如下。其中第一函数中的print(name)
这次就可以正常执行了,但是第二个函数遇到了刚刚一样的错误,因为从代码缩进来看,此时第一个函数内的name变量相较于第二个函数就是“全局”变量,这样再调用第二个函数就会显示同样的错误
如果是要在第二个函数中使用外层的变量,也是直接使用global
,如果要调用第一个函数中的变量,可以使用nonlocal
,实操如下。注意留意红框中内容的变化
综上:
在实际应用中,如果已经写好的函数中的功能可能与实际需求有所差别,此时源代码已经是维护很久的程序,直接在源代码上修改根本就不现实,如果再重新编写,工程量又不小,因此就有了装饰器来解决这个问题。装饰器就是保证原本的程序不动,而是另外进行自己需求功能的设计
比如编写一个简单而且实用的装饰器函数——程序运行计时。首先编写一个简单的函数,然后编写一个计时函数
接着就是想要在直接调用f1
就实现运行时间的输出,需要构建装饰器函数,如下。装饰器本身也是属于一种嵌套函数,对比上面直接不便编写的计时函数,就是调用函数的语句放在print('foo')
的位置,而函数名称作为参数传入到装饰器函数中,然后再要执行的程序前一行写上@装饰器第一个函数名
即可
也称匿名函数,和之前常规函数的区别就在于不需要函数名称,基本的结构:lambda arg1, arg2, …, argn: expre(args)
。操作实例如下
对比正常函数,关键词由def
变成lambda
,省去了函数名称和括号的设置,也不需要书写return
关键词,最后调用时直接在表达式后加对应的参数即可。除了通过位置方式传递传递参数外,也可以进行其它方式传参,实操如下
#多参数传递
print ((lambda a , b : a*b) (10,90))
#名称参数传递
print ((lambda a , b : a*b) (b=90,a=10))
#默认参数传递
print ((lambda a , b=90 : a*b) (10))
#可变参数传递
print ((lambda *c: sum(c)) (10,90,101,9999,111,222))
print ((lambda **c: sum(c.values())) (a=2,b=4,c=7))
除了直接执行匿名函数外,还可以对函数表达式进行赋值后调用,甚至还可以配合着普通函数作为函数的返回值,实操如下
map
函数的功能就是按照指定的操作方式(某一函数)对指定的可迭代对象(某一列表)进行数据处理,的基本结构:map(func, *iterables) → map object
。比如将列表的值都乘以10,实操如下
使用map
函数前提需要准备一个函数和一个可迭代的对象,直接赋值的变量为map
对象,需要进行列表转化后输出。除了使用常规的函数,也可以搭配lambda
匿名函数,快速批量处理数据,实操如下
对于map
函数需要注意一点,通过map
处理的可迭代对象(比如列表),最后的结果是不会改变元素的数量,也就是原来的对象中的元素是多少个最后处理的结果就是多少个
filter
函数就是对数据进行过滤处理,比如前面map
处理完数据后,如果列表中存在不想要的元素,就可以搭配着filter
函数进行不需要元素的过滤,常见的就是去除空值,基本结构:filter(func or None, iterables) → filter object
,实操如下
相关概念:
正则表达式,又称规则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE)
正则表达式是对字符串(包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”))操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑
正则表达式是一种文本模式,该模式描述在搜索文本时要匹配的一个或多个字符串
python正则表达式工具包:re
模块,系统自带,不需要安装,可以直接导入
先进行一个示例进行正则表达式的使用逻辑的梳理,准备数据如下
显然里面前三个格式上是有统一的样式的,而最后一个明显是来捣乱的,因此要提取里面的数据,可以使用下面的方式(\D
代表非数字字符,\d
代表数字字符,*
代表有0
或n
个字符)
如果直接使用re.match()
方法,会返回一个Match
对象,上面加上括号的目的就是为了获取满足括号内要求的值,如下
通过上面的示例,大致可以把正则表达式的使用步骤归纳为:【目标数据】(ls
)–> 【模式设置】(m
)–> 【匹配方式】(re.match()
)–> 【数据提取】(.groups()
)–> 【数据输出】(print()
)
re.match()方法讲解
1)re.match(pattern, string, flags=0)
→ 尝试从字符串的 起始位置 匹配一个模式,如果不是 起始位置 匹配成功的话,match()
就返回None
pattern
:匹配的正则表达式string
:要匹配的字符串flags
:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等2)返回结果
re.match()
方法返回一个匹配的对象,否则返回None
3)可以使用group(num)
或 groups()
匹配对象函数来获取匹配表达式
group()/group(0)
→ 返回成功匹配的内容group(num)
→ 返回成功匹配的内容中第num
个小组的字符groups()
→ 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号4)通过span()
方法用于以元祖形式返回匹配的起始位置和结束位置
span()
返回一个元组包含匹配 (开始,结束) 的位置start()
返回匹配开始的位置end()
返回匹配结束的位置1)概念
or
或者|
来指定2)修饰符
名称 | 作用 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.U | 使 . 匹配包括换行在内的所有字符 |
re.S | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
在进行匹配的逻辑中就使用到了\D
,\d
和*
,这些都属于文本匹配的模式,除此之外,接下来详细介绍匹配的模式和匹配的次数以及贪婪匹配的问题
匹配模式
模式 | 功能 |
---|---|
^ | 匹配文本开始位置 |
$ | 匹配文本结束位置 |
. | 匹配除 “\n” 之外的任何 单个 字符,要匹配包括 ‘\n’ 在内的任何字符,请使用类似 ‘[\s\S]’ 的模式 |
[…] | 用来表示一组字符,单独列出:[amk] 匹配 ‘a’,‘m’或’k’ |
[^…] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符 |
\d | 匹配一个数字字符。等价于 [0-9] |
\D | 匹配一个非数字字符。等价于 [^0-9] |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v] |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v] |
\w | 匹配包括下划线的任何单词字符。等价于’[A-Za-z0-9_]’ |
\W | 匹配任何非单词字符。等价于 ‘[^A-Za-z0-9_]’ |
匹配示例
例子 | 作用 |
---|---|
^p | 匹配以p开头的内容 |
p$ | 匹配以p结尾的内容 |
[Pp]ython | 匹配 “Python” 或 “python” |
rub[ye] | 匹配 “ruby” 或 “rube” |
[aeiou] | 匹配中括号内的任意一个字母 |
[0-9] | 匹配任何数字。类似于 [0123456789] |
[a-z] | 匹配任何小写字母 |
[A-Z] | 匹配任何大写字母 |
[a-zA-Z0-9] | 匹配任何字母及数字 |
[^aeiou] | 除了aeiou字母以外的所有字符 |
[^0-9] | 匹配除了数字外的字符 |
用于匹配次数
模式 | 功能 |
---|---|
* | 匹配0个或多个的表达式 |
+ | 匹配1个或多个的表达式 |
? | 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式 |
{n} | 匹配n个前面表达式 |
{n, m} | 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式 |
注意:关于 .*
和 .+
及 .?
之间的区别可以通过下面的示例进行分辨
s = 'one12 twothree 34 four'
print(re.match(r'.*1',s)) #匹配结束为1,之前为任意字符的文字
print(re.match(r'.*\d',s))#匹配结束是数字,之前为任意字符的文字
print(re.match(r'.*\s',s))#匹配结束为空白字符,之前为任意字符的文字
print(re.match(r'.+[1w]',s))#匹配包括1或者w字符的文字,如果只有1或者w在开头就没有办法匹配
print(re.match(r'.*[1w]',s))#匹配包括1或者w字符的文字
print(re.match(r'.?[1w]',s))#匹配1或者w字符开头或者在第二个位置的文字
输出结果如下:
调整一下匹配的字符串,比如s = '12 tothree 34 four'
,然后在对比最后三个的区别
除了单次匹配外,还可以指定多次匹配,比如下面对b出现的次数进行二次的匹配
贪婪匹配问题
?
测试如下:(.*?
:非贪婪模式,.*
贪婪模式)
除了re.match()
从开头进行匹配外,还有其它的匹配方法,还有常用的re.search()
方法
字符搜索
前面进行字符串的匹配都是使用的re.match()
方法,此方法的不方便的地方在于,每次匹配都是从开头进行的,要想找到里面的某个元素需要前面加上 .*
,有没有扫描全部的字符串而判断元素是否存在的方式呢?re.search()
就可以上场了
re.search(pattern, string, flags=0)
→ 扫描整个字符串并返回第一个成功的匹配
pattern
:匹配的正则表达式string
:要匹配的字符串。flags
:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等re.match()
与re.search()
的区别
re.match()
只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search()
匹配整个字符串,直到找到一个匹配
进行实操示例看看两者应用的差别,测试如下:(使用re.search()
方法的灵活性和代码简易度都比re.match()
好,根据需求选择合适的方法)
此外还有其他的方法,比如re.findall()
,re.sub()
,re.split()
等方法,有兴趣的话可以参考一下:字符搜索、替换与分割