Python高级

在熟悉了Python基础之后,Python中还有很多更高级的内容,学习它们可以让你的工作效率更高。

装饰器

装饰器是Python中的一个语法糖,本质就是一个函数。

简单理解

def out_func(func):
print("out函数开始")
def inner_func(*args, **kwargs):
print("inner函数开始")
result = func(*args, **kwargs)
print("inner函数结束")
return result
print("out函数结束")
return inner_func


# 装饰器用法
@out_func
def my_func(n):
print("my_func运行")
return n + 1


result = my_func(1)
print(result, '\n')


# 普通用法,完全等价
def my_func(n):
print("my_func运行")
return n + 1


result = out_func(my_func)(1)
print(result)

本质是将被装饰函数作为参数传入到装饰器函数中,并将被装饰函数的实参传入到装饰器函数的内部函数进行调用和返回,其中使用到了Python中的闭包。

带参装饰器

def out_func(x):
print('out函数开始')

def wrapper(func):
print('wrapper函数开始')

def inner_func(*args, **kwargs):
print("inner函数开始")
result = func(*args, **kwargs) * x
print("inner函数结束")
return result
print('wrapper函数结束')
return inner_func
print('out函数结束')
return wrapper


# 装饰器用法
@out_func(1000)
def my_func(n):
print("my_func函数开始")
print("my_func运行中.....")
print("my_func函数结束")
return n + 1


result = my_func(1)
print(result, '\n')


# 普通用法,完全等价
def my_func(n):
print("my_func函数开始")
print("my_func运行中.....")
print("my_func函数结束")
return n + 1


result = out_func(1000)(my_func)(1)
print(result)

依次将装饰器实参被装饰函数被装饰函数的实参由外到内传入到装饰器,可以理解为闭包套娃。

I/O流

所谓I/O流,是计算机中用于数据输入与输出的一种机制。

在编程中,I/O流主要指的是数据在程序与外部设备(如硬盘、网络、显示器、键盘、打印机等)之间的传输过程。

I/O流在大多数编程语言中都有提供,用来处理数据的读写。

字符流

  • input()​ 是输入流,从标准输入(键盘)读取数据。
  • print()​ 是输出流,将数据写入标准输出(屏幕)。

输入

# 输入的数据类型是str,需要根据需要进行转换
your_input = input(提示信息)

注意:程序执行会阻塞在此处,等待用户的输入并回车继续向后执行。

输出

# sep:数据之间的连接符号,end:最后追加的符号,flush:是否立即刷新
print(输出内容, sep="", end="\n", flush=False)

文件流

可以使用内置的 open​ 方法进行读写文件,也可以使用其他的依赖库来读写特殊文件,如 .xlsx​、.yaml​、.csv​、.json​、.xml​等等。

# 传统方式需要对文件流进行关闭
file = open(fileName, mode)
文件读写
file.close()

# 该方式可以自动对文件流关闭
with open(fileName, mode) as file:
文件操作

打开模式:

模式 描述
t 文本模式 (默认)。
x 写模式,新建一个文件,如果该文件已存在则会报错。
b 二进制模式。
+ 打开一个文件进行更新(可读可写)。
U 通用换行模式(Python 3 不支持)。
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

写入

# 将字符串写入文件
file.write(字符串)
# 将字符串列表写入文件,可自行添加换行符
file.writelines(字符串列表)

读取

# 读取文件的指定字节数,未给定则读取所有
file.read(字节数)
# 读取文件一整行(包含换行符)
file.readline(字节数)
# 读取文件的所有行,返回一个列表
file.readlines(字节数)

其他操作

# 刷新缓冲区
file.flush()

# 移动文件指针到指定位置,开始位置有0,1,2:头,当,尾
file.seek(偏移量, 开始位置)

# 返回文件指针当前位置
file.tell()

# 文件截断,截断后面的字符会被删除
file.truncate(字符数)

面向对象

  • 封装

    • 将属性和方法书写到类的里面的操作即为封装
    • 封装可以为属性和方法添加私有权限
  • 继承

    • 子类默认继承父类的所有属性和方法
    • 子类可以重写父类属性和方法
  • 多态

    • 传⼊入不不同的对象,产生不不同的结果

作用:是对⼀系列具有相同特征和行为的事物的统称,是一个抽象的概念,不是真实存在的事物。

特性:使用属性实现。

行为:使用方法实现。

class 类名(父类):

# 公开属性
属性名 = 初始值
# 私有属性
__属性名 = 初始值

