python3-基础教程
目录
10 def 函数
11 函数 参数
12 函数默认参数
13 全局 & 局部变量, global & local
14 模块安装
15 读写文件1
16 读写文件2
17 文件读写3
18 class 类
19 类 init 功能
20 input 输入
21 元组 列表
22 列表 list
23 多维列表
24 字典 dictionary
25 import 载入模块
26 自己的模块
27 continue & break
28 错误处理 try
29 zip lambda map
30 浅复制&深复制, copy & deepcopy
1 Python threading 1 什么是多线程
32 Python multiprocessing 1 介绍
33 Python tkinter 1 什么是tkinter窗口
34 pickle 存放数据
35 set 找不同
36 RegEx 正则表达
函数
可以指定参数赋值更加清楚,如果不指定则按顺序赋值
def function(a,b):
c = a + b
print("the c is ",c)
function(1,2) #a=1,b=2
function(a=1,b=1)#a=1,b=2
function(b=2,a=1)#a=1,b=2
默认参数值
若如果在形参表当中已经对参数赋值,则在调用时可以不再赋值而函数会自动调用默认值,若在实参中重新赋值则丢弃默认值接受传递值
注意:但是要保证所有未给默认值的参数要在给定默认值参数之前
全局与局部变量
在函数外定义的变量就是全局变量在任何地方无论函数内外都可以被调用,但是你对在函数内部使用函数外部定义的全局变量时,需要如下使用:
a = None
def func():
global a
a = 20
首选如果你并不对全局变量做赋值操作时,那么你可以不需要 global语句直接使用,但是一旦要使用赋值语句的时候你就需要是a位于等号左边,这样相当于定义了一个局部变量,而对该变量赋值并不会改变函数的全局变量,因为变量同名就近原则,所以如果此时想用全局变量必须global
在函数内部定义的变量仅能在函数内部被调用
python文件读写
写文件
#打开文件
my_file = open('myfile.txt',"w")
#写入字符串
my_file.write(str)
#关闭文件
my_file.close()
#打开文件
my_file = open('myfile.txt',"a")
#追加写入字符串
my_file.write(appended_str)
#关闭文件
my_file.close()
'myfile.txt’是我们要打开的文件,如果是已经可以找到的文件则将该文件直接返回给my_file,并有后面参数的权限,如果没有对应的文件则会自己新建一个文件并返回给my_file
第二个参数是要以什么形式打开文件,常见参数:
w : 写东西进去,并且每次写入会将以前内容覆盖
r : 以只读形式打开只能查看,并不能写入
a : 写东西进去,并且每次写入会接着以前内容不会覆盖
读文件
#以只读形式打开文件,这里的文件和写的时候不一样一旦没有会报错,一定打开已有文件进行读取
my_file = open('myfile.txt',"r")
#读取文件中所有内容
my_file.read(appended_str)
#读取文件中第一行内容,如果接着使用则读取下一行
my_file.readline(appended_str)
#读取文件中所有行内容,并且把每一行读的内容组织成字符串并作为一个元素,则所有行读取后的内容构成列表作为读取结果
my_file.readline(appended_str)
#关闭文件
my_file.close()
类(class)
基本定义
class Caculator:
name="good Caculator"
price =18
def add(self,x,y):
print(self.name)
res = a + b
print(res)
def minus(self,x,y):
res = a - b
print(res)
def times(self,x,y):
res = a * b
print(res)
def devide(self,x,y):
res = a + b
print(res)
1.类一般包含类属性和类方法
2.类当中的方法分为两种一种是实例方法,一种是静态方法。
1)若如果为实例方法那么则方法的第一个参数就是self,就是对自身类的一个引用。则若如果调用需要实例化对象以后使用"对象.方法名()"调用函数,并且在实例方法中可以通过self.s属性调用类中定义的变量。
2)若无self相当于静态方法可以只直接使用"类名.方法名()"调用函数,然而"对象.方法名()"仍然奏效
类的初始化-init()方法
init方法是是对类中的属性进行初始化或者进行一些操作或者调用该类中的其他方法以及其他类以及内部的属性和方法
init方法适合其他函数一样的类方法,仍然需要保证第一个参数为self并且可以设置默认值
如果我们未定义该方法,那么类中会自动调用默认的构造方法即(无任何操作):
def _init_ (self):
但是一旦定义了init方法,那么原来默认的构造方法就被抛弃,就以新的为构造函数进行初始化,定义类的时候会自动调用该方法,而定义类后面的括号就是构造函数的形参表。
input输入
a_input = input("please input a number:")
作用:将input函数的字符串形式的参数打印在屏幕上,并且接受键盘的输入值组织成字符串的形式并赋值给变量a_input
若如果你要得到特定类型的数据就要适用强制类型转换
a_input = int(input("please input a number:"))
元组(tuple) and 列表(list)
都是由一连串有序的元素构成
定义方法:
tuple:使用()定义或者直接定义
a_tuple = (12,23,46,78)
a_tuple = 12,23,46,78
list:使用[]定义
a_list = [12,23,46,78]
使用方法
循环依次处理元素:
for number in a_tuple:
print(number)
for number in a_list:
print(number)
循环依次按位取值:
for index in range(len(a_tuple)):
print(number)
for index in range(len(a_list)):
print(number)
1.len()函数作用是计算列表或者元组中元素的个数
2.range(n)是产生一个迭代器列表,从0迭代到n-1
列表专题
1.在最后追加元素:
a_list = [12,23,46,78]
a_list.append(0) #在列表最后追加元素0
2.指定位置插入元素:
a_list = [12,23,46,78]
a_list.insert(1,0) #在列表中序号为1的位置插入元素0
3.获得值对应元素索引:
a_list = [12,23,46,78]
a_list.insert(2) #获得列表当中第一次出现的2的索引
3.获得值对应元素索引:
a_list = [2,12,2,23,46,78]
a_list.count(2) #获得列表当中第一次出现的2的索引
4.统计出现次数指定值:
a_list = [2,12,2,23,46,78]
a_list.count(2) #输出列表中出现2的次数
5.删除最后一个值:
a_list = [12,23,46,78]
a_list.pop() #删除列表中最后一个值78
6.排序:
a_list = [12,23,46,78]
a_list.sort() #将列表从小到大排序并覆盖原有list
a_list.sort(reverse=True) #将列表从大到小排序并覆盖原有list
7.切片:
python列表从左至有从0为起始,从右至左移-1起始
:是从哪到哪的意思,如果一端空缺为无穷,左为负无穷,右为正无穷,且左右两端取值左闭右开
a_list = [12,23,46,78]
print(a_list[0:2])
print(a_list[:2])
print(a_list[1:])
print(a_list[:])
字典-dictionary
无序的元素构成的列表,且每一个元素都是一个键值对,其实就是一个改变索引值的列表,原来列表的索引值是确定的数字,现在可以是任意数字和字符串作为索引也就是冒号左端的东西成为键,而键对应存储的值仍叫值,值可以是任意类型的数、字符串、以及元组和列表甚至字典和函数
def function():
return 29
a_dict = {'apple':[1,2,3],"pear":{'a':2,'b':3},"bnana":function()}
print(a_dict['pear']['a'])
运行结果:
2
定义方法
a_list = [1,2,3,4,5]
a_dict = {'apple':1,"pear":2,"bnana":3}
使用方法
1.使用键可以调用对用值
print(a_dict['apple'])
2.使用键可以删除对用值
del a_dict['apple']
print(a_dict)
3.在字典中插入一个元素
a_dict['orange'] = 20 #插入一个键值对 'orange':20
print(a_dict)
import官方模块的方式
1.import time
使用模块名.方法名/属性名调用
import time
print(time.localtime())#输出当前时间
print(time.time())#输出当前时刻是多少秒
2.import time as t
为模块起别名t,如果模块名过于长我们可以用t来代替去引用方法和属性
3.from time import time,localtime #引入多个逗号隔开
引入模块当中特定方法和属性,使用该模块内方法和属性不需要使用模块名调用,直接写函数名和属性名即可
import time
print(localtime())#输出当前时间
print(time())#输出当前时刻是多少秒
4.from time import * #引入多个逗号隔开
引入模块当中所有方法和属性,使用该模块内方法和属性不需要使用模块名调用,直接写函数名和属性名即可
import time
print(localtime())#输出当前时间
print(time())#输出当前时刻是多少秒
引入自己的模块
每一个脚本都可以叫做一个模块,我们调用的方式和引用官方的模块一样,直接把待引用的那块脚本(.py文件)也就是模块的文件名作为模块名之后和前面引用官方模块的方法和属性一样
但是需要注意的是如果直接使用文件名作为模块名导入需要保证导入和待导入脚本在同一个文件夹下,若如果不在两种解决方式
第一写出待导入文件相对于导入文件的路径之间用.连接上下关系
直接将要调用的文件放在和官方库下载在本地的文件夹下
循环中的continue与break
continue:跳过本轮循环
break:跳过当前循环
错误处理-try
try可以针对系统爆出来的错进行处理
注:暂时无read.txt文件时
file = open("read.txt",'r')
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZjjNCkKu-1612539503364)(F5194E4F71284F209C3A34FE1C3C095B)]
发现异常并打印
try:#尝试运行缩进代码块
file = open("read.txt",'r')
except Exception as e: #捕获所有Exception类型异常存储到e当中
print(e) #缩进部分为捕获异常后对异常的操作这里是打印异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G4BFfsw5-1612539503366)(F99A85E63A5F491D8F80C28B0EA5E63C)]
更多的操作
try:#尝试运行缩进代码块
file = open("read.txt",'r+')
except Exception as e: #捕获所有Exception类型异常存储到e当中
#如果没有文件会产生保存并该告诉你"there is no file named as read"并同时询问你"do you want to create a new file(y/n)"如果是y我们就创建一个改名字的文件,其他情况下什么都不做
print("there is no file named as read")
repond = input("do you want to create a new file(y/n)")
if repond=='y':
file = open("read.txt",'w')
else:
pass
#如果由该文件,则执行else缩进的代码块写入内容后并关闭
else:
file.write("yyyyy")
file.close()
上面的try和else模块是对立事件,如果try下缩进的代码块没有报错则执行后面的else,如果报错则实行except Exception as e:下缩进的对于异常处理的操作
map、zip、lambda
zip
将列表合并起来,是竖向的合并,zip是英文拉链的意思,我们两个列表相当于两个链条,经过拉锁以后链条两端的齿轮是一一对应起来的,这里也是一样将两个列表中对应位置元素组织成元组,但是并不只是可以将两个列表合并对于多个列表也可以,都是将对应位置元素合并成一个n元组,知道所有元素结束。
但是进过zip函数得到的是zip对象,若想可视化则需要将其转化为list()对象
a = [1,2,3]
b = [4,5,6]
zip(a,b)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ko2k5ZuK-1612539503367)(F468219928DF4320833AE0CBECA5EE71)]
list(zip(a,b))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-raG64OS1-1612539503369)(8091B615EA604A0889F871DCD5B06FC8)]
可以zip多个列表
list(zip(a,a,b))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v8G1m7pD-1612539503371)(DFD76848EBC34E93B7B87E15C883A46B)]
zip对象相当于一个迭代器的输出
for i,j in zip(a,b):
print(i/2,j*2)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zfFuIehy-1612539503372)(A8550ACA3E1C471695B23BCD68498243)]
lambda
lambda和def的作用是一样的都是来定义一个函数,但是lambda通常用作定义代码较少的函数,而且使用lambda定义是后面不需要接函数名函数名放在等号左边二会直接接参数,所以又称匿名函数
def func1(x,y):
return x+y
func1(2,3)
func2 = lambda x,y:x+y
func2(2,3)
运行结果:
5
5
map
map就是将参数列表和函数连接起来的运算,并运行后将结果组织成列表
map(func1,[1],[2])
#将列表[1]和[2]中的参数依次分别赋给func1中的参数x,y并计算结果
#如果func1有n个非默认参数需要将n个列表并且按顺序列表和func1参数依次连接,
依次将每个列表相同位的数值依次赋给函数求值,并将该值作为相同位置的结果在结果列表中
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7PvVcVux-1612539503372)(3523281AD1744E5FBFF75137401350CA)]
输出的是map的对象,若如果想输出成列表形式,需要如下:
list(map(func1,[1],[2]))
运行结果:
[3]
输入更长的参数列表
list(map(func1,[1,3],[2,4]))
#首先将1,2依次赋给func1参数x,y并计算结果的3
#接着将3,4依次赋给func1参数x,y并计算结果为7
#相当于现将两个列表zip起来形成多个n元组,并将多个n元组依次赋给参数列表并计算结果
赋值复制 and 浅复制 and 深复制 (copy and deepcopy)
(赋值)伪复制
将一个列表赋值给另一个变量实现复制
a = [1,2,3]
b = a
将一个值赋给另一个列表,实现复制,[1,2,3]是在内存中开辟了一段内存地址并存储1,2,3,而a实际是对该块内存的引用即为该段内存起的别名,同样b也是对该块内存的引用即为该段内存起的别名,所以a和b都是对该块内存的引用控制的都是该块内存,所以无论通过a和b那个名字操控该块内存,都是同时对a和b产生影响,只是起了一个别的名字b,并没有真正开辟一段新空间去存储相同内容实现复制,所以有:
print(id(a)==id(b))
运行结果:
True
a和b对应内存同一个位置,实际上是一个东西改变任意一个都影响另一个
浅复制
import copy
a = [1,2,3]
c = copy.copy(a)
print(id(a)==id(c)
运行结果:
Flase
证明c和a变量对应内存当中并不是一个位置,不是一个东西,并且改变a或c中的任何一个元素并不会互相影响
import copy
a = [1,2,[3,4]]
c = copy.copy(a)
print(id(a)==id(c)
print(id(a[2])==id(c[2])
a[0]=11
print(c)
a[2][0]= 333
print(c)
运行结果:
False
True
[1,2,[3,4]]
[1,2,[333,4]]
我们从上面可以看出列表中的元素改变一个并不影响另一个,说明列表中的元素是重新开辟一段空间去存储类表中的元素,而对于列表中的对象元素就是列表,字典,元组等对象改变其中一个元素同时相互影响,因为对象存储的是地址并非存储值,所以当赋值的时候,浅拷贝只拷贝父对象,而不会拷贝子对象,子对象是共用的
深复制
而深度复制是完全复制所有的父对象和子对象,是实现一种id完全不同的一种复制,是开辟一块完全新的的空间并且将值复制一份存到新的空间当中
import copy
a = [1,2,[3,4]]
d = copy.deepcopy(a)
print(id(a)==id(d)
print(id(a[2])==id(d[2])
运行结果:
False
False
pickle 存放数据
保存python运算结果到本地目录上,并且将运算结果进一步其他的加工,我们将就需要包运算结果保存成pickle文件并随时提取进行加工,pickle可以保存列表、字典和变量等一些数据
保存pickle文件
import pickle
a_dict={'da':111,2:[23,1,4],'23':{1:2,'d':'sad'}}
#以二进制写入的方式打开文件pickle
file = open("pickle_example.pickle","wb")
#pickle.dump相当于挖土车,装载的是a_dict并倒入file当中
pickle.dump(a_dict,file)
#关闭文件
file.close()
打开pickle文件继续处理
import pickle
#以二进制读入的方式打开文件pickle
file = open("pickle_example.pickle","rb")
#使用pickle模块来读取file中能够读取的内容并存到另外一个字典当中
a_dict1 = pickle.load(file)
#关闭文件
file.close()
#打印读取内容
print(a_dict1)
可以使用下面代码代替上面代码,使用with可以自动关闭文件,防止忘记
同样写入的时候也可以使用with语句
import pickle
with open("pickle_example.pickle","rb") as file:
a_dict1 = pickle.load(file)
print(a_dict1)
set
在列表或序列当中找到不同的地方,不重合的地方
char_list[‘a’,‘b’,‘c’,‘c’,‘d’,‘d’,‘d’]
比如上面这个列表当中有一个a和b、2个c、3个d找不同的就是将所有重复的东西剔除掉,只留下不同的地方
基本用法
print(set(char_list))
print(type(set(char_list)))
运行结果:
{‘a’,‘b’,‘c’,‘d’}
<class ‘set’>
输出的结果是类似于字典的形式,以大括号形式表示,但是没有键对形式,是集合(set)形式,但是他返回的形式和字典一样是无序的,破坏了列表的有序性
我们可以将字符串想象成字符列表类似于上面的char_list,使用set会将字符串中以字符为元素进行出去冗余,而且会区分大小写因为大小写在内存中位置不同,而且会保留空格,因为空格也是一个字符
sentence = 'Welcome Back to This Tutorial'
print(set(sentence))
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GilEcLlO-1612539503373)(DFB353C5CD4E438D96E94EBB517B0469)]
set当中不可以传入列表的列表,只能传入列表或者元组
char_list['a','b','c','c','d','d','d']
sentence = 'Welcome Back to This Tutorial'
print(set(sentence+char_list))
运算结果:报错
set的功能
向set中添加元素
char_list['a','b','c','c','d','d','d']
unique_list = set(char_list)
unique_list.add('a')
print(unique_list)
unique_list.add('x')
print(unique_list)
运行结果:
{‘d’,‘b’,‘c’,‘a’}
{‘d’,‘x’,‘b’,‘c’,‘a’}
结果分析当我们获得一个set类型的数据当我们想里边添加元素的时候会保持互异性如果里边已经有我们要添加的元素了,我们就可以不用添加了,若没有则需要添加
但是不可以添加一组数(列表)形式,只能单独加,即:
unique_list.add(['x','a'])
运行结果:报错
清空set
char_list['a','b','c','c','d','d','d']
unique_list = set(char_list)
unique_list.clear()
print(unique_list)
运行结果:
set() #空的set
在set中删除元素
char_list['a','b','c','c','d','d','d']
unique_list = set(char_list)
unique_list.add('x')
print(unique_list.remove('x'))
print(unique_list)
运行结果:
None
{‘d’,‘b’,‘c’,‘a’}
unique_list.remove(‘x’)做的是去除集合中的元素’x’,但是返回值是None,若想看去除元素之后的列表需要重新打印一遍,但是除去的元素一定是set中具有的元素,若移除不具有的元素会产生报错,若想避免该种情况可以使用discard函数,它对该种情况不会报错,但是不会减少任何元素
char_list['a','b','c','c','d','d','d']
unique_list = set(char_list)
unique_list.add('x')
print(unique_list.discard('y'))
print(unique_list)
运行结果:
None
{‘d’,’x’,‘b’,‘c’,‘a’}
求集合的交集
set1={'d','x','b','c','a'}
set2={'a','e','i'}
print(set1.intersection(set2))
运行结果:{‘a’}
求集合的差集
求set1具有的元素而set2不具有的元素组成的集合
set1={'d','x','b','c','a'}
set2={'a','e','i'}
print(set1.difference(set2))
运行结果:
{‘d’,’x’,‘b’,‘c’}
正则表达式(Regular Expression)
正则表达式匹配的东西就是在从含有许多固定格式的代码当中,比如网页代码找到我们所需要的信息
正则表达式 (Regular Expression) 又称 RegEx, 是用来匹配字符的一种工具. 在一大串字符中寻找你需要的内容. 它常被用在很多方面, 比如网页爬虫, 文稿整理, 数据筛选等等. 最简单的一个例子, 比如我需要爬取网页中每一页的标题. 而网页中的标题常常是这种形式.
<title>我是标题<title>
而且每个网页的标题各不相同, 我就能使用正则表达式, 用一种简单的匹配方法,通过找两个
之间夹的, 一次性选取出成千上万网页的标题信息. 正则表达式绝对不是一天就能学会和记住的, 因为表达式里面的内容非常多, 强烈建议, 现在这个阶段, 你只需要了解正则里都有些什么, 不用记住, 等到你真正需要用到它的时候, 再反过头来, 好好琢磨琢磨, 那个时候才是你需要训练自己记住这些表达式的时候.
简单的匹配
正则表达式无非就是在做这么一回事. 在文字中找到特定的内容, 比如下面的内容. 我们在 dog runs to cat 这句话中寻找是否存在 cat 或者 bird.
# matching string
pattern1 = "cat"
pattern2 = "bird"
string = "dog runs to cat"
print(pattern1 in string) # True
print(pattern2 in string) # False
但是正则表达式绝非不止这样简单的匹配, 它还能做更加高级的内容. 首先需要调用一个 python 的内置模块 re. 然后我们重复上面的步骤, 不过这次使用正则. 可以看出, 如果 re.search() 找到了结果, 它会返回一个 match 的 object. 如果没有匹配到, 它会返回 None. 这个 re.search() 只是 re 中的一个功能, 之后会介绍其它的功能.
import re
# regular expression
pattern1 = "cat"
pattern2 = "bird"
string = "dog runs to cat"
print(re.search(pattern1, string)) # <_sre.SRE_Match object; span=(12, 15), match='cat'>
#就是返回一个对象告诉你我在索引12到15的位置处找到了匹配的cat
print(re.search(pattern2, string)) # None
灵活匹配
除了上面的简单匹配, 下面的内容才是正则的核心内容, 使用特殊的 pattern 来灵活匹配需要找的文字.
匹配多种可能
如果需要找到潜在的多个可能性文字, 我们可以使用 [] 将可能的字符囊括进来. 比如 [ab] 就说明我想要找的字符可以是 a 也可以是 b. 这里我们还需要注意的是, 建立一个正则的规则, 我们在 pattern 的"“前面需要加上一个 r 用来表示这是正则表达式, 而不是普通字符串. 通过下面这种形式, 如果字符串中出现run或者是ran”, 它都能找到.
# multiple patterns ("run" or "ran")
ptn = r"r[au]n" # start with "r" means raw string
print(re.search(ptn, "dog runs to cat")) # <_sre.SRE_Match object; span=(4, 7), match='run'>
匹配更多种可能
同样, 中括号 [] 中还可以是以下这些或者是这些的组合. 比如 [A-Z] 表示的就是所有大写的英文字母. [0-9a-z] 表示可以是数字也可以是任何小写字母.
print(re.search(r"r[A-Z]n", "dog runs to cat")) # None
print(re.search(r"r[a-z]n", "dog runs to cat")) # <_sre.SRE_Match object; span=(4, 7), match='run'>
print(re.search(r"r[0-9]n", "dog r2ns to cat")) # <_sre.SRE_Match object; span=(4, 7), match='r2n'>
print(re.search(r"r[0-9a-z]n", "dog runs to cat")) # <_sre.SRE_Match object; span=(4, 7), match='run'>
按类型匹配
除了上面自己定义规则, 还有很多匹配的规则时提前就给你定义好了的. 下面有一些特殊的匹配类型给大家先总结一下, 然后再上一些例子.
\d : 任何数字
\D : 不是数字
\s : 任何 white space, 如 [\t\n\r\f\v]
\S : 不是 white space
\w : 任何大小写字母, 数字和 _ [a-zA-Z0-9_]
\W : 不是 \w
\b : 空白字符 (只在某个字的开头或结尾,贴着文字的空白符是可以匹配的到的)
\B : 空白字符 (不在某个字的开头或结尾,无论前后是否有空白格子都可以匹配的到)
\ : 匹配
. : 匹配任何字符 (除了 \n)
^ : 匹配开头(文字在开头才可以匹配的到)
$ : 匹配结尾(文字在结尾才可以匹配的到)
? : 前面的字符可有可无(括号里的内容是否添加的两种情况都匹配)
下面就是具体的举例说明啦.
# \d : decimal digit
print(re.search(r"r\dn", "run r4n")) # <_sre.SRE_Match object; span=(4, 7), match='r4n'>
# \D : any non-decimal digit
print(re.search(r"r\Dn", "run r4n")) # <_sre.SRE_Match object; span=(0, 3), match='run'>
# \s : any white space [\t\n\r\f\v]
print(re.search(r"r\sn", "r\nn r4n")) # <_sre.SRE_Match object; span=(0, 3), match='r\nn'>
# \S : opposite to \s, any non-white space
print(re.search(r"r\Sn", "r\nn r4n")) # <_sre.SRE_Match object; span=(4, 7), match='r4n'>
# \w : [a-zA-Z0-9_]
print(re.search(r"r\wn", "r\nn r4n")) # <_sre.SRE_Match object; span=(4, 7), match='r4n'>
# \W : opposite to \w
print(re.search(r"r\Wn", "r\nn r4n")) # <_sre.SRE_Match object; span=(0, 3), match='r\nn'>
# \b : empty string (only at the start or end of the word)
print(re.search(r"\bruns\b", "dog runs to cat")) # <_sre.SRE_Match object; span=(4, 8), match='runs'>
# \B : empty string (but not at the start or end of a word)
print(re.search(r"\B runs \B", "dog runs to cat")) # <_sre.SRE_Match object; span=(8, 14), match=' runs '>
# \\ : match \
print(re.search(r"runs\\", "runs\ to me")) # <_sre.SRE_Match object; span=(0, 5), match='runs\\'>
# . : match anything (except \n)
print(re.search(r"r.n", "r[ns to me")) # <_sre.SRE_Match object; span=(0, 3), match='r[n'>
# ^ : match line beginning
print(re.search(r"^dog", "dog runs to cat")) # <_sre.SRE_Match object; span=(0, 3), match='dog'>
# $ : match line ending
print(re.search(r"cat$", "dog runs to cat")) # <_sre.SRE_Match object; span=(12, 15), match='cat'>
# ? : may or may not occur
print(re.search(r"Mon(day)?", "Monday")) # <_sre.SRE_Match object; span=(0, 6), match='Monday'>
print(re.search(r"Mon(day)?", "Mon")) # <_sre.SRE_Match object; span=(0, 3), match='Mon'>
多行匹配
如果一个字符串有很多行, 上面我们想使用 ^ 形式来匹配行开头的字符, 如果用通常的形式是不成功的. 比如下面的 I 出现在第二行开头, 但是使用 r"^I" 却匹配不到第二行, 这时候, 我们要使用 另外一个参数, 让 re.search() 可以对每一行单独处理. 就是把字符串里的每一个句子都作为单独的句子都具有句尾句首,并依次句首匹配,这个参数就是 flags=re.M, 或者这样写也行 flags=re.MULTILINE.
string = """
dog runs to cat.
I run to dog.
"""
print(re.search(r"^I", string)) # None
print(re.search(r"^I", string, flags=re.M)) # <_sre.SRE_Match object; span=(18, 19), match='I'>
重复匹配
如果我们想让某个规律被重复使用, 在正则里面也是可以实现的, 而且实现的方式还有很多. 具体可以分为这三种:
- : 重复零次或多次(a后面的b出现0次或任意词都是可以匹配的)
- : 重复一次或多次(a后面的b出现一次以上的任意词都是可以匹配的)
{n, m} : 重复 n 至 m 次(a后面的b出现n到m次的任意词都是可以匹配的)
{n} : 重复 n 次(a后面的b出现n次的词都是可以匹配的)
举例如下:
# * : occur 0 or more times
print(re.search(r"ab*", "a")) # <_sre.SRE_Match object; span=(0, 1), match='a'>
print(re.search(r"ab*", "abbbbb")) # <_sre.SRE_Match object; span=(0, 6), match='abbbbb'>
# + : occur 1 or more times
print(re.search(r"ab+", "a")) # None
print(re.search(r"ab+", "abbbbb")) # <_sre.SRE_Match object; span=(0, 6), match='abbbbb'>
# {n, m} : occur n to m times
print(re.search(r"ab{2,10}", "a")) # None
print(re.search(r"ab{2,10}", "abbbbb")) # <_sre.SRE_Match object; span=(0, 6), match='abbbbb'>
分组
我们甚至可以为找到的内容分组, 使用 () 能轻松实现这件事. 通过分组, 我们能轻松定位所找到的内容. 比如在这个 (\d+) 组里, \d是数字有了加号是识别无限长度的数字,需要找到的是一些数字, .是识别出来’\n’以外的所有字符,这样字符构成的无线长度的东西,在 (.+) 这个组里, 我们会找到 Date: 后面的所有内容. 当使用 match.group() 时, 他会返回所有组里的内容, 而如果给 .group(2) 里加一个数, 它就能定位你需要返回哪个组里的信息.
match = re.search(r"(\d+), Date: (.+)", "ID: 021523, Date: Feb/12/2017")
#若未指定,则将所有匹配内容完全返回
print(match.group()) # 021523, Date: Feb/12/2017
#指定后,则将第一个括号内所有匹配内容完全返回
print(match.group(1)) # 021523
#指定后,则将第二个括号内所有匹配内容完全返回
print(match.group(2)) # Date: Feb/12/2017
有时候, 组会很多, 光用数字可能比较难找到自己想要的组, 这时候, 如果有一个名字当做索引, 会是一件很容易的事. 我们需要在括号的开头写上这样的形式 ?P<名字> 就给这个组定义了一个名字. 然后就能用这个名字找到这个组的内容.
match = re.search(r"(?P<id>\d+), Date: (?P<date>.+)", "ID: 021523, Date: Feb/12/2017")
print(match.group('id')) # 021523
print(match.group('date')) # Date: Feb/12/2017
findall
前面我们说的都是只找到了最开始匹配上的一项而已, 如果需要找到全部的匹配项, 我们可以使用 findall 功能. 然后返回一个列表. 注意下面还有一个新的知识点, | 是 or 的意思, 要不是前者要不是后者.
# findall
print(re.findall(r"r[ua]n", "run ran ren")) # ['run', 'ran']
# | : or
print(re.findall(r"(run|ran)", "run ran ren")) # ['run', 'ran']
replace
我们还能通过正则表达式匹配上一些形式的字符串然后再替代掉这些字符串. 使用这种匹配 re.sub(), 将会比 python 自带的 string.replace() 要灵活多变.
print(re.sub(r"r[au]ns", "catches", "dog runs to cat")) # dog catches to cat
split
再来我们 Python 中有个字符串的分割功能, 比如想获取一句话中所有的单词. 比如 “a is b”.split(" "), 这样它就会产生一个列表来保存所有单词. 但是在正则中, 这种普通的分割也可以做的淋漓精致.
#当遇到,;.其中任一个一个字符的时候就分裂,由于.具有匹配所有除'\n'以外字符,而想真正匹配.则需要加反斜杠
print(re.split(r"[,;\.]", "a;b,c.d;e")) # ['a', 'b', 'c', 'd', 'e']
compile
最后, 我们还能使用 compile 过后的正则, 来对这个正则重复使用. 先将正则 compile 进一个变量, 比如 compiled_re, 然后直接使用这个 compiled_re 来搜索.
#先将匹配形式给编译起来
compiled_re = re.compile(r"r[ua]n")
#再使用编译器去匹配字符串中的模式
print(compiled_re.search("dog ran to cat")) # <_sre.SRE_Match object; span=(4, 7), match='ran'>
小抄
为了大家方便记忆, 我很久以前在网上找到了一份小抄, 这个小抄的原出处应该是这里. 小抄很有用, 不记得的时候回头方便看.