# 公开方法,self即对象自身,由对象调用
方法名(self, 形参):
# super可以调用父类的方法
super()
代码

# 私有方法
__方法名(self, 形参):
代码

# 类方法,由类调用
@classmethod
def 方法名(cls, 形参):
return cls.类属性

# 静态方法,类和对象都可以调用
@staticmethod
def 方法名(形参):
代码
...

父类不是必须的,继承父类后拥有父类的属性及方法,可以多继承并且具有传递性。

类名一般用大驼峰命名。

父类的方法可以重写,以实现自己的方法。

对象

作用:是类创建出来的真实存在的事物。

# 实例化对象
对象名 = 类名(属性值)
# 对象属性的获取及修改
对象名.属性 = 属性值
# 对象调用方法
对象名.方法(实参)

属性值非必须,创建对象时,可以传入属性值对其初始化。

身份运算符

运算符 描述
is is 是判断两个标识符是不是引用自一个对象
is not is not 是判断两个标识符是不是引用自不同对象

魔法方法

  • __init__​ : 构造函数,在生成对象时调用
  • __del__​ : 析构函数,释放对象时使用
  • __repr__​ : 打印,转换
  • __str__​ : print输出方法
  • __setitem__​ : 按照索引赋值
  • __getitem__​: 按照索引获取值
  • __len__​: 获得长度
  • __cmp__​: 比较运算
  • __call__​: 函数调用
  • __add__​: 加运算
  • __sub__​: 减运算
  • __mul__​: 乘运算
  • __truediv__​: 除运算
  • __mod__​: 求余运算
  • __pow__​: 乘方

异常

当检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的"异常"。

简单使用

try:
可能发送异常的代码
...
except (异常类型, ...) as e:
出现该类型异常后处理的代码
异常类型可以同时捕获多个
else:
没有异常时执行的代码
可以没有该部分语句
finally:
无论有没有异常都会执行的代码
可以没有该部分语句

Exception是所有程序异常类的父类,指定该异常类型可以捕获所有异常,但是不够精确。

自定义异常类型

 # 自定义异常类
class 异常类名(Exception):
代码

# 设置抛出异常的描述信息
def __str__(self):
return ...

# 抛出异常
raise 异常类名()

异常具备传递性,可将异常抛出到上一级。

模块/包

Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句,模块能定义函数,类和变量量,模块⾥也能包含可执行的代码。

导入模块

# 直接导入模块
import 模块名

# 导入模块的某个功能
from 模块名 import 功能名

# 导入模块的所有功能
from 模块名 import *

# 导入模块并取别名
import 模块名 as 别名

# 导入模块的某个功能并取别名
from 模块名 import 功能名 as 别名

模块的定位顺序

当导⼊⼀个模块,Python解析器对模块位置的搜索顺序是:

  1. 当前⽬录
  2. 如果不在当前⽬录,Python则搜索在shell变量PYTHONPATH下的每个目录。
  3. 如果都找不到,Python会搜索默认路径。UNIX下,默认路径一般为/usr/local/lib/python/

模块搜索路径存储在system模块的sys.path变量中。变量⾥包含当前目录,PYTHONPATH和由安装过程决定的默认目录。

自⼰的⽂件名不要和已有模块名重复,否则导致模块功能无法使用。

使⽤from 模块名 import功能的时候,如果功能名字重复,调用到的是最后定义或导⼊的功能。

如果模块文件中含__all__​变量,使用*​导入全部功能时,只会导入该变量列表的功能。

# 定义模块,文件名为 my_module.py
__all__ = ["func1", ]

def func1():
print("功能1")

def func2():
print("功能2")

# 导入模块(需要在第二个文件中执行)
from my_module import *

# 可用
func1()
# 不可用
func2()

将有关联的模块都组织的放在同一个文件夹下,并且该文件夹下包含一个__init__.py​文件,那么这个文件夹就称为包。

# 导入某个包中的某个模块的某个功能
import 包名.模块名.功能

# 导入包的所有模块
from 包名 import *

# 导入包中某个模块的所有功能
from 包名.模块名 import *

也可以在__init__.py​文件中定义__all__ = []​变量来控制导入的模块及功能。

生成器

生成器本质是一个使用了yield​关键字的函数,它可以在迭代过程中逐步产生多个值,而不是一次性全部给出,可以理解为是一个特殊的迭代器。

import sys


# 使用生成器实现斐波那契数列
def fibonacci(n):
a, b, count = 0, 1, 0
while True:
if count > n:
return
yield a
a, b = b, a+b
count += 1


f = fibonacci(20)

while True:
try:
print(next(f), end='\n')
except StopIteration:
sys.exit()

生成器函数的优势是它们可以按需生成值,避免一次性生成大量数据并占用大量内存。

迭代器

迭代器的本质是一个类,该类实现了__next__​方法,从而可以在调用next()​函数时返回下一个数据,而在官方文档中要求实现该方法时,应该尽可能的是一个可迭代对象,数据集使用iter()​函数可以返回一个可迭代对象,而迭代器实现__iter__​方法即可返回一个特殊的可迭代对像,因此,一般一个迭代器需要实现__iter__​和__next__​两个方法。

class MyIter:

def __init__(self, n):
self.n = n

# 返回可迭代对象的魔法方法
def __iter__(self):
return self

# 迭代器的魔法方法
def __next__(self):
# 可抛出异常来防止出现无限循环的情况
if self.n > 20:
raise StopIteration

current = self.n
self.n += 1
return current


# 获取迭代器对象
myiter = MyIter(10)

print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))

for i in myiter:
print(i)

网络协议

TCP/IP协议栈的五层模型:

  1. 物理层(Physical Layer):物理层定义了物理介质、电压级别、数据传输速率等硬件细节,以确保数据能够在网络上传输。它负责传输比特流。物理层包括有关电缆、光纤、无线传输等物理媒介的规范。
  2. 数据链路层(Data Link Layer):数据链路层负责数据的分帧、物理地址寻址(MAC地址)、流量控制和错误检测。它通常分为两个子层:逻辑链路控制(LLC)和媒体访问控制(MAC)。以太网协议(Ethernet)是数据链路层中的一个常见协议,一条数据帧由报头和报文构成,报头18个字节由源地址、目标地址、数据描述信息三部分组成,且地址必须是唯一的MAC地址,已经满足了子网通信。
  3. 网络层(Network Layer):网络层处理数据包的路由和转发,将数据从源主机传输到目标主机,并使用IP地址来标识主机和路由器。IPv4和IPv6是两个常见的网络层协议。遵循ARP协议,将网络层的IP地址映射到数据链路层的MAC地址,已经满足了广域网通信。
  4. 传输层(Transport Layer):传输层负责端到端的数据传输,确保数据可靠性和流量控制。它定义了端口号、数据分段、错误检测和纠正。TCP(传输控制协议)和UDP(用户数据报协议)是两个常见的传输层协议。端口范围0—65535​。
  5. 应用层(Application Layer):应用层是网络协议栈的最高层,它包含了各种应用程序和服务,例如Web浏览器、电子邮件、文件传输、远程登录等。应用层协议与用户交互,并提供通信的用户界面。常见的应用层协议包括HTTP、FTP、SMTP、POP3、IMAP、DNS等。

传输层的TCP/UDP

TCP传输也称流(stream)传输。UDP传输也称数据报(dgram)传输。

TCP协议

TCP连接的三次握手
  1. 客户端发送请求(SYN):客户端(通常是浏览器、应用程序等)首先向服务器发起连接请求。它发送一个TCP数据包,其中包含一个标志位(SYN,同步)和一个初始序列号(ISN,Initial Sequence Number)。此时,客户端进入SYN_SENT状态,等待服务器的响应。
  2. 服务器响应请求(SYN + ACK):服务器接收到客户端的连接请求后,确认并要求回应。服务器发送一个TCP数据包,其中包含SYN和ACK(确认)标志位,以及一个自己的初始序列号。此时,服务器进入SYN_RECEIVED状态。
  3. 客户端确认响应(ACK):客户端接收到服务器的响应后,发送一个确认(ACK)数据包,以确认收到服务器的确认响应。此时,服务器收到客户端的确认后,连接建立完成,双方可以开始进行数据传输。客户端和服务器都进入ESTABLISHED状态。
TCP断开的四次挥手
  1. 客户端发送关闭请求(FIN):客户端首先向服务器发送一个FIN(Finish)标志位,表示客户端不再发送数据,但仍然可以接收数据。此时,客户端进入FIN_WAIT_1状态。
  2. 服务器确认客户端的关闭请求(ACK):服务器接收到客户端的FIN后,会向客户端发送一个确认ACK(Acknowledgment)数据包,以确认收到关闭请求。此时,服务器进入CLOSE_WAIT状态。
  3. 服务器发送关闭请求(FIN):当服务器不再有数据要发送给客户端时,它向客户端发送一个FIN标志位,表示服务器也准备关闭连接。此时,服务器进入LAST_ACK状态。
  4. 客户端确认服务器的关闭请求(ACK):客户端接收到服务器的FIN后,发送一个确认ACK,表示接受服务器的关闭请求。此时,客户端进入TIME_WAIT状态;进入TIME_WAIT状态的客户端会等待一段时间(等待2倍的最大报文段寿命时间),以确保服务器收到确认,然后再正式关闭连接。这个等待状态有助于处理可能在网络中延迟的数据包,以确保连接正常关闭。

UDP协议

UDP数据包被发送到目标设备,没有建立连接的握手过程。

UDP的优点包括低开销,较小的头部开销(相对于TCP),以及适用于实时应用程序的低延迟。但UDP的缺点是它不提供数据的可靠性,因此在丢失或损坏数据包时,应用程序必须自行处理。这使得UDP更适合于一些特定的应用,而不适合需要可靠数据传输的应用。

Socket套接字

Socket(套接字)是一种编程接口,用于实现网络通信。它提供了一种抽象层,允许应用程序通过网络进行数据传输。Socket抽象层位于操作系统内核和应用程序之间,提供了一组函数和方法,使应用程序能够创建、绑定、连接、发送和接收数据包。

TCP传输

服务端

import socket

# 指定使用IPv4地址簇,TCP(流)套接字
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 端口复用
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
# 绑定IP及端口
server.bind(("127.0.0.1", 8000))
# 最大连接数
server.listen(10)

# 阻塞等待连接,返回客户端连接和客户端地址
conn, client_addr = server.accept()
# 接收客户端数据
data = conn.recv(1024)
# 发送数据
conn.send(data.upper())

# 断开连接
conn.close()
server.close()

客户端

import socket

# 指定使用IPv4地址簇,TCP(流)套接字
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 发起连接
client.connect(("127.0.0.1", 8000))

# 发送数据
client.send(input(">>").encode('utf-8'))
# 接收数据
data = client.recv(1024)
# 关闭连接
client.close()

TCP传输时,客户端在发送数据量小且时间间隔很短的情况下,会使用Nagle优化算法,将多个数据包合并发送,在服务端会发生粘包现象。

如果接收的数据字节不够,会先通过socket将数据发送到操作系统的内存中,然后网卡通过内存取走一部分数据进行传输,由于数据的残留,也会发送粘包现象。

解决方法:自定义一个报头,包含数据信息,先发送一个报头长度,根据长度获取报头,对报头进行解析,获取数据的长度,最后根据数据长度多次循环读取即可解决粘包问题。

UDP传输

服务端

import socket

# 指定使用IPv4地址簇,TCP(流)套接字
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)

# 绑定IP及端口
server.bind(("127.0.0.1", 8000))

# 接收数据
data, client_addr = server.recvfrom(1024)

# 发送数据到指定客户端
server.sendto(data.upper(), client_addr)

# 关闭socket
server.close()

客户端

import socket

# 指定使用IPv4地址簇,TCP(流)套接字
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)

# 发送数据到服务端
client.sendto(input(">>").encode('utf-8'), ('127.0.0.1', 8000))

# 接收数据
data, server_addr = client.recvfrom(1024)

# 关闭socket
client.close()

UDP传输时,虽然没有粘包现象,但是如果接收的数据字节不够,那么会直接丢失剩下的数据,存在数据安全问题。

并发编程

概念

线程:是计算机中可以被CPU调度的最小单元。进程:是计算机中资源分配的最小单元。

一个程序,至少有一个进程,一个进程至少有一个主线程,线程是真正的工作单位。

一个进程可以有多个线程,且同一进程下的线程共享该进程资源。

通过多进程和多线程可以提高程序的运行效率,从而达到高并发。

多线程

常用方法

  • t.start()​:启动一个线程,代表该线程准备就绪,等待CPU调度,具体开始执行时间有CPU决定。
  • t.join(timeout)​:加入到优先执行队列,会阻塞调用它的线程,直到该线程执行完毕。
  • t.setDaemon(bool)​:是否设置为守护线程,守护线程即主线程执行完毕就会关闭子线程。
  • t.setName()​:设置线程名。
  • t.is_alive()​:检查线程是否处于活动状态。
  • threading.current_thread()​:获取当前正在执行的线程对象。
  • threading.Thread.run(self)​:自定义线程类执行的方法,可直接通过self._args​获取参数。
  • threading.Lock()​:获取线程锁,使用线程锁以确保是线程安全的,否则可能导致数据混乱,是非递归锁,不支持同一线程多次获取锁,对于一些复杂的线程逻辑可使用RLock​ (可重入锁)。
  • lock.acquire()​和lock.release()​:加锁和解锁,注意规范使用勿导致死锁,也可使用with lock:​替代。

多线程

import threading
import time

def worker(num):
for _ in range(1000):
print(f"Worker {num} is working")


start_time = time.time()

thread1 = threading.Thread(target=worker, args=(1,))
thread2 = threading.Thread(target=worker, args=(2,))

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print(time.time() - start_time)

自定义线程类

import threading
import time

class MyThread(threading.Thread):
def __init__(self, num):
super().__init__()
self.num = num

# 重写run方法即可
def run(self):
for _ in range(1000):
print(f"Worker {self.num} is working")

start_time = time.time()

thread1 = MyThread(1)
thread2 = MyThread(2)

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print(time.time() - start_time)

线程池

import concurrent.futures
import time

def worker(num):
for _ in range(1000):
print(f"Worker {num} is working")

return "Accomplish"

start_time = time.time()

# 创建线程池
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
# 提交任务并获取Future对象
futures = [executor.submit(worker, args=i) for i in range(2)]

# 获取结果
for future in concurrent.futures.as_completed(futures):
result = future.result()

print(time.time() - start_time)

多进程

多进程的常用方法与多线程类似,只是将threading​替换为multiprocessing​即可。

多进程

import multiprocessing
import time

def worker(num):
for _ in range(1000):
print(f"Worker {num} is working")

if __name__ == "__main__":
start_time = time.time()
for i in range(2):
p = multiprocessing.Process(target=worker, args=(i,))
p.start()
p.join()

print(time.time() - start_time)

进程池

import multiprocessing
import time

def worker(num):
for _ in range(1000):
print(f"Worker {num} is working")

if __name__ == '__main__':
start_time = time.time()

# 创建进程池
with multiprocessing.Pool(processes=2) as pool:
pool.map(worker, [(1,), (2,)])

print(time.time() - start_time)

Python中的GIL

介绍

GIL(Global Interpreter Lock),全局解释器锁,是CPython(Python的标准实现)中的一个重要特性。它是一种互斥锁,用于确保在任何给定时刻只有一个线程可以执行Python字节码。

影响

GIL对于IO密集型任务的影响较小,因为在这种情况下,大部分时间线程都在等待IO操作完成。然而,在计算密集型任务中,GIL可能成为性能瓶颈。

不要以为GIL的存在就一味的使用多进程,多进程会消耗更多的资源,不利于程序的运行和维护。

不要使用过多线程,因为CPU在线程间进行切换也是需要消耗时间和资源的。

解决

  1. IO密集型:使用多线程或异步编程,如文件的读写、资源下载等等。
  2. 计算密集型:使用多进程,如大量的数据计算等等。

正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。

正则表达式并不是Python特有的性质,而是各大编程语言通用的特性与写法,只需要调用各大编程语言独有的函数或方法即可实现正则表达式的匹配规则。

正则表达式

三大核心

字符

匹配固定字符
# 只匹配 固定字符
pattern = "固定字符"
匹配集合字符
# 匹配 a or 1 字符
pattern = "[a1]"

# 匹配 not (a or 1) 字符
pattern = "[^a1]"

# 匹配 [a, z] [A, Z] [0-9] 区间字符
pattern = "[a-z]"
pattern = "[A-Z]"
pattern = "[0-9]"
匹配特殊字符
# 匹配 除换行符的任意一个字符
pattern = "."

# 匹配 字母、数字、下划线(汉字)
pattern = "\w"

# 匹配 数字
pattern = "\d"

# 匹配 空白符(换行符\n、制表符\t、回车符\r、换页符\f)
pattern = "\s"

特殊字符可以使用大写,表示取反(非)。

匹配转义字符

部分字符在正则表达式中有其特有的意义,如.​代表任意字符,那么如果要匹配".",则需要使用到\.​来进行转义。

匹配字符串起始
# 匹配字符串开头
pattern = "^"

# 匹配字符串结尾
pattern = "$"

一般运用在用户输入的字符串校验中,要求更为严格的匹配。

数量

匹配模糊次数
# 匹配 0次 或 1次
pattern = "?"

# 匹配 0次 或 n次
pattern = "*"

# 匹配 1次 或 n次
pattern = "+"

量词默认使用贪婪匹配,可以在量词后面加上一个?​,那么就是使用非贪婪匹配。

贪婪匹配:最大程度匹配,如果有多个匹配结果,那么就取字符最多最长的作为匹配结果。非贪婪匹配:最小程度匹配,如果有多个匹配结果,那么就取字符最少最短的作为匹配结果。

匹配指定次数
# 匹配 指定n 次
pattern = "{n}"

# 匹配 [n, +∞) 次
pattern = "{n,}"

# 匹配 [n, m] 次
pattern = "{n,m}"

分组

提取数据
string_example = "Email:2677232159@qq.com"

# 一组提取
pattern = "(\d+)@qq.com" # ['2677232159']

# 多组提取
pattern = "(\d+)@(\w+.\w+)" # [('2677232159', 'qq.com')]

# 嵌套提取
pattern = "\d+@((\w+).\w+)" # [('qq.com', 'qq')]
分组或
string_example = "2677232159@qq.com, tmt20010201@163.com"

# 匹配其中之一并提取
pattern = "(\w+@(qq|163)\.\w+)" # [('2677232159@qq.com', 'qq'), ('tmt20010201@163.com', '163')]
命名分组
pattern = "(?P<name>表达式)"

一般与finditer​搭配,根据item.groupdict()​可见分组名。

常见的正则

此处正则一般用于用户输入进行校验。

  • 身份证:^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[1-2]\d|3[01])\d{3}(\d|X|x)$
  • 手机号:^1[3-9]\d{9}$
  • 邮箱:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
  • 日期:^\d{4}-\d{1,2}-\d{1,2}$
  • QQ:^[1-9]\d{4,10}$
  • 银行卡:^\d{16,19}$
  • 汉字:^[\u4e00-\u9fa5]+$
  • 用户名:^[a-zA-Z0-9_]{6,20}$
  • URL:^(http|https):\/\/[^\s/$.?#].[^\s]*$
  • IPV4:^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$
  • IPV6:^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$

re模块

re模块提供了很多方法来处理正则表达式。

re方法

  • re.compile(pattern, flags=0)​:返回一个预编译正则表达式对象,该对象拥有一系列匹配方法,都可以使用此处的pattern。
  • re.match(pattern, string, flags=0)​:从字符串的起始位置匹配,成功返回一个匹配对象,否则返回None。
  • re.search(pattern, string, flags=0)​:扫描整个字符串并返回第一个成功的匹配对象。
  • re.findall(pattern, string, flags=0)​:在字符串中找到正则表达式所匹配的所有子串,并返回一个列表。
  • re.finditer(pattern, string, flags=0)​:和findall类似,但返回的是一个迭代器,每个元素都是一个匹配对象,包含更多的信息。
  • re.split(pattern, string, maxsplit=0, flags=0)​:使用正则表达式来分割字符串。
  • re.sub(pattern, repl, string, count=0, flags=0)​:使用正则表达式在字符串中进行查找,并使用指定的字符串替换掉查找到的内容。
  • re.subn(pattern, repl, string, count=0, flags=0)​:和sub方法类似,但是返回一个元组,第一个元素是替换后的字符串,第二个元素是替换的次数。

Match方法:

当匹配成功时返回一个 Match 对象

  • group([group1, …])​:方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用 group() 或 group(0);
  • start([group])​:方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;
  • end([group])​:方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0;
  • span([group])​:方法返回 (start(group), end(group))。

flags可选标志

修饰符 描述
re.I 使匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响 ^ 和 $
re.S 使 . 匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

速查表

符号 描述
. 匹配除了换行符之外的任何字符
* 匹配前面的子表达式零次或多次
+ 匹配前面的子表达式一次或多次
? 匹配前面的子表达式零次或一次
n是一个非负整数。匹配确定的n次
n是一个非负整数。至少匹配n次
m和n均为非负整数,其中n <= m。最少匹配n次且最多匹配m次
\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_]
^ 匹配输入字符串的开始位置
$ 匹配输入字符串的结束位置
[abc] 匹配方括号内的任何字符
[^abc] 匹配除了方括号内字符的任何字符
(pattern) 分组匹配pattern并捕获该匹配的子字符串。可以使用\number对捕获的子字符串进行引用
| 匹配该符号前后的任一表达式
\number 匹配number指定的捕获

XPath

XPath 是一门在 XML 文档中查找信息的语言,通常用来匹配一个HTML节点。

节点

XML和HTML都是有标签构成,具有很强的层级关系,基于此关系XPath语法可以选择出我们想要的数据。

  • 父:直接包含该节点的节点,称为该节点的父节点。
  • 子:该节点直接包含的节点,称为该节点的子节点。
  • 同胞:与该节点在同一个父节点下的节点,都称为该节点的同胞节点/兄弟节点。
  • 先辈:直接或间接的包含该节点的节点,都称为该节点的先辈节点。
  • 后代:该节点直接或间接的包含的节点,都称为该节点的后代节点。

语法

节点选取

XPath 使用路径表达式在 XML/HTML 文档中选取节点。

语法 描述
/ 从根节点开始选择元素
// 从文档的任何位置选择符合条件的元素
. 当前节点
.. 父节点
@ 选择属性

谓语

谓语用来查找某个特定的节点或者包含某个指定的值的节点。

表达式 描述
//tagname[@attribute] 定位具有特定属性的元素
//tagname[@attribute='value'] 定位具有特定属性和值的元素
//tagname[text()='text'] 定位包含指定文本的元素
//tagname[contains(@attribute, 'value')] 定位属性包含部分值的元素
//tagname[starts-with(@attribute, 'value')] 定位属性值以特定字符串开头的元素
//tagname[ends-with(@attribute, 'value')] 定位属性值以特定字符串结束的元素
//tagname[last()] 定位某一标签的最后一个元素
//tagname[position()=n]​或 //tagname[n] 定位某一标签的第 n 个元素
//tagname/child::tagname 定位某一元素的tagname子节点

XPath 提供了很多 (axes)来定位和导航节点之间的关系。

轴名称 描述 示例
child:: 选择当前节点的子节点 child::li
parent:: 选择当前节点的父节点 parent::ul
following-sibling:: 选择当前节点之后的兄弟节点 //h2/following-sibling::p
preceding-sibling:: 选择当前节点之前的兄弟节点 //h2/preceding-sibling::p
ancestor:: 选择当前节点的所有祖先节点 ancestor::div
descendant:: 选择当前节点的所有子孙节点 descendant::a
self:: 选择当前节点本身 self::div

逻辑运算符

运算符 描述 示例
and 逻辑与 //input[@type='text' and @name='user']
or 逻辑或 //input[@type='text' or @type='password']
not 逻辑非 //input[not(@type='text')]
= 等于 //input[@name='username']
!= 不等于 //input[@name!='admin']
< 小于 //price[. < 100]
> 大于 //price[. > 100]

字符串函数

函数 作用 示例
string-length() 获取字符串长度 //input[string-length(@value)>5]
substring() 获取字符串的子串 //input[substring(@id, 1, 4)='user']
substring-before() 获取指定字符串之前的部分 //input[substring-before(@id, '_')]
substring-after() 获取指定字符串之后的部分 //input[substring-after(@id, '_')]
translate() 替换字符串中的字符 //input[translate(@name, 'ABC', 'abc')]
concat() 拼接多个字符串 concat('Hello', ' ', 'World')

数学函数

函数 作用 示例
count() 计算当前节点集的数量 count(//li)
sum() 计算指定节点的总和 sum(//price)
floor() 向下取整 //price[floor(.)=5]
ceiling() 向上取整 //price[ceiling(.)=5]
round() 四舍五入 //price[round(.)=5]

通配符

通配符 描述
* 匹配任何元素节点
@* 匹配任何属性节点(无属性取空)
node() 匹配任何类型的节点

多路径

通过在路径表达式中使用|​运算符,可以选取若干个路径。

使用

Python中的XPath使用lxml库中的etree模块。

先安装依赖:

pip3 install lxml

基本使用如下:

from lxml import etree

html = etree.HTML(HTML文本)
# 是一个节点还是多个节点,取决于路径表达式的书写。
nodes = html.xpath(路径表达式)

Python中的标准库

以下是Python标准库中常用的模块:

  • os 模块:os 模块提供了许多与操作系统交互的函数,例如创建、移动和删除文件和目录,以及访问环境变量等。
  • sys 模块:sys 模块提供了访问 Python 运行时系统信息的函数,例如命令行参数和环境变量。
  • time 模块:time 模块提供了处理时间和日期的函数,例如获取当前时间和日期,以及执行日期和时间操作。
  • datetime 模块:datetime 模块提供了处理日期和时间的函数,例如获取当前日期和时间,以及执行日期和时间操作。
  • math 模块:math 模块提供了处理数学运算的函数,例如计算平方根、对数和三角函数等。
  • string 模块:string 模块提供了处理字符串的函数,例如查找字符串、格式化字符串和操作字符串等。
  • random 模块:random 模块提供了生成随机数的函数,例如生成随机整数、浮点数和字符串等。
  • json 模块:json 模块提供了处理 JSON 数据的函数,例如将 Python 数据转换为 JSON 字符串和将 JSON 字符串转换为 Python 数据等。
  • re 模块:re 模块提供了处理正则表达式的函数,例如匹配字符串、替换字符串和查找字符串等。
  • urllib 模块:urllib 模块提供了处理 URL 的函数,例如获取 URL 的内容、获取 URL 的响应头、获取 URL 的响应码、获取 URL 的主机名、获取 URL 的端口号、获取 URL 的路径和获取 URL 的查询字符串等。
  • csv 模块:csv 模块提供了处理 CSV 数据的函数,例如读取 CSV 文件、写入 CSV 文件和处理 CSV 数据等。
  • base64 模块:base64 模块提供了处理 Base64 编码的函数,例如将二进制数据转换为 Base64 字符串和将 Base64 字符串转换为二进制数据等。
  • itertools 模块:itertools 模块提供了生成迭代器的函数,例如生成排列、组合和笛卡尔积等。
  • collections 模块:collections 模块提供了处理集合的函数,例如生成集合、字典和元组等。
  • pickle 模块:pickle 模块提供了处理 Python 对象序列化的函数,例如将 Python 对象序列化为字节流和将字节流反序列化为 Python 对象等。
  • shelve 模块:shelve 模块提供了处理 Python 对象存储的函数,例如将 Python 对象存储在磁盘上并以键值对的形式访问它们等。
  • hashlib 模块:hashlib 模块提供了处理密码散列的函数,例如计算 MD5、SHA1、SHA256 等散列值。
  • zlib 模块:zlib 模块提供了处理压缩数据的函数,例如压缩和解压缩数据等。
  • gzip 模块:gzip 模块提供了处理 gzip 压缩数据的函数,例如压缩和解压缩数据等。
  • bz2 模块:bz2 模块提供了处理 bzip2 压缩数据的函数,例如压缩和解压缩数据等。
  • lzma 模块:lzma 模块提供了处理 lzma 压缩数据的函数,例如压缩和解压缩数据等。
  • lz4 模块:lz4 模块提供了处理 lz4 压缩数据的函数,例如压缩和解压缩数据等。
  • snappy 模块:snappy 模块提供了处理 snappy 压缩数据的函数,例如压缩和解压缩数据等。
  • uuid 模块:uuid 模块提供了生成 UUID 的函数,例如生成 UUIDv1、UUIDv2、UUIDv3、UUIDv4 和 UUIDv5 等。
  • binascii 模块:binascii 模块提供了处理二进制数据的函数,例如将二进制数据转换为 ASCII 字符串和将 ASCII 字符串转换为二进制数据等。
  • quopri 模块:quopri 模块提供了处理 Quoted-Printable 编码的函数,例如将二进制数据转换为 Quoted-Printable 编码字符串和将 Quoted-Printable 编码字符串转换为二进制数据等。
  • codecs 模块:codecs 模块提供了处理字符编码的函数,例如将字符串从一种编码转换为另一种编码等。
  • filecmp 模块:filecmp 模块提供了处理文件比较的函数,例如比较两个文件是否相同、获取两个文件之间的差异、获取两个文件之间的差异并返回一个包含差异信息的字典等。
  • shutil 模块:shutil 模块提供了处理文件和目录操作的函数,例如复制文件、复制目录、移动文件、删除文件、删除目录、创建目录、创建临时文件和创建临时目录等。
  • tempfile 模块:tempfile 模块提供了处理临时文件的函数,例如创建临时文件、创建临时目录、获取临时文件的路径等。
  • glob 模块:glob 模块提供了处理文件路径通配符的函数,例如获取指定目录下的所有文件和目录的路径等。
  • fnmatch 模块:fnmatch 模块提供了处理文件路径通配符的函数,例如匹配文件路径和目录路径等。
  • linecache 模块:linecache 模块提供了处理文件行缓存的函数,例如获取文件行数、获取文件行、获取文件行数和获取文件行等。
  • macpath 模块:macpath 模块提供了处理 Mac OS 路径的函数,例如获取 Mac OS 路径、获取 Mac OS 路径的父路径、获取 Mac OS 路径的父路径和获取 Mac OS 路径的父路径等。
  • ntpath 模块:ntpath 模块提供了处理 Windows 路径的函数,例如获取 Windows 路径、获取 Windows 路径的父路径、获取 Windows 路径的父路径和获取 Windows 路径的父路径等。